diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index d8bcc2d2a76..003bf7d4dff 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -52,12 +52,29 @@ private newtype TIRDataFlowNode = TFinalParameterNode(Parameter p, int indirectionIndex) { exists(Ssa::FinalParameterUse use | use.getParameter() = p and - use.getIndirectionIndex() = indirectionIndex + use.getIndirectionIndex() = indirectionIndex and + parameterIsDefined(p) ) } or TFinalGlobalValue(Ssa::GlobalUse globalUse) or TInitialGlobalValue(Ssa::GlobalDef globalUse) +/** + * Holds if the value of `*p` (or `**p`, `***p`, etc.) is redefined somewhere in the body + * of the enclosing function of `p`. + * + * Only parameters satisfying this predicate will generate a `FinalParameterNode` transferring + * flow out of the function. + */ +private predicate parameterIsDefined(Parameter p) { + exists(Ssa::Def def | + def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst() = p and + def.getIndirectionIndex() = 0 and + def.getIndirection() > 1 and + not def.getValue().asInstruction() instanceof InitializeParameterInstruction + ) +} + /** * An operand that is defined by a `FieldAddressInstruction`. */ diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll index d254d272960..6121bbbc50a 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll @@ -918,6 +918,8 @@ class Def extends DefOrUse { Instruction getAddress() { result = this.getAddressOperand().getDef() } /** + * Gets the indirection index of this definition. + * * This predicate ensures that joins go from `defOrUse` to the result * instead of the other way around. */ @@ -926,6 +928,19 @@ class Def extends DefOrUse { pragma[only_bind_into](result) = pragma[only_bind_out](defOrUse).getIndirectionIndex() } + /** + * Gets the indirection level that this definition is writing to. + * For instance, `x = y` is a definition of `x` at indirection level 1 and + * `*x = y` is a definition of `x` at indirection level 2. + * + * This predicate ensures that joins go from `defOrUse` to the result + * instead of the other way around. + */ + pragma[inline] + int getIndirection() { + pragma[only_bind_into](result) = pragma[only_bind_out](defOrUse).getIndirection() + } + Node0Impl getValue() { result = defOrUse.getValue() } predicate isCertain() { defOrUse.isCertain() } diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp index bbd3ab38868..5b17984fc79 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp @@ -579,7 +579,7 @@ namespace IndirectFlowThroughGlobals { } } -void write_to_param(int* x) { // $ ast-def=x ir-def=*x +void write_to_param(int* x) { // $ ast-def=x int s = source(); x = &s; } @@ -587,5 +587,5 @@ void write_to_param(int* x) { // $ ast-def=x ir-def=*x void test_write_to_param() { int x = 0; write_to_param(&x); - sink(x); // $ SPURIOUS: ast,ir + sink(x); // $ SPURIOUS: ast } \ No newline at end of file