Repository navigation
Conversation
47a1463 to
4c0bb1d
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The reference-conversion mapping can associate the same qualifier with both the reference address and the object, causing queries to select both instead of a single intended node.
3 open issues
What changed in this PR
Corrects C++ expression-to-data-flow-node mapping for glvalue member-call qualifiers, distinguishing object values from their addresses. This supports more precise standard-library modeling while retaining the existing pointer/pointee taint rule.
Changes:
- Maps eligible glvalue qualifiers to indirect nodes while preserving temporary-materialization handling.
- Updates expected node and path labels affected by the mapping change.
| File | Description |
|---|---|
cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected |
Updates qualifier labels in XXE query paths. |
cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected |
Updates qualifier labels in field-flow paths. |
cpp/ql/test/library-tests/dataflow/external-models/steps.expected |
Updates labels for modeled data-flow steps. |
cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/ExprNodes.qll |
Adds glvalue qualifier mapping logic and explanatory comments. |
Review effort: Max (auto)
… totally inverse of each other, but they will be after this PR is merged).
…f reference-related ones that currently causes duplicated results.
…in the convertion chain is a glvalue expression.
a58aecf to
a7eb87a
Compare
…ow depend on such a predicate.
| // instruction (which is of type `glvalue<Foo>`) is the qualifier. | ||
| // So the `DataFlow::Node` we want to represent `x` is the indirection of | ||
| // the qualifier (which will then have type `Foo`). | ||
| // Since the node -> expression mapping is one-to-many we additionally need to |
There was a problem hiding this comment.
I've a hard time following this, especially since the example you give seems many-to-one (there's only one expression in the example *x).
There was a problem hiding this comment.
Oh, right. I can see how this is a bit confusing. Let me clear up things in the conversation here, and then we can figure out how to improve the comments.
The instruction-to-node mapping is many-to-one, but the node-to-expression mapping is one-to-many. This does play a role here:
- Since the instruction-to-node mapping is many-to-one a single node can correspond to multiple instructions, and one of those can be a glvalue.
- Since the node-to-expression mapping is one-to-many a single node may correspond to multiple expression, and one of those can be a glvalue.
In the (*x).f() example we have:
- One node (representing the qualifier) consisting of three instructions:
r1(glval<Foo *>) = VariableAddress[x] : r2(Foo *) = Load[x] : &:r1 r3(glval<Foo>) = CopyValue : r2 - Two relevant expressions:
xand*x. Ther2instruction maps tox, andr3maps to*x. So the combined mapping (i.e., node-to-instruction) maps the node to bothxand*x, and only one of those is a glvalue.
Does that make it more clear? I'm still trying to figure out how to articulate this in a code comment 😅
There was a problem hiding this comment.
That makes it more clear indeed.
There was a problem hiding this comment.
I've slightly rewritten it in 4b9c587. Let me know if that transfers the necessary information to the comment! 🤞


This PR fixes a special case in the
Expr<->DataFlow::Nodemapping: When the qualifier instruction is a glvalue the resultingDataFlow::Nodefor whichasExprequals the qualifier expression was theInstructionNodefor the instruction, whereas it should be theIndirectInstructionNode.I noticed this problem in #22939 where we lost a result that I added as a testcase in #22990. It is essentially this situation:
On
mainit currently “works” because taint-tracking has this ad-hoc rule which conflates pointers and their indirection:codeql/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
Lines 212 to 229 in 682545f
I want to get rid of this disjunct at some point, but until we fix some more pointer/pointee conflation it has to stay (stay tuned for another upcoming PR!).
On
mainthis is the path we get for that test with debug-printing enabled:the
r153_3instruction is a glvalue-producing instruction which computes a pointer to thepasswordvariable. So thec_strstep steps from aglvalue<std::string>expression (the qualifier glvalue instruction) to aconst charexpression (the indirection of the resultingconst char*expression).When I disable the conflation rule and runs the query on this database again we get:
That is, we now start at the indirection of the qualifier (i.e., a
std::stringexpression) and thec_strmodel step gets us to aconst charexpression (the indirection of the resultingconst char*expression), as expected.Because of the above conflation there shouldn’t really be any changes to any results. However, since
toStringis defined in terms of which expressions a node maps to, there are lots of changes to .expected files.DCA was uneventful. There's a small change in a Coding Standards query's
.expectedfile that needs to be accepted. See the backlinked PR.Additionally, Copilot flagged up a problem with the new
asExprrule causing multipleDataFlow::Nodes'asExprto return the same expression for reference-typed qualifies. So that required a fix in 13a9724, and fixing that one revealed a bug inParameter <-> DataFlow::Nodemapping which affected Coding Standards:Node.asParameterreturned a non-indirect parameter most of the time, but for references it returns the indirect parameter (since that's most often what you want), but theDataFlow::parameterNodepredicate's return type required the node to be a direct parameter node. So this was fixed in a7eb87a.github/codeql-coding-standards#1218 should be merged once this PR goes in.