Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cpp/ql/lib/change-notes/2026-08-27-protobuf-models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* 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.
58 changes: 58 additions & 0 deletions cpp/ql/lib/ext/Protobuf.model.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
extensions:
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
# Flow summaries for the Protocol Buffers C++ API. All of these methods are declared on
# `google::protobuf::MessageLite`; `subtypes` covers `Message` and every generated message.
#
# File-descriptor variants (`{Parse,Serialize}*FromFileDescriptor`) are intentionally omitted:
# the descriptor is an `int`, not a data buffer, so there is no buffer argument to model.

# Deserialization: the encoded input (`Argument[*0]`) taints the message (`this`).
- ["google::protobuf", "MessageLite", True, "ParseFromString", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParsePartialFromString", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "MergeFromString", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "MergePartialFromString", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParseFromArray", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParsePartialFromArray", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParseFromCord", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParsePartialFromCord", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "MergeFromCord", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "MergePartialFromCord", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParseFromIstream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParsePartialFromIstream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParseFromZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParsePartialFromZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParseFromBoundedZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParsePartialFromBoundedZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "MergeFromBoundedZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
Comment on lines +25 to +29
- ["google::protobuf", "MessageLite", True, "MergePartialFromBoundedZeroCopyStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParseFromCodedStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "ParsePartialFromCodedStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "MergeFromCodedStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "MergePartialFromCodedStream", "", "", "Argument[*0]", "Argument[-1]", "taint", "manual"]

# Serialization into an output buffer/stream: the message (`this`) taints `Argument[*0]`.
- ["google::protobuf", "MessageLite", True, "SerializeToString", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializePartialToString", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "AppendToString", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "AppendPartialToString", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializeToArray", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializePartialToArray", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializeToCord", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializePartialToCord", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "AppendToCord", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "AppendPartialToCord", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializeToOstream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializePartialToOstream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializeToZeroCopyStream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializePartialToZeroCopyStream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializeToCodedStream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializePartialToCodedStream", "", "", "Argument[-1]", "Argument[*0]", "taint", "manual"]

# Serialization returning the bytes: the message (`this`) taints the (by-value) return value.
- ["google::protobuf", "MessageLite", True, "SerializeAsString", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializePartialAsString", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializeAsCord", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
- ["google::protobuf", "MessageLite", True, "SerializePartialAsCord", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ models
| 95 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual |
| 96 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
| 97 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
| 98 | Summary: google::protobuf; MessageLite; true; ParseFromArray; ; ; Argument[*0]; Argument[-1]; taint; manual |
| 99 | Summary: google::protobuf; MessageLite; true; ParseFromString; ; ; Argument[*0]; Argument[-1]; taint; manual |
| 100 | Summary: google::protobuf; MessageLite; true; SerializeAsString; ; ; Argument[-1]; ReturnValue; taint; manual |
| 101 | Summary: google::protobuf; MessageLite; true; SerializeToArray; ; ; Argument[-1]; Argument[*0]; taint; manual |
edges
| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:56 |
| 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 |
Expand Down Expand Up @@ -144,6 +148,26 @@ edges
| azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | |
| azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | |
| azure.cpp:295:10:295:20 | contentType | azure.cpp:295:10:295:20 | contentType | provenance | |
| protobuf.cpp:75:33:75:40 | call to source | protobuf.cpp:76:22:76:25 | *data | provenance | TaintFunction |
| protobuf.cpp:76:2:76:4 | ParseFromString output argument | protobuf.cpp:78:2:78:4 | *msg | provenance | |
| protobuf.cpp:76:22:76:25 | *data | protobuf.cpp:76:2:76:4 | ParseFromString output argument | provenance | MaD:99 |
| protobuf.cpp:78:2:78:4 | *msg | protobuf.cpp:78:23:78:25 | SerializeToArray output argument | provenance | MaD:101 |
| protobuf.cpp:78:23:78:25 | SerializeToArray output argument | protobuf.cpp:79:7:79:10 | * ... | provenance | |
| protobuf.cpp:84:33:84:40 | call to source | protobuf.cpp:85:21:85:31 | *call to data | provenance | TaintFunction |
| protobuf.cpp:85:2:85:4 | ParseFromArray output argument | protobuf.cpp:87:2:87:4 | *msg | provenance | |
| protobuf.cpp:85:21:85:31 | *call to data | protobuf.cpp:85:2:85:4 | ParseFromArray output argument | provenance | MaD:98 |
| protobuf.cpp:87:2:87:4 | *msg | protobuf.cpp:87:23:87:25 | SerializeToArray output argument | provenance | MaD:101 |
| protobuf.cpp:87:23:87:25 | SerializeToArray output argument | protobuf.cpp:88:7:88:10 | * ... | provenance | |
| protobuf.cpp:95:33:95:40 | call to source | protobuf.cpp:96:22:96:25 | *data | provenance | TaintFunction |
| protobuf.cpp:96:2:96:4 | ParseFromString output argument | protobuf.cpp:98:23:98:25 | *msg | provenance | |
| protobuf.cpp:96:22:96:25 | *data | protobuf.cpp:96:2:96:4 | ParseFromString output argument | provenance | MaD:99 |
| protobuf.cpp:98:2:98:5 | ParseFromString output argument | protobuf.cpp:100:2:100:5 | *msg2 | provenance | |
| protobuf.cpp:98:23:98:25 | *msg | protobuf.cpp:98:27:98:43 | call to SerializeAsString | provenance | MaD:100 |
| protobuf.cpp:98:23:98:45 | call to SerializeAsString | protobuf.cpp:98:2:98:5 | ParseFromString output argument | provenance | MaD:99 |
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | protobuf.cpp:98:23:98:45 | call to SerializeAsString | provenance | |
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | protobuf.cpp:98:27:98:43 | call to SerializeAsString | provenance | |
| protobuf.cpp:100:2:100:5 | *msg2 | protobuf.cpp:100:24:100:26 | SerializeToArray output argument | provenance | MaD:101 |
| protobuf.cpp:100:24:100:26 | SerializeToArray output argument | protobuf.cpp:101:7:101:10 | * ... | provenance | |
| test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | |
| test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:48 |
Expand Down Expand Up @@ -532,6 +556,29 @@ nodes
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
| protobuf.cpp:75:33:75:40 | call to source | semmle.label | call to source |
| protobuf.cpp:76:2:76:4 | ParseFromString output argument | semmle.label | ParseFromString output argument |
| protobuf.cpp:76:22:76:25 | *data | semmle.label | *data |
| protobuf.cpp:78:2:78:4 | *msg | semmle.label | *msg |
| protobuf.cpp:78:23:78:25 | SerializeToArray output argument | semmle.label | SerializeToArray output argument |
| protobuf.cpp:79:7:79:10 | * ... | semmle.label | * ... |
| protobuf.cpp:84:33:84:40 | call to source | semmle.label | call to source |
| protobuf.cpp:85:2:85:4 | ParseFromArray output argument | semmle.label | ParseFromArray output argument |
| protobuf.cpp:85:21:85:31 | *call to data | semmle.label | *call to data |
| protobuf.cpp:87:2:87:4 | *msg | semmle.label | *msg |
| protobuf.cpp:87:23:87:25 | SerializeToArray output argument | semmle.label | SerializeToArray output argument |
| protobuf.cpp:88:7:88:10 | * ... | semmle.label | * ... |
| protobuf.cpp:95:33:95:40 | call to source | semmle.label | call to source |
| protobuf.cpp:96:2:96:4 | ParseFromString output argument | semmle.label | ParseFromString output argument |
| protobuf.cpp:96:22:96:25 | *data | semmle.label | *data |
| protobuf.cpp:98:2:98:5 | ParseFromString output argument | semmle.label | ParseFromString output argument |
| protobuf.cpp:98:23:98:25 | *msg | semmle.label | *msg |
| protobuf.cpp:98:23:98:45 | call to SerializeAsString | semmle.label | call to SerializeAsString |
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | semmle.label | call to SerializeAsString |
| protobuf.cpp:98:27:98:43 | call to SerializeAsString | semmle.label | call to SerializeAsString |
| protobuf.cpp:100:2:100:5 | *msg2 | semmle.label | *msg2 |
| protobuf.cpp:100:24:100:26 | SerializeToArray output argument | semmle.label | SerializeToArray output argument |
| protobuf.cpp:101:7:101:10 | * ... | semmle.label | * ... |
| test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | semmle.label | *ymlStepGenerated_with_body |
| test.cpp:7:47:7:52 | value2 | semmle.label | value2 |
| test.cpp:7:64:7:69 | value2 | semmle.label | value2 |
Expand Down
122 changes: 122 additions & 0 deletions cpp/ql/test/library-tests/dataflow/external-models/protobuf.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@

// --- stub library headers ---

namespace std {
typedef unsigned long size_t;

template <class T> class allocator {
};

template<class charT> struct char_traits {
};

template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
class basic_string {
public:
basic_string();
basic_string(const charT* s, const Allocator& a = Allocator());
const charT* data() const;
size_t size() const;
};

typedef basic_string<char> string;

class istream {
public:
istream();
};

class ostream {
public:
ostream();
};
}

namespace google {
namespace protobuf {
// A faithful subset of `MessageLite`; every method below is declared on `MessageLite`
// in the real headers (message_lite.h), including the iostream-based ones.
class MessageLite {
public:
bool ParseFromString(const std::string &data);
bool MergeFromString(const std::string &data);
bool ParsePartialFromString(const std::string &data);
bool ParseFromArray(const void *data, int size);
bool ParseFromIstream(std::istream *input);
bool SerializeToString(std::string *output) const;
bool SerializePartialToString(std::string *output) const;
bool AppendToString(std::string *output) const;
bool SerializeToArray(void *data, int size) const;
bool SerializeToOstream(std::ostream *output) const;
std::string SerializeAsString() const;
};

class Message : public MessageLite {
};
}
}

// A generated message type derives from `Message`.
class Person : public google::protobuf::Message {
};

// --- test code ---

char *source();
void sink(char);

// Message taint is observed through `SerializeToArray`, whose scalar output flows cleanly
// to a sink. The object-typed serialize outputs (String/Ostream/...) and the input-stream
// parse methods are checked directly by `steps.ql`, which asserts each summary step exists.

// Deserialization: the encoded input taints the message (`this`).
void test_ParseFromString() {
Person msg;
std::string data = std::string(source());
msg.ParseFromString(data);
char buf[64];
msg.SerializeToArray(buf, sizeof(buf));
sink(*buf); // $ ir
}

void test_ParseFromArray() {
Person msg;
std::string data = std::string(source());
msg.ParseFromArray(data.data(), data.size());
char buf[64];
msg.SerializeToArray(buf, sizeof(buf));
sink(*buf); // $ ir
}

// Serialization returning the bytes: the message taints the returned string, observed by
// parsing it into a second message and serializing that back out.
void test_SerializeAsString() {
Person msg;
std::string data = std::string(source());
msg.ParseFromString(data);
Person msg2;
msg2.ParseFromString(msg.SerializeAsString());
char buf[64];
msg2.SerializeToArray(buf, sizeof(buf));
sink(*buf); // $ ir
}

// Additional modeled methods, exercised so their summary steps are covered by `steps.ql`.
void test_step_coverage() {
Person msg;
std::string data = std::string(source());

msg.MergeFromString(data);
msg.ParsePartialFromString(data);

std::istream in;
msg.ParseFromIstream(&in);

std::string out;
msg.SerializeToString(&out);
msg.SerializePartialToString(&out);
msg.AppendToString(&out);

std::ostream os;
msg.SerializeToOstream(&os);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@
| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument |
| azure.cpp:287:79:287:98 | call to string | azure.cpp:287:62:287:99 | call to Url |
| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value |
| protobuf.cpp:76:22:76:25 | *data | protobuf.cpp:76:2:76:4 | ParseFromString output argument |
| protobuf.cpp:78:2:78:4 | *msg | protobuf.cpp:78:23:78:25 | SerializeToArray output argument |
| protobuf.cpp:85:21:85:31 | *call to data | protobuf.cpp:85:2:85:4 | ParseFromArray output argument |
| protobuf.cpp:87:2:87:4 | *msg | protobuf.cpp:87:23:87:25 | SerializeToArray output argument |
| protobuf.cpp:96:22:96:25 | *data | protobuf.cpp:96:2:96:4 | ParseFromString output argument |
| protobuf.cpp:98:23:98:25 | *msg | protobuf.cpp:98:27:98:43 | call to SerializeAsString |
| protobuf.cpp:98:23:98:45 | call to SerializeAsString | protobuf.cpp:98:2:98:5 | ParseFromString output argument |
| protobuf.cpp:100:2:100:5 | *msg2 | protobuf.cpp:100:24:100:26 | SerializeToArray output argument |
| protobuf.cpp:109:22:109:25 | *data | protobuf.cpp:109:2:109:4 | MergeFromString output argument |
| protobuf.cpp:110:29:110:32 | *data | protobuf.cpp:110:2:110:4 | ParsePartialFromString output argument |
| protobuf.cpp:113:23:113:25 | *& ... | protobuf.cpp:113:2:113:4 | ParseFromIstream output argument |
| protobuf.cpp:116:2:116:4 | *msg | protobuf.cpp:116:24:116:27 | SerializeToString output argument |
| protobuf.cpp:117:2:117:4 | *msg | protobuf.cpp:117:31:117:34 | SerializePartialToString output argument |
| protobuf.cpp:118:2:118:4 | *msg | protobuf.cpp:118:21:118:24 | AppendToString output argument |
| protobuf.cpp:121:2:121:4 | *msg | protobuf.cpp:121:25:121:27 | SerializeToOstream output argument |
| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual |
| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated |
| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body |
Expand Down