Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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-03-30-nsdmi-dataflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added dataflow through members initialized via non-static data member initialization (NSDMI).
45 changes: 26 additions & 19 deletions cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -850,11 +850,6 @@ module Public {
{
ThisParameterInstructionNode() { instr.getIRVariable() instanceof IRThisVariable }

override predicate isSourceParameterOf(Function f, ParameterPosition pos) {
pos.(DirectPosition).getArgumentIndex() = -1 and
instr.getEnclosingFunction() = f
}

override string toStringImpl() { result = "this" }
}

Expand Down Expand Up @@ -1129,7 +1124,7 @@ class IndirectArgumentOutNode extends PostUpdateNodeImpl {
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
Function getStaticCallTarget() { result = this.getCallInstruction().getStaticCallTarget() }
Declaration getStaticCallTarget() { result = this.getCallInstruction().getStaticCallTarget() }

override string toStringImpl() {
exists(string prefix | if indirectionIndex > 0 then prefix = "" else prefix = "pointer to " |
Expand Down Expand Up @@ -1633,7 +1628,7 @@ abstract private class AbstractParameterNode extends Node {
* implicit `this` parameter is considered to have position `-1`, and
* pointer-indirection parameters are at further negative positions.
*/
predicate isSourceParameterOf(Function f, ParameterPosition pos) { none() }
predicate isSourceParameterOf(Declaration f, ParameterPosition pos) { none() }

/**
* Holds if this node is the parameter of `sc` at the specified position. The
Expand All @@ -1659,6 +1654,11 @@ abstract private class AbstractParameterNode extends Node {

/** Gets the `Parameter` associated with this node, if it exists. */
Parameter getParameter() { none() } // overridden by subclasses

/**
* Holds if this node represents an implicit `this` parameter, if it exists.
Comment thread
MathiasVP marked this conversation as resolved.
*/
predicate isThis() { none() } // overridden by subclasses
}

abstract private class AbstractIndirectParameterNode extends AbstractParameterNode {
Expand Down Expand Up @@ -1701,16 +1701,17 @@ private class IndirectInstructionParameterNode extends AbstractIndirectParameter
)
}

/** Gets the parameter whose indirection is initialized. */
override Parameter getParameter() { result = init.getParameter() }

override predicate isThis() { init.hasIndex(-1) }

override DataFlowCallable getEnclosingCallable() {
result.asSourceCallable() = this.getFunction()
}

override Declaration getFunction() { result = init.getEnclosingFunction() }

override predicate isSourceParameterOf(Function f, ParameterPosition pos) {
override predicate isSourceParameterOf(Declaration f, ParameterPosition pos) {
this.getFunction() = f and
exists(int argumentIndex, int indirectionIndex |
indirectPositionHasArgumentIndexAndIndex(pos, argumentIndex, indirectionIndex) and
Expand Down Expand Up @@ -1738,6 +1739,18 @@ abstract class InstructionDirectParameterNode extends InstructionNode, AbstractD
* Gets the `IRVariable` that this parameter references.
*/
final IRVariable getIRVariable() { result = instr.getIRVariable() }

override predicate isThis() { instr.hasIndex(-1) }

override Parameter getParameter() { result = instr.getParameter() }

override predicate isSourceParameterOf(Declaration f, ParameterPosition pos) {
this.getFunction() = f and
exists(int argumentIndex |
pos.(DirectPosition).getArgumentIndex() = argumentIndex and
instr.hasIndex(argumentIndex)
)
}
}

abstract private class AbstractExplicitParameterNode extends AbstractDirectParameterNode { }
Expand All @@ -1748,13 +1761,7 @@ private class ExplicitParameterInstructionNode extends AbstractExplicitParameter
{
ExplicitParameterInstructionNode() { exists(instr.getParameter()) }

override predicate isSourceParameterOf(Function f, ParameterPosition pos) {
f.getParameter(pos.(DirectPosition).getArgumentIndex()) = instr.getParameter()
}

override string toStringImpl() { result = instr.getParameter().toString() }

override Parameter getParameter() { result = instr.getParameter() }
}

/**
Expand Down Expand Up @@ -1782,9 +1789,9 @@ private class DirectBodyLessParameterNode extends AbstractExplicitParameterNode,
{
DirectBodyLessParameterNode() { indirectionIndex = 0 }

override predicate isSourceParameterOf(Function f, ParameterPosition pos) {
override predicate isSourceParameterOf(Declaration f, ParameterPosition pos) {
this.getFunction() = f and
f.getParameter(pos.(DirectPosition).getArgumentIndex()) = p
f.(Function).getParameter(pos.(DirectPosition).getArgumentIndex()) = p
}

override Parameter getParameter() { result = p }
Expand All @@ -1795,10 +1802,10 @@ private class IndirectBodyLessParameterNode extends AbstractIndirectParameterNod
{
IndirectBodyLessParameterNode() { not this instanceof DirectBodyLessParameterNode }

override predicate isSourceParameterOf(Function f, ParameterPosition pos) {
override predicate isSourceParameterOf(Declaration f, ParameterPosition pos) {
exists(int argumentPosition |
this.getFunction() = f and
f.getParameter(argumentPosition) = p and
f.(Function).getParameter(argumentPosition) = p and
indirectPositionHasArgumentIndexAndIndex(pos, argumentPosition, indirectionIndex)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1170,7 +1170,7 @@ class DataFlowCall extends TDataFlowCall {
/**
* Gets the `Function` that the call targets, if this is statically known.
*/
Function getStaticCallSourceTarget() { none() }
Declaration getStaticCallSourceTarget() { none() }

/**
* Gets the target of this call. We use the following strategy for deciding
Expand All @@ -1182,7 +1182,7 @@ class DataFlowCall extends TDataFlowCall {
* whether is it manual or generated.
*/
final DataFlowCallable getStaticCallTarget() {
exists(Function target | target = this.getStaticCallSourceTarget() |
exists(Declaration target | target = this.getStaticCallSourceTarget() |
// Don't use the source callable if there is a manual model for the
// target
not exists(SummarizedCallable sc |
Expand Down Expand Up @@ -1242,7 +1242,7 @@ private class NormalCall extends DataFlowCall, TNormalCall {

override CallTargetOperand getCallTargetOperand() { result = call.getCallTargetOperand() }

override Function getStaticCallSourceTarget() { result = call.getStaticCallTarget() }
override Declaration getStaticCallSourceTarget() { result = call.getStaticCallTarget() }

override ArgumentOperand getArgumentOperand(int index) { result = call.getArgumentOperand(index) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ private import TypeFlow
private import semmle.code.cpp.ir.ValueNumbering

/**
* Gets the C++ type of `this` in the member function `f`.
* Gets the C++ type of `this` in an `IRFunction` generated from `f`.
* The result is a glvalue if `isGLValue` is true, and
* a prvalue if `isGLValue` is false.
Comment thread
MathiasVP marked this conversation as resolved.
*/
bindingset[isGLValue]
private CppType getThisType(Cpp::MemberFunction f, boolean isGLValue) {
result.hasType(f.getTypeOfThis(), isGLValue)
private CppType getThisType(Cpp::Declaration f, boolean isGLValue) {
result.hasType(f.(Cpp::MemberFunction).getTypeOfThis(), isGLValue)
or
exists(Cpp::PointerType pt |
pt.getBaseType() = f.(Cpp::Field).getDeclaringType() and
result.hasType(pt, isGLValue)
)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ irFlow
| test.cpp:1308:7:1308:12 | call to source | test.cpp:1309:8:1309:16 | ... ++ |
| test.cpp:1312:7:1312:12 | call to source | test.cpp:1313:8:1313:24 | ... ? ... : ... |
| test.cpp:1312:7:1312:12 | call to source | test.cpp:1314:8:1314:8 | x |
| test.cpp:1318:13:1318:18 | call to source | test.cpp:1327:10:1327:10 | i |
| test.cpp:1329:11:1329:16 | call to source | test.cpp:1330:10:1330:10 | i |
| true_upon_entry.cpp:9:11:9:16 | call to source | true_upon_entry.cpp:13:8:13:8 | x |
| true_upon_entry.cpp:17:11:17:16 | call to source | true_upon_entry.cpp:21:8:21:8 | x |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1324,7 +1324,7 @@ struct nsdmi {

void nsdmi_test() {
nsdmi x;
sink(x.i); // $ MISSING: ir ast
sink(x.i); // $ ir MISSING: ast

nsdmi y(source());
sink(y.i); // $ ir ast
Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,5 @@
astTypeBugs
irTypeBugs
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary param] *0 in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary param] this in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] read: Argument[*0].Element in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] read: Argument[*0].Element[****] in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] read: Argument[*0].Element[***] in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] read: Argument[*0].Element[**] in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] read: Argument[*0].Element[*] in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] to write: Argument[this] in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] to write: Argument[this].Element in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] to write: Argument[this].Element[****] in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] to write: Argument[this].Element[***] in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] to write: Argument[this].Element[**] in iterator |
| ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | ../../../include/iterator.h:21:3:21:10 | [summary] to write: Argument[this].Element[*] in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary param] *0 in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary param] this in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] read: Argument[*0].Element in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] read: Argument[*0].Element[****] in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] read: Argument[*0].Element[***] in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] read: Argument[*0].Element[**] in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] read: Argument[*0].Element[*] in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] to write: Argument[this] in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] to write: Argument[this].Element in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] to write: Argument[this].Element[****] in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] to write: Argument[this].Element[***] in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] to write: Argument[this].Element[**] in iterator |
| ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | ../../../include/iterator.h:22:3:22:10 | [summary] to write: Argument[this].Element[*] in iterator |
| ../../../include/iterator.h:30:18:30:26 | ../../../include/iterator.h:30:18:30:26 | ../../../include/iterator.h:30:18:30:26 | [summary param] this in operator* |
| ../../../include/iterator.h:30:18:30:26 | ../../../include/iterator.h:30:18:30:26 | ../../../include/iterator.h:30:18:30:26 | [summary] read: Argument[this].Element in operator* |
| ../../../include/iterator.h:30:18:30:26 | ../../../include/iterator.h:30:18:30:26 | ../../../include/iterator.h:30:18:30:26 | [summary] read: Argument[this].Element[*] in operator* |
| ../../../include/iterator.h:30:18:30:26 | ../../../include/iterator.h:30:18:30:26 | ../../../include/iterator.h:30:18:30:26 | [summary] to write: ReturnValue[**] in operator* |
| ../../../include/iterator.h:30:18:30:26 | ../../../include/iterator.h:30:18:30:26 | ../../../include/iterator.h:30:18:30:26 | [summary] to write: ReturnValue[*] in operator* |
| ../../../include/iterator.h:31:16:31:25 | ../../../include/iterator.h:31:16:31:25 | ../../../include/iterator.h:31:16:31:25 | [summary param] this in operator-> |
| ../../../include/iterator.h:31:16:31:25 | ../../../include/iterator.h:31:16:31:25 | ../../../include/iterator.h:31:16:31:25 | [summary] read: Argument[this].Element in operator-> |
| ../../../include/iterator.h:31:16:31:25 | ../../../include/iterator.h:31:16:31:25 | ../../../include/iterator.h:31:16:31:25 | [summary] read: Argument[this].Element[*] in operator-> |
| ../../../include/iterator.h:31:16:31:25 | ../../../include/iterator.h:31:16:31:25 | ../../../include/iterator.h:31:16:31:25 | [summary] to write: ReturnValue[**] in operator-> |
| ../../../include/iterator.h:31:16:31:25 | ../../../include/iterator.h:31:16:31:25 | ../../../include/iterator.h:31:16:31:25 | [summary] to write: ReturnValue[*] in operator-> |
| test.cpp:356:7:356:11 | test.cpp:356:7:356:11 | test.cpp:356:7:356:11 | field |
| test.cpp:356:7:356:11 | test.cpp:356:7:356:11 | test.cpp:356:7:356:11 | field |
| test.cpp:356:7:356:11 | test.cpp:356:7:356:11 | test.cpp:356:7:356:11 | field |
| test.cpp:1318:9:1318:9 | test.cpp:1318:9:1318:9 | test.cpp:1318:9:1318:9 | i |
| test.cpp:1318:9:1318:9 | test.cpp:1318:9:1318:9 | test.cpp:1318:9:1318:9 | i |
| test.cpp:1318:9:1318:9 | test.cpp:1318:9:1318:9 | test.cpp:1318:9:1318:9 | i |
incorrectBaseType
| clang.cpp:22:8:22:20 | *& ... | Expected 'Node.getType()' to be int, but it was int * |
| clang.cpp:23:17:23:29 | *& ... | Expected 'Node.getType()' to be int, but it was int * |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ import AstTest

module IrTest {
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes

query predicate irTypeBugs(Location location, Node node) {
exists(int n |
// Flow summary nodes don't have a type since we don't necessarily have
// the source code in the database.
not node instanceof FlowSummaryNode and
n = count(node.getType()) and
location = node.getLocation() and
n != 1
Expand Down
2 changes: 1 addition & 1 deletion cpp/ql/test/library-tests/dataflow/fields/C.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class C
void func()
{
sink(s1); // $ ast,ir
sink(s2); // $ MISSING: ast,ir
sink(s2); // $ ir MISSING: ast
sink(s3); // $ ast,ir
sink(s4); // $ MISSING: ast,ir
}
Expand Down
Loading
Loading