cpp: model Protocol Buffers parse/serialize taint flow - #22448
Conversation
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).
There was a problem hiding this comment.
Pull request overview
Adds C++ taint-flow summaries for Protocol Buffers MessageLite APIs and inherited generated message types.
Changes:
- Models parse/merge and serialization flows.
- Adds representative flow tests and expected results.
- Documents the analysis improvement.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
cpp/ql/lib/ext/Protobuf.model.yml |
Defines protobuf flow summaries. |
cpp/ql/test/library-tests/dataflow/external-models/protobuf.cpp |
Adds protobuf test fixtures. |
cpp/ql/test/library-tests/dataflow/external-models/flow.expected |
Updates flow expectations. |
cpp/ql/test/library-tests/dataflow/external-models/steps.expected |
Updates summary-step expectations. |
cpp/ql/lib/change-notes/2026-08-27-protobuf-models.md |
Records the new models. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| - ["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"] |
jketema
left a comment
There was a problem hiding this comment.
Thanks for this. I made a brief first pass over this, which should hopefully put you on the right path.
Co-authored-by: Jeroen Ketema <93738568+jketema@users.noreply.github.com>
jketema
left a comment
There was a problem hiding this comment.
Some further comments. I think the .yml file looks good now. I would still significantly reduce the number of comments, which don't seem to add much.
| # Deserialization: the encoded input taints the message (`this`). The `*FromString` methods each | ||
| # have a `string_view` overload (the buffer is the by-value argument, so `Argument[0]`) and a | ||
| # `const Cord &` overload (the buffer is behind a reference, so `Argument[*0]`). The remaining | ||
| # inputs below are pointers or references, so they take `Argument[*0]`. |
There was a problem hiding this comment.
| # Deserialization: the encoded input taints the message (`this`). The `*FromString` methods each | |
| # have a `string_view` overload (the buffer is the by-value argument, so `Argument[0]`) and a | |
| # `const Cord &` overload (the buffer is behind a reference, so `Argument[*0]`). The remaining | |
| # inputs below are pointers or references, so they take `Argument[*0]`. | |
| # Deserialization |
| // A faithful subset of `MessageLite`. The string/Cord/stream signatures mirror the real | ||
| // `message_lite.h`; the iostream-based methods are declared on `Message` in the real headers | ||
| // but are modeled here on `MessageLite` (with `subtypes` covering `Message`). |
There was a problem hiding this comment.
This read like a shortcut was taken that does not accurately represent actual protobuf. This should be fixed.
|
|
||
| // Every modeled method is called below so its summary step is covered by `steps.ql`. Endpoint | ||
| // mistakes and rows that fail to bind show up as missing lines in `steps.expected`. | ||
| void test_step_coverage() { |
There was a problem hiding this comment.
None of the "tests" below tell me that any of this is actually working. Ideally there should be sink calls here with // $ ir annotations.
Replace the step-coverage function with one sink test per model row using a template source, declare the Cord overloads of the ToString methods in the stub, drop the incorrect istream comment from the fixture, and shorten the model-file and change-note comments per review.
|
|
|
Internal testing showed nothing out of the ordinary. So if you fix the test, then this can be merged. |
Add flow summaries for the protobuf C++ API on google::protobuf::MessageLite (subtypes=true, so Message and all generated messages are covered):