Skip to content

C++: Implement MaD support for flow through perfect-forwarding functions - #22532

Open
MathiasVP wants to merge 13 commits into
github:mainfrom
MathiasVP:flow-through-forwards-using-callbacks-3
Open

C++: Implement MaD support for flow through perfect-forwarding functions#22532
MathiasVP wants to merge 13 commits into
github:mainfrom
MathiasVP:flow-through-forwards-using-callbacks-3

Conversation

@MathiasVP

Copy link
Copy Markdown
Contributor

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:

struct Foo {
  Foo(int);
};

std::vector<Foo> v;

we model a call such as v.emplace_back(42) as:

v.emplace_back(42, &Foo)

and give emplace_back two summaries:

  1. Argument[0] -> Argument[1].Parameter[0]
  2. Argument[1].Parameter[this] -> Argument[this].Element

The first summary states that 42 goes into the 0'th parameter of the Foo constructor, and the second summary states that the this parameter of the constructed object goes into the this argument of v with an Element content.

(A few lines I told in the above paragraph:

  • You cannot take the address of a constructor in C++. But that doesn't mean we cannot use it as an implementation detail!
  • We don't actually use the last argument as the argument position of the synthetic function pointer for the constructor. This PR adds a new argument position which we name forward in 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:

std::vector<Foo> v;
v.emplace_back(42); // calls `Foo(42)`
v.emplace(v.begin(), 42); // calls `Foo(42)`
std::make_unique<Foo> p(42); // calls `Foo(42)`

To know which type is constructed we add a new extensible called forwardsModel with 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 to T(args0, ..., argsN). The 0 specifies an offset so we can support cases like v.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.

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.
Comment thread cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll Dismissed
@MathiasVP MathiasVP added no-change-note-required This PR does not need a change note C++ labels Sep 9, 2026
@MathiasVP
MathiasVP marked this pull request as ready for review September 9, 2026 10:08
@MathiasVP
MathiasVP requested a review from a team as a code owner September 9, 2026 10:08
Copilot AI balanced review requested due to automatic review settings September 9, 2026 10:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 Medium severity · 2 Low severity

New issues introduced by this change (8)
Severity Finding
Medium severity cpp/​ql/​lib/​semmle/​code/​cpp/​dataflow/​ExternalFlow.qll — Make the concrete-type fallback exclusive. When constructorType names a template placeholder such…
Medium severity cpp/​ql/​lib/​semmle/​code/​cpp/​ir/​dataflow/​internal/​DataFlowPrivate.qll — Constrain numberOfForwardedArguments to the constructor's accepted arity. As written, a…
Medium severity cpp/​ql/​lib/​semmle/​code/​cpp/​ir/​dataflow/​internal/​DataFlowPrivate.qll — Removing references and requiring type equality does not reproduce constructor overload resolution.…
Medium severity cpp/​ql/​lib/​semmle/​code/​cpp/​ir/​implementation/​aliased_ssa/​Instruction.qll — Count positional operands rather than their defining instructions. QL deduplicates equal result…
Medium severity cpp/​ql/​lib/​semmle/​code/​cpp/​ir/​implementation/​raw/​Instruction.qll — Count positional operands rather than their defining instructions. QL deduplicates equal result…
Medium severity cpp/​ql/​lib/​semmle/​code/​cpp/​ir/​implementation/​unaliased_ssa/​Instruction.qll — Count positional operands rather than their defining instructions. QL deduplicates equal result…
Low severity cpp/​ql/​lib/​semmle/​code/​cpp/​dataflow/​ExternalFlow.qll — Document the semantics of the three new forwarding-specific columns. The schema lists start,…
Low severity 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 forwardsModel and 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.

Comment thread cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
Comment thread cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
Comment on lines +622 to +624
i < numberOfForwardedArguments and
stripReferences(call.getPositionalArgument(start + i).getResultType()) =
stripReferences(constructor.getParameter(i).getUnspecifiedType())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm not going to implement full C++ overload resolution in this PR. So I'll just leave this as-is for now.

Comment thread cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll Outdated
Comment thread cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll Outdated
Comment thread cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll Outdated
Comment thread cpp/ql/lib/semmle/code/cpp/dataflow/ExternalFlow.qll
Comment thread cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll Outdated
@MathiasVP

Copy link
Copy Markdown
Contributor Author

@hvitved I'm finally done responding to Copilot's (really excellent!) review comments. It should be all good now 🤞

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C++ no-change-note-required This PR does not need a change note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants