Skip to content

Commit ed540df

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 b756a08 commit ed540df

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
@@ -96,6 +96,10 @@ models
9696
| 95 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual |
9797
| 96 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
9898
| 97 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
99+
| 98 | Summary: google::protobuf; MessageLite; true; ParseFromArray; ; ; Argument[*0]; Argument[-1]; taint; manual |
100+
| 99 | Summary: google::protobuf; MessageLite; true; ParseFromString; ; ; Argument[*0]; Argument[-1]; taint; manual |
101+
| 100 | Summary: google::protobuf; MessageLite; true; SerializeAsString; ; ; Argument[-1]; ReturnValue; taint; manual |
102+
| 101 | Summary: google::protobuf; MessageLite; true; SerializeToArray; ; ; Argument[-1]; Argument[*0]; taint; manual |
99103
edges
100104
| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:56 |
101105
| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:93:29:93:39 | recv_buffer | provenance | Src:MaD:56 Sink:MaD:4 |
@@ -144,6 +148,26 @@ edges
144148
| azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | |
145149
| azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | |
146150
| azure.cpp:295:10:295:20 | contentType | azure.cpp:295:10:295:20 | contentType | provenance | |
151+
| protobuf.cpp:75:33:75:40 | call to source | protobuf.cpp:76:22:76:25 | *data | provenance | TaintFunction |
152+
| protobuf.cpp:76:2:76:4 | ParseFromString output argument | protobuf.cpp:78:2:78:4 | *msg | provenance | |
153+
| protobuf.cpp:76:22:76:25 | *data | protobuf.cpp:76:2:76:4 | ParseFromString output argument | provenance | MaD:99 |
154+
| protobuf.cpp:78:2:78:4 | *msg | protobuf.cpp:78:23:78:25 | SerializeToArray output argument | provenance | MaD:101 |
155+
| protobuf.cpp:78:23:78:25 | SerializeToArray output argument | protobuf.cpp:79:7:79:10 | * ... | provenance | |
156+
| protobuf.cpp:84:33:84:40 | call to source | protobuf.cpp:85:21:85:31 | *call to data | provenance | TaintFunction |
157+
| protobuf.cpp:85:2:85:4 | ParseFromArray output argument | protobuf.cpp:87:2:87:4 | *msg | provenance | |
158+
| protobuf.cpp:85:21:85:31 | *call to data | protobuf.cpp:85:2:85:4 | ParseFromArray output argument | provenance | MaD:98 |
159+
| protobuf.cpp:87:2:87:4 | *msg | protobuf.cpp:87:23:87:25 | SerializeToArray output argument | provenance | MaD:101 |
160+
| protobuf.cpp:87:23:87:25 | SerializeToArray output argument | protobuf.cpp:88:7:88:10 | * ... | provenance | |
161+
| protobuf.cpp:95:33:95:40 | call to source | protobuf.cpp:96:22:96:25 | *data | provenance | TaintFunction |
162+
| protobuf.cpp:96:2:96:4 | ParseFromString output argument | protobuf.cpp:98:23:98:25 | *msg | provenance | |
163+
| protobuf.cpp:96:22:96:25 | *data | protobuf.cpp:96:2:96:4 | ParseFromString output argument | provenance | MaD:99 |
164+
| protobuf.cpp:98:2:98:5 | ParseFromString output argument | protobuf.cpp:100:2:100:5 | *msg2 | provenance | |
165+
| protobuf.cpp:98:23:98:25 | *msg | protobuf.cpp:98:27:98:43 | call to SerializeAsString | provenance | MaD:100 |
166+
| protobuf.cpp:98:23:98:45 | call to SerializeAsString | protobuf.cpp:98:2:98:5 | ParseFromString output argument | provenance | MaD:99 |
167+
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | protobuf.cpp:98:23:98:45 | call to SerializeAsString | provenance | |
168+
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | protobuf.cpp:98:27:98:43 | call to SerializeAsString | provenance | |
169+
| protobuf.cpp:100:2:100:5 | *msg2 | protobuf.cpp:100:24:100:26 | SerializeToArray output argument | provenance | MaD:101 |
170+
| protobuf.cpp:100:24:100:26 | SerializeToArray output argument | protobuf.cpp:101:7:101:10 | * ... | provenance | |
147171
| test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | |
148172
| test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | |
149173
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:48 |
@@ -532,6 +556,29 @@ nodes
532556
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
533557
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
534558
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
559+
| protobuf.cpp:75:33:75:40 | call to source | semmle.label | call to source |
560+
| protobuf.cpp:76:2:76:4 | ParseFromString output argument | semmle.label | ParseFromString output argument |
561+
| protobuf.cpp:76:22:76:25 | *data | semmle.label | *data |
562+
| protobuf.cpp:78:2:78:4 | *msg | semmle.label | *msg |
563+
| protobuf.cpp:78:23:78:25 | SerializeToArray output argument | semmle.label | SerializeToArray output argument |
564+
| protobuf.cpp:79:7:79:10 | * ... | semmle.label | * ... |
565+
| protobuf.cpp:84:33:84:40 | call to source | semmle.label | call to source |
566+
| protobuf.cpp:85:2:85:4 | ParseFromArray output argument | semmle.label | ParseFromArray output argument |
567+
| protobuf.cpp:85:21:85:31 | *call to data | semmle.label | *call to data |
568+
| protobuf.cpp:87:2:87:4 | *msg | semmle.label | *msg |
569+
| protobuf.cpp:87:23:87:25 | SerializeToArray output argument | semmle.label | SerializeToArray output argument |
570+
| protobuf.cpp:88:7:88:10 | * ... | semmle.label | * ... |
571+
| protobuf.cpp:95:33:95:40 | call to source | semmle.label | call to source |
572+
| protobuf.cpp:96:2:96:4 | ParseFromString output argument | semmle.label | ParseFromString output argument |
573+
| protobuf.cpp:96:22:96:25 | *data | semmle.label | *data |
574+
| protobuf.cpp:98:2:98:5 | ParseFromString output argument | semmle.label | ParseFromString output argument |
575+
| protobuf.cpp:98:23:98:25 | *msg | semmle.label | *msg |
576+
| protobuf.cpp:98:23:98:45 | call to SerializeAsString | semmle.label | call to SerializeAsString |
577+
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | semmle.label | call to SerializeAsString |
578+
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | semmle.label | call to SerializeAsString |
579+
| protobuf.cpp:100:2:100:5 | *msg2 | semmle.label | *msg2 |
580+
| protobuf.cpp:100:24:100:26 | SerializeToArray output argument | semmle.label | SerializeToArray output argument |
581+
| protobuf.cpp:101:7:101:10 | * ... | semmle.label | * ... |
535582
| test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | semmle.label | *ymlStepGenerated_with_body |
536583
| test.cpp:7:47:7:52 | value2 | semmle.label | value2 |
537584
| 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
@@ -4,6 +4,21 @@
44
| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument |
55
| azure.cpp:287:79:287:98 | call to string | azure.cpp:287:62:287:99 | call to Url |
66
| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value |
7+
| protobuf.cpp:76:22:76:25 | *data | protobuf.cpp:76:2:76:4 | ParseFromString output argument |
8+
| protobuf.cpp:78:2:78:4 | *msg | protobuf.cpp:78:23:78:25 | SerializeToArray output argument |
9+
| protobuf.cpp:85:21:85:31 | *call to data | protobuf.cpp:85:2:85:4 | ParseFromArray output argument |
10+
| protobuf.cpp:87:2:87:4 | *msg | protobuf.cpp:87:23:87:25 | SerializeToArray output argument |
11+
| protobuf.cpp:96:22:96:25 | *data | protobuf.cpp:96:2:96:4 | ParseFromString output argument |
12+
| protobuf.cpp:98:23:98:25 | *msg | protobuf.cpp:98:27:98:43 | call to SerializeAsString |
13+
| protobuf.cpp:98:23:98:45 | call to SerializeAsString | protobuf.cpp:98:2:98:5 | ParseFromString output argument |
14+
| protobuf.cpp:100:2:100:5 | *msg2 | protobuf.cpp:100:24:100:26 | SerializeToArray output argument |
15+
| protobuf.cpp:109:22:109:25 | *data | protobuf.cpp:109:2:109:4 | MergeFromString output argument |
16+
| protobuf.cpp:110:29:110:32 | *data | protobuf.cpp:110:2:110:4 | ParsePartialFromString output argument |
17+
| protobuf.cpp:113:23:113:25 | *& ... | protobuf.cpp:113:2:113:4 | ParseFromIstream output argument |
18+
| protobuf.cpp:116:2:116:4 | *msg | protobuf.cpp:116:24:116:27 | SerializeToString output argument |
19+
| protobuf.cpp:117:2:117:4 | *msg | protobuf.cpp:117:31:117:34 | SerializePartialToString output argument |
20+
| protobuf.cpp:118:2:118:4 | *msg | protobuf.cpp:118:21:118:24 | AppendToString output argument |
21+
| protobuf.cpp:121:2:121:4 | *msg | protobuf.cpp:121:25:121:27 | SerializeToOstream output argument |
722
| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual |
823
| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated |
924
| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body |

0 commit comments

Comments
 (0)