Skip to content

Commit 9c85aab

Browse files
committed
cpp: model Protocol Buffers parse/serialize taint flow
Add flow summaries for the protobuf C++ API on google::protobuf::MessageLite (subtypes=true, so Message and all generated messages are covered): - ParseFrom*/MergeFrom* (string, array, Cord, istream, zero-copy and coded-stream forms) propagate taint from the encoded input to the message. - SerializeTo*/AppendTo* propagate taint from the message to the output buffer or stream; SerializeAs*/... to the return value. File-descriptor variants are omitted (the fd is an int, not a buffer).
1 parent 401a516 commit 9c85aab

5 files changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added flow summaries for the Protocol Buffers C++ API (`google::protobuf::MessageLite`, covering `Message` and all generated messages). The `ParseFrom*`/`MergeFrom*` methods (string, array, Cord, istream, and zero-copy/coded-stream forms) propagate taint from the encoded input to the message, and the `SerializeTo*`/`SerializeAs*`/`AppendTo*` methods propagate taint from the message to the output buffer, stream, or return value.

cpp/ql/lib/ext/Protobuf.model.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
extensions:
2+
- addsTo:
3+
pack: codeql/cpp-all
4+
extensible: summaryModel
5+
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
6+
# Flow summaries for the Protocol Buffers C++ API. All of these methods are declared on
7+
# `google::protobuf::MessageLite`; `subtypes` covers `Message` and every generated message.
8+
#
9+
# File-descriptor variants (`{Parse,Serialize}*FromFileDescriptor`) are intentionally omitted:
10+
# the descriptor is an `int`, not a data buffer, so there is no buffer argument to model.
11+
12+
# Deserialization: the encoded input (`Argument[*0]`) taints the message (`this`).
13+
- ["google::protobuf", "MessageLite", True, "ParseFromString", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
14+
- ["google::protobuf", "MessageLite", True, "ParsePartialFromString", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
15+
- ["google::protobuf", "MessageLite", True, "MergeFromString", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
16+
- ["google::protobuf", "MessageLite", True, "MergePartialFromString", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
17+
- ["google::protobuf", "MessageLite", True, "ParseFromArray", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
18+
- ["google::protobuf", "MessageLite", True, "ParsePartialFromArray", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
19+
- ["google::protobuf", "MessageLite", True, "ParseFromCord", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
20+
- ["google::protobuf", "MessageLite", True, "ParsePartialFromCord", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
21+
- ["google::protobuf", "MessageLite", True, "MergeFromCord", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
22+
- ["google::protobuf", "MessageLite", True, "MergePartialFromCord", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
23+
- ["google::protobuf", "MessageLite", True, "ParseFromIstream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
24+
- ["google::protobuf", "MessageLite", True, "ParsePartialFromIstream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
25+
- ["google::protobuf", "MessageLite", True, "ParseFromZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
26+
- ["google::protobuf", "MessageLite", True, "ParsePartialFromZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
27+
- ["google::protobuf", "MessageLite", True, "ParseFromBoundedZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
28+
- ["google::protobuf", "MessageLite", True, "ParsePartialFromBoundedZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
29+
- ["google::protobuf", "MessageLite", True, "MergeFromBoundedZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
30+
- ["google::protobuf", "MessageLite", True, "MergePartialFromBoundedZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
31+
- ["google::protobuf", "MessageLite", True, "ParseFromCodedStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
32+
- ["google::protobuf", "MessageLite", True, "ParsePartialFromCodedStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
33+
- ["google::protobuf", "MessageLite", True, "MergeFromCodedStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
34+
- ["google::protobuf", "MessageLite", True, "MergePartialFromCodedStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
35+
36+
# Serialization into an output buffer/stream: the message (`this`) taints `Argument[*0]`.
37+
- ["google::protobuf", "MessageLite", True, "SerializeToString", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
38+
- ["google::protobuf", "MessageLite", True, "SerializePartialToString", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
39+
- ["google::protobuf", "MessageLite", True, "AppendToString", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
40+
- ["google::protobuf", "MessageLite", True, "AppendPartialToString", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
41+
- ["google::protobuf", "MessageLite", True, "SerializeToArray", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
42+
- ["google::protobuf", "MessageLite", True, "SerializePartialToArray", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
43+
- ["google::protobuf", "MessageLite", True, "SerializeToCord", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
44+
- ["google::protobuf", "MessageLite", True, "SerializePartialToCord", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
45+
- ["google::protobuf", "MessageLite", True, "AppendToCord", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
46+
- ["google::protobuf", "MessageLite", True, "AppendPartialToCord", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
47+
- ["google::protobuf", "MessageLite", True, "SerializeToOstream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
48+
- ["google::protobuf", "MessageLite", True, "SerializePartialToOstream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
49+
- ["google::protobuf", "MessageLite", True, "SerializeToZeroCopyStream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
50+
- ["google::protobuf", "MessageLite", True, "SerializePartialToZeroCopyStream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
51+
- ["google::protobuf", "MessageLite", True, "SerializeToCodedStream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
52+
- ["google::protobuf", "MessageLite", True, "SerializePartialToCodedStream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
53+
54+
# Serialization returning the bytes: the message (`this`) taints the (by-value) return value.
55+
- ["google::protobuf", "MessageLite", True, "SerializeAsString", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
56+
- ["google::protobuf", "MessageLite", True, "SerializePartialAsString", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
57+
- ["google::protobuf", "MessageLite", True, "SerializeAsCord", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
58+
- ["google::protobuf", "MessageLite", True, "SerializePartialAsCord", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]

cpp/ql/test/library-tests/dataflow/external-models/flow.expected

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ models
119119
| 118 | Summary: boost::asio::ip; basic_resolver<InternetProtocol>; false; resolve; (string_view,string_view,flags); ; Argument[0..1]; ReturnValue; taint; manual |
120120
| 119 | Summary: boost::asio::ip; basic_resolver<InternetProtocol>; false; resolve; (string_view,string_view,flags,error_code &); ; Argument[0..1]; ReturnValue; taint; manual |
121121
| 120 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
122+
| 121 | Summary: google::protobuf; MessageLite; true; ParseFromArray; ; ; Argument[*0]; Argument[-1]; taint; manual |
123+
| 122 | Summary: google::protobuf; MessageLite; true; ParseFromString; ; ; Argument[*0]; Argument[-1]; taint; manual |
124+
| 123 | Summary: google::protobuf; MessageLite; true; SerializeAsString; ; ; Argument[-1]; ReturnValue; taint; manual |
125+
| 124 | Summary: google::protobuf; MessageLite; true; SerializeToArray; ; ; Argument[-1]; Argument[*0]; taint; manual |
122126
edges
123127
| asio_streams.cpp:129:34:129:44 | read_until output argument | asio_streams.cpp:133:7:133:17 | recv_buffer | provenance | Src:MaD:56 |
124128
| asio_streams.cpp:129:34:129:44 | read_until output argument | asio_streams.cpp:135:29:135:39 | recv_buffer | provenance | Src:MaD:56 Sink:MaD:4 |
@@ -263,6 +267,26 @@ edges
263267
| bdlbb.cpp:94:46:94:48 | *src | bdlbb.cpp:94:37:94:40 | copy output argument | provenance | MaD:100 |
264268
| bdlbb.cpp:96:37:96:39 | copy output argument | bdlbb.cpp:97:7:97:10 | * ... | provenance | |
265269
| bdlbb.cpp:96:42:96:44 | *dst | bdlbb.cpp:96:37:96:39 | copy output argument | provenance | MaD:102 |
270+
| protobuf.cpp:75:33:75:40 | call to source | protobuf.cpp:76:22:76:25 | *data | provenance | TaintFunction |
271+
| protobuf.cpp:76:2:76:4 | ParseFromString output argument | protobuf.cpp:78:2:78:4 | *msg | provenance | |
272+
| protobuf.cpp:76:22:76:25 | *data | protobuf.cpp:76:2:76:4 | ParseFromString output argument | provenance | MaD:122 |
273+
| protobuf.cpp:78:2:78:4 | *msg | protobuf.cpp:78:23:78:25 | SerializeToArray output argument | provenance | MaD:124 |
274+
| protobuf.cpp:78:23:78:25 | SerializeToArray output argument | protobuf.cpp:79:7:79:10 | * ... | provenance | |
275+
| protobuf.cpp:84:33:84:40 | call to source | protobuf.cpp:85:21:85:31 | *call to data | provenance | TaintFunction |
276+
| protobuf.cpp:85:2:85:4 | ParseFromArray output argument | protobuf.cpp:87:2:87:4 | *msg | provenance | |
277+
| protobuf.cpp:85:21:85:31 | *call to data | protobuf.cpp:85:2:85:4 | ParseFromArray output argument | provenance | MaD:121 |
278+
| protobuf.cpp:87:2:87:4 | *msg | protobuf.cpp:87:23:87:25 | SerializeToArray output argument | provenance | MaD:124 |
279+
| protobuf.cpp:87:23:87:25 | SerializeToArray output argument | protobuf.cpp:88:7:88:10 | * ... | provenance | |
280+
| protobuf.cpp:95:33:95:40 | call to source | protobuf.cpp:96:22:96:25 | *data | provenance | TaintFunction |
281+
| protobuf.cpp:96:2:96:4 | ParseFromString output argument | protobuf.cpp:98:23:98:25 | *msg | provenance | |
282+
| protobuf.cpp:96:22:96:25 | *data | protobuf.cpp:96:2:96:4 | ParseFromString output argument | provenance | MaD:122 |
283+
| protobuf.cpp:98:2:98:5 | ParseFromString output argument | protobuf.cpp:100:2:100:5 | *msg2 | provenance | |
284+
| protobuf.cpp:98:23:98:25 | *msg | protobuf.cpp:98:27:98:43 | call to SerializeAsString | provenance | MaD:123 |
285+
| protobuf.cpp:98:23:98:45 | call to SerializeAsString | protobuf.cpp:98:2:98:5 | ParseFromString output argument | provenance | MaD:122 |
286+
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | protobuf.cpp:98:23:98:45 | call to SerializeAsString | provenance | |
287+
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | protobuf.cpp:98:27:98:43 | call to SerializeAsString | provenance | |
288+
| protobuf.cpp:100:2:100:5 | *msg2 | protobuf.cpp:100:24:100:26 | SerializeToArray output argument | provenance | MaD:124 |
289+
| protobuf.cpp:100:24:100:26 | SerializeToArray output argument | protobuf.cpp:101:7:101:10 | * ... | provenance | |
266290
| test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | |
267291
| test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | |
268292
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:48 |
@@ -765,6 +789,29 @@ nodes
765789
| bdlbb.cpp:96:37:96:39 | copy output argument | semmle.label | copy output argument |
766790
| bdlbb.cpp:96:42:96:44 | *dst | semmle.label | *dst |
767791
| bdlbb.cpp:97:7:97:10 | * ... | semmle.label | * ... |
792+
| protobuf.cpp:75:33:75:40 | call to source | semmle.label | call to source |
793+
| protobuf.cpp:76:2:76:4 | ParseFromString output argument | semmle.label | ParseFromString output argument |
794+
| protobuf.cpp:76:22:76:25 | *data | semmle.label | *data |
795+
| protobuf.cpp:78:2:78:4 | *msg | semmle.label | *msg |
796+
| protobuf.cpp:78:23:78:25 | SerializeToArray output argument | semmle.label | SerializeToArray output argument |
797+
| protobuf.cpp:79:7:79:10 | * ... | semmle.label | * ... |
798+
| protobuf.cpp:84:33:84:40 | call to source | semmle.label | call to source |
799+
| protobuf.cpp:85:2:85:4 | ParseFromArray output argument | semmle.label | ParseFromArray output argument |
800+
| protobuf.cpp:85:21:85:31 | *call to data | semmle.label | *call to data |
801+
| protobuf.cpp:87:2:87:4 | *msg | semmle.label | *msg |
802+
| protobuf.cpp:87:23:87:25 | SerializeToArray output argument | semmle.label | SerializeToArray output argument |
803+
| protobuf.cpp:88:7:88:10 | * ... | semmle.label | * ... |
804+
| protobuf.cpp:95:33:95:40 | call to source | semmle.label | call to source |
805+
| protobuf.cpp:96:2:96:4 | ParseFromString output argument | semmle.label | ParseFromString output argument |
806+
| protobuf.cpp:96:22:96:25 | *data | semmle.label | *data |
807+
| protobuf.cpp:98:2:98:5 | ParseFromString output argument | semmle.label | ParseFromString output argument |
808+
| protobuf.cpp:98:23:98:25 | *msg | semmle.label | *msg |
809+
| protobuf.cpp:98:23:98:45 | call to SerializeAsString | semmle.label | call to SerializeAsString |
810+
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | semmle.label | call to SerializeAsString |
811+
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | semmle.label | call to SerializeAsString |
812+
| protobuf.cpp:100:2:100:5 | *msg2 | semmle.label | *msg2 |
813+
| protobuf.cpp:100:24:100:26 | SerializeToArray output argument | semmle.label | SerializeToArray output argument |
814+
| protobuf.cpp:101:7:101:10 | * ... | semmle.label | * ... |
768815
| test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | semmle.label | *ymlStepGenerated_with_body |
769816
| test.cpp:7:47:7:52 | value2 | semmle.label | value2 |
770817
| test.cpp:7:64:7:69 | value2 | semmle.label | value2 |
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
2+
// --- stub library headers ---
3+
4+
namespace std {
5+
typedef unsigned long size_t;
6+
7+
template <class T> class allocator {
8+
};
9+
10+
template<class charT> struct char_traits {
11+
};
12+
13+
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
14+
class basic_string {
15+
public:
16+
basic_string();
17+
basic_string(const charT* s, const Allocator& a = Allocator());
18+
const charT* data() const;
19+
size_t size() const;
20+
};
21+
22+
typedef basic_string<char> string;
23+
24+
class istream {
25+
public:
26+
istream();
27+
};
28+
29+
class ostream {
30+
public:
31+
ostream();
32+
};
33+
}
34+
35+
namespace google {
36+
namespace protobuf {
37+
// A faithful subset of `MessageLite`; every method below is declared on `MessageLite`
38+
// in the real headers (message_lite.h), including the iostream-based ones.
39+
class MessageLite {
40+
public:
41+
bool ParseFromString(const std::string &data);
42+
bool MergeFromString(const std::string &data);
43+
bool ParsePartialFromString(const std::string &data);
44+
bool ParseFromArray(const void *data, int size);
45+
bool ParseFromIstream(std::istream *input);
46+
bool SerializeToString(std::string *output) const;
47+
bool SerializePartialToString(std::string *output) const;
48+
bool AppendToString(std::string *output) const;
49+
bool SerializeToArray(void *data, int size) const;
50+
bool SerializeToOstream(std::ostream *output) const;
51+
std::string SerializeAsString() const;
52+
};
53+
54+
class Message : public MessageLite {
55+
};
56+
}
57+
}
58+
59+
// A generated message type derives from `Message`.
60+
class Person : public google::protobuf::Message {
61+
};
62+
63+
// --- test code ---
64+
65+
char *source();
66+
void sink(char);
67+
68+
// Message taint is observed through `SerializeToArray`, whose scalar output flows cleanly
69+
// to a sink. The object-typed serialize outputs (String/Ostream/...) and the input-stream
70+
// parse methods are checked directly by `steps.ql`, which asserts each summary step exists.
71+
72+
// Deserialization: the encoded input taints the message (`this`).
73+
void test_ParseFromString() {
74+
Person msg;
75+
std::string data = std::string(source());
76+
msg.ParseFromString(data);
77+
char buf[64];
78+
msg.SerializeToArray(buf, sizeof(buf));
79+
sink(*buf); // $ ir
80+
}
81+
82+
void test_ParseFromArray() {
83+
Person msg;
84+
std::string data = std::string(source());
85+
msg.ParseFromArray(data.data(), data.size());
86+
char buf[64];
87+
msg.SerializeToArray(buf, sizeof(buf));
88+
sink(*buf); // $ ir
89+
}
90+
91+
// Serialization returning the bytes: the message taints the returned string, observed by
92+
// parsing it into a second message and serializing that back out.
93+
void test_SerializeAsString() {
94+
Person msg;
95+
std::string data = std::string(source());
96+
msg.ParseFromString(data);
97+
Person msg2;
98+
msg2.ParseFromString(msg.SerializeAsString());
99+
char buf[64];
100+
msg2.SerializeToArray(buf, sizeof(buf));
101+
sink(*buf); // $ ir
102+
}
103+
104+
// Additional modeled methods, exercised so their summary steps are covered by `steps.ql`.
105+
void test_step_coverage() {
106+
Person msg;
107+
std::string data = std::string(source());
108+
109+
msg.MergeFromString(data);
110+
msg.ParsePartialFromString(data);
111+
112+
std::istream in;
113+
msg.ParseFromIstream(&in);
114+
115+
std::string out;
116+
msg.SerializeToString(&out);
117+
msg.SerializePartialToString(&out);
118+
msg.AppendToString(&out);
119+
120+
std::ostream os;
121+
msg.SerializeToOstream(&os);
122+
}

cpp/ql/test/library-tests/dataflow/external-models/steps.expected

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@
8282
| bdlbb.cpp:92:48:92:51 | *call to data | bdlbb.cpp:92:37:92:40 | copy output argument |
8383
| bdlbb.cpp:94:46:94:48 | *src | bdlbb.cpp:94:37:94:40 | copy output argument |
8484
| bdlbb.cpp:96:42:96:44 | *dst | bdlbb.cpp:96:37:96:39 | copy output argument |
85+
| protobuf.cpp:76:22:76:25 | *data | protobuf.cpp:76:2:76:4 | ParseFromString output argument |
86+
| protobuf.cpp:78:2:78:4 | *msg | protobuf.cpp:78:23:78:25 | SerializeToArray output argument |
87+
| protobuf.cpp:85:21:85:31 | *call to data | protobuf.cpp:85:2:85:4 | ParseFromArray output argument |
88+
| protobuf.cpp:87:2:87:4 | *msg | protobuf.cpp:87:23:87:25 | SerializeToArray output argument |
89+
| protobuf.cpp:96:22:96:25 | *data | protobuf.cpp:96:2:96:4 | ParseFromString output argument |
90+
| protobuf.cpp:98:23:98:25 | *msg | protobuf.cpp:98:27:98:43 | call to SerializeAsString |
91+
| protobuf.cpp:98:23:98:45 | call to SerializeAsString | protobuf.cpp:98:2:98:5 | ParseFromString output argument |
92+
| protobuf.cpp:100:2:100:5 | *msg2 | protobuf.cpp:100:24:100:26 | SerializeToArray output argument |
93+
| protobuf.cpp:109:22:109:25 | *data | protobuf.cpp:109:2:109:4 | MergeFromString output argument |
94+
| protobuf.cpp:110:29:110:32 | *data | protobuf.cpp:110:2:110:4 | ParsePartialFromString output argument |
95+
| protobuf.cpp:113:23:113:25 | *& ... | protobuf.cpp:113:2:113:4 | ParseFromIstream output argument |
96+
| protobuf.cpp:116:2:116:4 | *msg | protobuf.cpp:116:24:116:27 | SerializeToString output argument |
97+
| protobuf.cpp:117:2:117:4 | *msg | protobuf.cpp:117:31:117:34 | SerializePartialToString output argument |
98+
| protobuf.cpp:118:2:118:4 | *msg | protobuf.cpp:118:21:118:24 | AppendToString output argument |
99+
| protobuf.cpp:121:2:121:4 | *msg | protobuf.cpp:121:25:121:27 | SerializeToOstream output argument |
85100
| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual |
86101
| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated |
87102
| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body |

0 commit comments

Comments
 (0)