C++: Implement MaD support for flow through perfect-forwarding functions - #22532
C++: Implement MaD support for flow through perfect-forwarding functions#22532MathiasVP wants to merge 13 commits into
Conversation
In the upcoming commits we will add a new extensional predicate which allows us to model that a function forwards it arguments to the constructor of a given type. This initial commit adds the test YAML models for this new extensional predicate.
signature to implement the forwardsModel. So instead of recursing on the number of elements in the signature we will recurse on the number of elements in the type (or name) columns. For well-formed models this will be equivalent.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Constructor arity, overload resolution, and positional-argument counting can currently produce missing or spurious flow targets.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review tier: Balanced
Findings: 6
New issues introduced by this change (8)
| Severity | Finding |
|---|---|
cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll — Make the concrete-type fallback exclusive. When constructorType names a template placeholder such… |
|
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll — Constrain numberOfForwardedArguments to the constructor's accepted arity. As written, a… |
|
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll — Removing references and requiring type equality does not reproduce constructor overload resolution.… |
|
cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll — Count positional operands rather than their defining instructions. QL deduplicates equal result… |
|
cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll — Count positional operands rather than their defining instructions. QL deduplicates equal result… |
|
cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll — Count positional operands rather than their defining instructions. QL deduplicates equal result… |
|
cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll — Document the semantics of the three new forwarding-specific columns. The schema lists start,… |
|
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll — Use “never mind” as two words in this parenthetical phrase. |
What changed in this PR
Adds MaD support for modeling data flow through perfect-forwarding C++ functions into constructors.
Changes:
- Introduces
forwardsModeland synthetic constructor argument positions. - Adds positional-argument and value-category indirection helpers.
- Adds forwarding and default-argument test coverage.
| File | Description |
|---|---|
cpp/ql/test/library-tests/dataflow/external-models/test.cpp |
Adds forwarding scenarios. |
cpp/ql/test/library-tests/dataflow/external-models/sources.expected |
Updates expected sources. |
cpp/ql/test/library-tests/dataflow/external-models/sinks.expected |
Updates expected sinks. |
cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml |
Defines test forwarding models. |
cpp/ql/test/library-tests/dataflow/external-models/flow.expected |
Updates expected flow graph. |
cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll |
Adds positional-argument helpers. |
cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll |
Adds positional-argument helpers. |
cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll |
Adds positional-argument helpers. |
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll |
Separates glvalue/prvalue indirections. |
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll |
Resolves synthetic constructor calls. |
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll |
Adds forwarding data-flow nodes. |
cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll |
Decodes forwarding positions. |
cpp/ql/lib/semmle/code/cpp/dataflow/internal/ExternalFlowExtensions.qll |
Declares forwardsModel. |
cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll |
Interprets forwarding models and summaries. |
cpp/ql/lib/ext/empty.model.yml |
Initializes the new extensible predicate. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| i < numberOfForwardedArguments and | ||
| stripReferences(call.getPositionalArgument(start + i).getResultType()) = | ||
| stripReferences(constructor.getParameter(i).getUnspecifiedType()) |
There was a problem hiding this comment.
Yeah, I'm not going to implement full C++ overload resolution in this PR. So I'll just leave this as-is for now.
C++: Accept Copilot suggestion Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
… many parameters as the number of forwarded arguments.
|
@hvitved I'm finally done responding to Copilot's (really excellent!) review comments. It should be all good now 🤞 |


This PR implements the necessary library changes to support MaD summaries for functions such as vector::emplace_back or make_unique. These functions receive a list of arguments and then forwards them to a constructor call.
@hvitved had a super cool implementation idea. Given:
we model a call such as
v.emplace_back(42)as:v.emplace_back(42, &Foo)and give
emplace_backtwo summaries:Argument[0] -> Argument[1].Parameter[0]Argument[1].Parameter[this] -> Argument[this].ElementThe first summary states that
42goes into the 0'th parameter of theFooconstructor, and the second summary states that thethisparameter of the constructed object goes into thethisargument ofvwith anElementcontent.(A few lines I told in the above paragraph:
forwardin MaD. I'm happy to change this name to something else if anyone has any strong opinions about this.)In order to know which constructor to forward to we need to know what type is being constructed. For example:
To know which type is constructed we add a new extensible called
forwardsModelwith rows very much like what we have for MaD summaries. For example, I've added this as row as a test:["", "Container<T>", True, "emplace<Args>", "(Args &&)", "", "0", "T", "manual"]this says that a call to
Container<T>::emplace(args0, ..., argsN)forwards its arguments to a call toT(args0, ..., argsN). The0specifies an offset so we can support cases likev.emplace(v.begin(), 42)where we need to ignore the first argument.This PR doesn't actually add any non-test MaD summaries. I'll delay that to a future PR.