diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index df89c6fac964..db8f0bd02139 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -1336,6 +1336,8 @@ predicate nodeIsHidden(Node n) { n instanceof FinalGlobalValue or n instanceof InitialGlobalValue + or + n instanceof SsaPhiInputNode } predicate neverSkipInPathGraph(Node n) { @@ -1634,6 +1636,8 @@ private Instruction getAnInstruction(Node n) { or result = n.(SsaPhiNode).getPhiNode().getBasicBlock().getFirstInstruction() or + result = n.(SsaPhiInputNode).getBasicBlock().getFirstInstruction() + or n.(IndirectInstruction).hasInstructionAndIndirectionIndex(result, _) or not n instanceof IndirectInstruction and @@ -1763,7 +1767,7 @@ module IteratorFlow { crementCall = def.getValue().asInstruction().(StoreInstruction).getSourceValue() and sv = def.getSourceVariable() and bb.getInstruction(i) = crementCall and - Ssa::ssaDefReachesRead(sv, result.asDef(), bb, i) + Ssa::ssaDefReachesReadExt(sv, result.asDef(), bb, i) ) } @@ -1797,7 +1801,7 @@ module IteratorFlow { isIteratorWrite(writeToDeref, address) and operandForFullyConvertedCall(address, starCall) and bbStar.getInstruction(iStar) = starCall and - Ssa::ssaDefReachesRead(_, def.asDef(), bbStar, iStar) and + Ssa::ssaDefReachesReadExt(_, def.asDef(), bbStar, iStar) and ultimate = getAnUltimateDefinition*(def) and beginStore = ultimate.getValue().asInstruction() and operandForFullyConvertedCall(beginStore.getSourceValueOperand(), beginCall) 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 dc591dccbb98..7fa03c19345a 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 @@ -45,6 +45,7 @@ private newtype TIRDataFlowNode = or Ssa::isModifiableByCall(operand, indirectionIndex) } or + TSsaPhiInputNode(Ssa::PhiNode phi, IRBlock input) { phi.hasInputFromBlock(_, _, _, _, input) } or TSsaPhiNode(Ssa::PhiNode phi) or TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or TRawIndirectOperand0(Node0Impl node, int indirectionIndex) { @@ -158,6 +159,12 @@ class Node extends TIRDataFlowNode { /** Gets the operands corresponding to this node, if any. */ Operand asOperand() { result = this.(OperandNode).getOperand() } + /** + * Gets the operand that is indirectly tracked by this node behind `index` + * number of indirections. + */ + Operand asIndirectOperand(int index) { hasOperandAndIndex(this, result, index) } + /** * Holds if this node is at index `i` in basic block `block`. * @@ -170,6 +177,9 @@ class Node extends TIRDataFlowNode { or this.(SsaPhiNode).getPhiNode().getBasicBlock() = block and i = -1 or + this.(SsaPhiInputNode).getBlock() = block and + i = block.getInstructionCount() + or this.(RawIndirectOperand).getOperand().getUse() = block.getInstruction(i) or this.(RawIndirectInstruction).getInstruction() = block.getInstruction(i) @@ -622,7 +632,7 @@ class SsaPhiNode extends Node, TSsaPhiNode { final override Location getLocationImpl() { result = phi.getBasicBlock().getLocation() } - override string toStringImpl() { result = "Phi" } + override string toStringImpl() { result = phi.toString() } /** * Gets a node that is used as input to this phi node. @@ -631,7 +641,7 @@ class SsaPhiNode extends Node, TSsaPhiNode { */ cached final Node getAnInput(boolean fromBackEdge) { - localFlowStep(result, this) and + result.(SsaPhiInputNode).getPhiNode() = phi and exists(IRBlock bPhi, IRBlock bResult | bPhi = phi.getBasicBlock() and bResult = result.getBasicBlock() | @@ -654,6 +664,58 @@ class SsaPhiNode extends Node, TSsaPhiNode { predicate isPhiRead() { phi.isPhiRead() } } +/** + * INTERNAL: Do not use. + * + * A node that is used as an input to a phi node. + * + * This class exists to allow more powerful barrier guards. Consider this + * example: + * + * ```cpp + * int x = source(); + * if(!safe(x)) { + * x = clear(); + * } + * // phi node for x here + * sink(x); + * ``` + * + * At the phi node for `x` it is neither the case that `x` is dominated by + * `safe(x)`, or is the case that the phi is dominated by a clearing of `x`. + * + * By inserting a "phi input" node as the last entry in the basic block that + * defines the inputs to the phi we can conclude that each of those inputs are + * safe to pass to `sink`. + */ +class SsaPhiInputNode extends Node, TSsaPhiInputNode { + Ssa::PhiNode phi; + IRBlock block; + + SsaPhiInputNode() { this = TSsaPhiInputNode(phi, block) } + + /** Gets the phi node associated with this node. */ + Ssa::PhiNode getPhiNode() { result = phi } + + /** Gets the basic block in which this input originates. */ + IRBlock getBlock() { result = block } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override Declaration getFunction() { result = phi.getBasicBlock().getEnclosingFunction() } + + override DataFlowType getType() { result = this.getSourceVariable().getType() } + + override predicate isGLValue() { phi.getSourceVariable().isGLValue() } + + final override Location getLocationImpl() { result = block.getLastInstruction().getLocation() } + + override string toStringImpl() { result = "Phi input" } + + /** Gets the source variable underlying this phi node. */ + Ssa::SourceVariable getSourceVariable() { result = phi.getSourceVariable() } +} + /** * INTERNAL: do not use. * @@ -2176,6 +2238,9 @@ private module Cached { // Def-use/Use-use flow Ssa::ssaFlow(nodeFrom, nodeTo) or + // Phi input -> Phi + nodeFrom.(SsaPhiInputNode).getPhiNode() = nodeTo.(SsaPhiNode).getPhiNode() + or IteratorFlow::localFlowStep(nodeFrom, nodeTo) or // Operand -> Instruction flow @@ -2614,6 +2679,22 @@ class ContentSet instanceof Content { } } +pragma[nomagic] +private predicate guardControlsPhiInput( + IRGuardCondition g, boolean branch, Ssa::Definition def, IRBlock input, Ssa::PhiNode phi +) { + phi.hasInputFromBlock(def, _, _, _, input) and + ( + g.controls(input, branch) + or + exists(EdgeKind kind | + g.getBlock() = input and + kind = getConditionalEdge(branch) and + input.getSuccessor(kind) = phi.getBasicBlock() + ) + ) +} + /** * Holds if the guard `g` validates the expression `e` upon evaluating to `branch`. * @@ -2662,13 +2743,21 @@ module BarrierGuard { * * NOTE: If an indirect expression is tracked, use `getAnIndirectBarrierNode` instead. */ - ExprNode getABarrierNode() { + Node getABarrierNode() { exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge | e = value.getAnInstruction().getConvertedResultExpression() and - result.getConvertedExpr() = e and + result.asConvertedExpr() = e and guardChecks(g, value.getAnInstruction().getConvertedResultExpression(), edge) and g.controls(result.getBasicBlock(), edge) ) + or + exists( + IRGuardCondition g, boolean branch, Ssa::DefinitionExt def, IRBlock input, Ssa::PhiNode phi + | + guardChecks(g, def.getARead().asOperand().getDef().getConvertedResultExpression(), branch) and + guardControlsPhiInput(g, branch, def, input, phi) and + result = TSsaPhiInputNode(phi, input) + ) } /** @@ -2704,7 +2793,7 @@ module BarrierGuard { * * NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead. */ - IndirectExprNode getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) } + Node getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) } /** * Gets an indirect expression node with indirection index `indirectionIndex` that is @@ -2740,13 +2829,23 @@ module BarrierGuard { * * NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead. */ - IndirectExprNode getAnIndirectBarrierNode(int indirectionIndex) { + Node getAnIndirectBarrierNode(int indirectionIndex) { exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge | e = value.getAnInstruction().getConvertedResultExpression() and - result.getConvertedExpr(indirectionIndex) = e and + result.asIndirectConvertedExpr(indirectionIndex) = e and guardChecks(g, value.getAnInstruction().getConvertedResultExpression(), edge) and g.controls(result.getBasicBlock(), edge) ) + or + exists( + IRGuardCondition g, boolean branch, Ssa::DefinitionExt def, IRBlock input, Ssa::PhiNode phi + | + guardChecks(g, + def.getARead().asIndirectOperand(indirectionIndex).getDef().getConvertedResultExpression(), + branch) and + guardControlsPhiInput(g, branch, def, input, phi) and + result = TSsaPhiInputNode(phi, input) + ) } } @@ -2755,6 +2854,14 @@ module BarrierGuard { */ signature predicate instructionGuardChecksSig(IRGuardCondition g, Instruction instr, boolean branch); +private EdgeKind getConditionalEdge(boolean branch) { + branch = true and + result instanceof TrueEdge + or + branch = false and + result instanceof FalseEdge +} + /** * Provides a set of barrier nodes for a guard that validates an instruction. * @@ -2763,12 +2870,20 @@ signature predicate instructionGuardChecksSig(IRGuardCondition g, Instruction in */ module InstructionBarrierGuard { /** Gets a node that is safely guarded by the given guard check. */ - ExprNode getABarrierNode() { + Node getABarrierNode() { exists(IRGuardCondition g, ValueNumber value, boolean edge, Operand use | instructionGuardChecks(g, value.getAnInstruction(), edge) and use = value.getAnInstruction().getAUse() and result.asOperand() = use and - g.controls(use.getDef().getBlock(), edge) + g.controls(result.getBasicBlock(), edge) + ) + or + exists( + IRGuardCondition g, boolean branch, Ssa::DefinitionExt def, IRBlock input, Ssa::PhiNode phi + | + instructionGuardChecks(g, def.getARead().asOperand().getDef(), branch) and + guardControlsPhiInput(g, branch, def, input, phi) and + result = TSsaPhiInputNode(phi, input) ) } } 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 30511ba12854..203437a91861 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 @@ -657,19 +657,9 @@ class GlobalDefImpl extends DefImpl, TGlobalDefImpl { */ predicate adjacentDefRead(IRBlock bb1, int i1, SourceVariable sv, IRBlock bb2, int i2) { adjacentDefReadExt(_, sv, bb1, i1, bb2, i2) - or - exists(PhiNode phi | - lastRefRedefExt(_, sv, bb1, i1, phi) and - phi.definesAt(sv, bb2, i2, _) - ) } predicate useToNode(IRBlock bb, int i, SourceVariable sv, Node nodeTo) { - exists(Phi phi | - phi.asPhi().definesAt(sv, bb, i, _) and - nodeTo = phi.getNode() - ) - or exists(UseImpl use | use.hasIndexInBlock(bb, i, sv) and nodeTo = use.getNode() @@ -723,46 +713,26 @@ predicate nodeToDefOrUse(Node node, SourceVariable sv, IRBlock bb, int i, boolea */ private predicate indirectConversionFlowStep(Node nFrom, Node nTo) { not exists(SourceVariable sv, IRBlock bb2, int i2 | - nodeToDefOrUse(nTo, sv, bb2, i2, _) and + useToNode(bb2, i2, sv, nTo) and adjacentDefRead(bb2, i2, sv, _, _) ) and - ( - exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr | - hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and - hasOperandAndIndex(nTo, op2, pragma[only_bind_into](indirectionIndex)) and - instr = op2.getDef() and - conversionFlow(op1, instr, _, _) - ) - or - exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr | - hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and - hasOperandAndIndex(nTo, op2, indirectionIndex - 1) and - instr = op2.getDef() and - isDereference(instr, op1, _) - ) + exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr | + hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and + hasOperandAndIndex(nTo, op2, pragma[only_bind_into](indirectionIndex)) and + instr = op2.getDef() and + conversionFlow(op1, instr, _, _) ) } /** - * The reason for this predicate is a bit annoying: - * We cannot mark a `PointerArithmeticInstruction` that computes an offset based on some SSA - * variable `x` as a use of `x` since this creates taint-flow in the following example: - * ```c - * int x = array[source] - * sink(*array) - * ``` - * This is because `source` would flow from the operand of `PointerArithmeticInstruction` to the - * result of the instruction, and into the `IndirectOperand` that represents the value of `*array`. - * Then, via use-use flow, flow will arrive at `*array` in `sink(*array)`. - * - * So this predicate recurses back along conversions and `PointerArithmeticInstruction`s to find the - * first use that has provides use-use flow, and uses that target as the target of the `nodeFrom`. + * Holds if `node` is a phi input node that should receive flow from the + * definition to (or use of) `sv` at `(bb1, i1)`. */ -private predicate adjustForPointerArith(PostUpdateNode pun, SourceVariable sv, IRBlock bb2, int i2) { - exists(IRBlock bb1, int i1, Node adjusted | - indirectConversionFlowStep*(adjusted, pun.getPreUpdateNode()) and - nodeToDefOrUse(adjusted, sv, bb1, i1, _) and - adjacentDefRead(bb1, i1, sv, bb2, i2) +private predicate phiToNode(SsaPhiInputNode node, SourceVariable sv, IRBlock bb1, int i1) { + exists(PhiNode phi, IRBlock input | + phi.hasInputFromBlock(_, sv, bb1, i1, input) and + node.getPhiNode() = phi and + node.getBlock() = input ) } @@ -777,10 +747,14 @@ private predicate adjustForPointerArith(PostUpdateNode pun, SourceVariable sv, I private predicate ssaFlowImpl( IRBlock bb1, int i1, SourceVariable sv, Node nodeFrom, Node nodeTo, boolean uncertain ) { - exists(IRBlock bb2, int i2 | - nodeToDefOrUse(nodeFrom, sv, bb1, i1, uncertain) and - adjacentDefRead(bb1, i1, sv, bb2, i2) and - useToNode(bb2, i2, sv, nodeTo) + nodeToDefOrUse(nodeFrom, sv, bb1, i1, uncertain) and + ( + exists(IRBlock bb2, int i2 | + adjacentDefRead(bb1, i1, sv, bb2, i2) and + useToNode(bb2, i2, sv, nodeTo) + ) + or + phiToNode(nodeTo, sv, bb1, i1) ) and nodeFrom != nodeTo } @@ -789,7 +763,7 @@ private predicate ssaFlowImpl( private Node getAPriorDefinition(DefinitionExt next) { exists(IRBlock bb, int i, SourceVariable sv | lastRefRedefExt(_, pragma[only_bind_into](sv), pragma[only_bind_into](bb), - pragma[only_bind_into](i), next) and + pragma[only_bind_into](i), _, next) and nodeToDefOrUse(result, sv, bb, i, _) ) } @@ -896,9 +870,31 @@ private predicate isArgumentOfCallable(DataFlowCall call, Node n) { * Holds if there is use-use flow from `pun`'s pre-update node to `n`. */ private predicate postUpdateNodeToFirstUse(PostUpdateNode pun, Node n) { - exists(SourceVariable sv, IRBlock bb2, int i2 | - adjustForPointerArith(pun, sv, bb2, i2) and - useToNode(bb2, i2, sv, n) + // We cannot mark a `PointerArithmeticInstruction` that computes an offset + // based on some SSA + // variable `x` as a use of `x` since this creates taint-flow in the + // following example: + // ```c + // int x = array[source] + // sink(*array) + // ``` + // This is because `source` would flow from the operand of `PointerArithmetic` + // instruction to the result of the instruction, and into the `IndirectOperand` + // that represents the value of `*array`. Then, via use-use flow, flow will + // arrive at `*array` in `sink(*array)`. + // So this predicate recurses back along conversions and `PointerArithmetic` + // instructions to find the first use that has provides use-use flow, and + // uses that target as the target of the `nodeFrom`. + exists(Node adjusted, IRBlock bb1, int i1, SourceVariable sv | + indirectConversionFlowStep*(adjusted, pun.getPreUpdateNode()) and + useToNode(bb1, i1, sv, adjusted) + | + exists(IRBlock bb2, int i2 | + adjacentDefRead(bb1, i1, sv, bb2, i2) and + useToNode(bb2, i2, sv, n) + ) + or + phiToNode(n, sv, bb1, i1) ) } @@ -953,11 +949,16 @@ predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) { /** Holds if `nodeTo` receives flow from the phi node `nodeFrom`. */ predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) { - exists(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1, IRBlock bb2, int i2 | + exists(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1 | phi = nodeFrom.getPhiNode() and - phi.definesAt(sv, bb1, i1, _) and - adjacentDefRead(bb1, i1, sv, bb2, i2) and - useToNode(bb2, i2, sv, nodeTo) + phi.definesAt(sv, bb1, i1, _) + | + exists(IRBlock bb2, int i2 | + adjacentDefRead(bb1, i1, sv, bb2, i2) and + useToNode(bb2, i2, sv, nodeTo) + ) + or + phiToNode(nodeTo, sv, bb1, i1) ) } @@ -1031,22 +1032,26 @@ module SsaCached { * Holds if the node at index `i` in `bb` is a last reference to SSA definition * `def`. The reference is last because it can reach another write `next`, * without passing through another read or write. + * + * The path from node `i` in `bb` to `next` goes via basic block `input`, + * which is either a predecessor of the basic block of `next`, or `input` = + * `bb` in case `next` occurs in basic block `bb`. */ cached predicate lastRefRedefExt( - DefinitionExt def, SourceVariable sv, IRBlock bb, int i, DefinitionExt next + DefinitionExt def, SourceVariable sv, IRBlock bb, int i, IRBlock input, DefinitionExt next ) { - SsaImpl::lastRefRedefExt(def, sv, bb, i, next) + SsaImpl::lastRefRedefExt(def, sv, bb, i, input, next) } cached - Definition phiHasInputFromBlock(PhiNode phi, IRBlock bb) { - SsaImpl::phiHasInputFromBlock(phi, result, bb) + Definition phiHasInputFromBlockExt(PhiNode phi, IRBlock bb) { + SsaImpl::phiHasInputFromBlockExt(phi, result, bb) } cached - predicate ssaDefReachesRead(SourceVariable v, Definition def, IRBlock bb, int i) { - SsaImpl::ssaDefReachesRead(v, def, bb, i) + predicate ssaDefReachesReadExt(SourceVariable v, DefinitionExt def, IRBlock bb, int i) { + SsaImpl::ssaDefReachesReadExt(v, def, bb, i) } predicate variableRead = SsaInput::variableRead/4; @@ -1198,11 +1203,11 @@ class Phi extends TPhi, SsaDef { final override Location getLocation() { result = phi.getBasicBlock().getLocation() } - override string toString() { result = "Phi" } + override string toString() { result = phi.toString() } - SsaPhiNode getNode() { result.getPhiNode() = phi } + SsaPhiInputNode getNode(IRBlock block) { result.getPhiNode() = phi and result.getBlock() = block } - predicate hasInputFromBlock(Definition inp, IRBlock bb) { inp = phiHasInputFromBlock(phi, bb) } + predicate hasInputFromBlock(Definition inp, IRBlock bb) { inp = phiHasInputFromBlockExt(phi, bb) } final Definition getAnInput() { this.hasInputFromBlock(result, _) } } @@ -1228,13 +1233,21 @@ class PhiNode extends SsaImpl::DefinitionExt { */ predicate isPhiRead() { this instanceof SsaImpl::PhiReadNode } - /** Holds if `inp` is an input to this phi node along the edge originating in `bb`. */ - predicate hasInputFromBlock(Definition inp, IRBlock bb) { - inp = SsaCached::phiHasInputFromBlock(this, bb) + /** + * Holds if the node at index `i` in `bb` is a last reference to SSA + * definition `def` of `sv`. The reference is last because it can reach + * this phi node, without passing through another read or write. + * + * The path from node `i` in `bb` to this phi node goes via basic block + * `input`, which is either a predecessor of the basic block of this phi + * node, or `input` = `bb` in case this phi node occurs in basic block `bb`. + */ + predicate hasInputFromBlock(DefinitionExt def, SourceVariable sv, IRBlock bb, int i, IRBlock input) { + SsaCached::lastRefRedefExt(def, sv, bb, i, input, this) } /** Gets a definition that is an input to this phi node. */ - final Definition getAnInput() { this.hasInputFromBlock(result, _) } + final Definition getAnInput() { this.hasInputFromBlock(result, _, _, _, _) } } /** An static single assignment (SSA) definition. */ @@ -1249,6 +1262,15 @@ class DefinitionExt extends SsaImpl::DefinitionExt { result = this.getAPhiInputOrPriorDefinition*() and not result instanceof PhiNode } + + /** Gets a node that represents a read of this SSA definition. */ + Node getARead() { + exists(SourceVariable sv, IRBlock bb, int i | SsaCached::ssaDefReachesReadExt(sv, this, bb, i) | + useToNode(bb, i, sv, result) + or + phiToNode(result, sv, bb, i) + ) + } } class Definition = SsaImpl::Definition; diff --git a/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql b/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql index 267d0b9bd884..587a2ecc6ffb 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.ql @@ -209,6 +209,7 @@ class LoopWithAlloca extends Stmt { DataFlow::localFlow(result, DataFlow::exprNode(va)) and // Phi nodes will be preceded by nodes that represent actual definitions not result instanceof DataFlow::SsaPhiNode and + not result instanceof DataFlow::SsaPhiInputNode and // A source is outside the loop if it's not inside the loop not exists(Expr e | e = getExpr(result) | this = getAnEnclosingLoopOfExpr(e)) ) diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp index 0e9c9f1bc778..6a150af4e7b8 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp @@ -75,4 +75,54 @@ void bg_indirect_expr() { if (guarded(buf)) { sink(buf); } +} + +void test_guard_and_reassign() { + int x = source(); + + if(!guarded(x)) { + x = 0; + } + sink(x); // $ SPURIOUS: ast,ir +} + +void test_phi_read_guard(bool b) { + int x = source(); + + if(b) { + if(!guarded(x)) + return; + } + else { + if(!guarded(x)) + return; + } + + sink(x); // $ SPURIOUS: ast,ir +} + +bool unsafe(int); + +void test_guard_and_reassign_2() { + int x = source(); + + if(unsafe(x)) { + x = 0; + } + sink(x); // $ SPURIOUS: ast +} + +void test_phi_read_guard_2(bool b) { + int x = source(); + + if(b) { + if(unsafe(x)) + return; + } + else { + if(unsafe(x)) + return; + } + + sink(x); // $ SPURIOUS: ast } \ No newline at end of file diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/TestBase.qll b/cpp/ql/test/library-tests/dataflow/dataflow-tests/TestBase.qll index c91bd782c056..71db02642878 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/TestBase.qll +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/TestBase.qll @@ -11,6 +11,10 @@ module AstTest { g.(FunctionCall).getTarget().getName() = "guarded" and checked = g.(FunctionCall).getArgument(0) and isTrue = true + or + g.(FunctionCall).getTarget().getName() = "unsafe" and + checked = g.(FunctionCall).getArgument(0) and + isTrue = false } /** Common data flow configuration to be used by tests. */ @@ -105,9 +109,13 @@ module IRTest { predicate testBarrierGuard(IRGuardCondition g, Expr checked, boolean isTrue) { exists(Call call | call = g.getUnconvertedResultExpression() and + checked = call.getArgument(0) + | call.getTarget().hasName("guarded") and - checked = call.getArgument(0) and isTrue = true + or + call.getTarget().hasName("unsafe") and + isTrue = false ) } diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow-ir.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow-ir.expected index 00e6b03b9319..f6c8375660c1 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow-ir.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/localFlow-ir.expected @@ -69,45 +69,61 @@ | test.cpp:8:8:8:9 | t1 | test.cpp:9:8:9:9 | t1 | | test.cpp:9:8:9:9 | t1 | test.cpp:11:7:11:8 | t1 | | test.cpp:9:8:9:9 | t1 | test.cpp:11:7:11:8 | t1 | +| test.cpp:10:8:10:9 | t2 | test.cpp:11:7:11:8 | Phi input | +| test.cpp:10:8:10:9 | t2 | test.cpp:11:7:11:8 | Phi input | | test.cpp:10:8:10:9 | t2 | test.cpp:13:10:13:11 | t2 | -| test.cpp:10:8:10:9 | t2 | test.cpp:15:3:15:6 | Phi | -| test.cpp:10:8:10:9 | t2 | test.cpp:15:3:15:6 | Phi | +| test.cpp:11:7:11:8 | Phi input | test.cpp:15:3:15:6 | SSA phi read(t2) | +| test.cpp:11:7:11:8 | Phi input | test.cpp:15:3:15:6 | SSA phi(*t2) | | test.cpp:11:7:11:8 | t1 | test.cpp:21:8:21:9 | t1 | | test.cpp:12:5:12:10 | ... = ... | test.cpp:13:10:13:11 | t2 | | test.cpp:12:10:12:10 | 0 | test.cpp:12:5:12:10 | ... = ... | -| test.cpp:13:10:13:11 | t2 | test.cpp:15:3:15:6 | Phi | -| test.cpp:13:10:13:11 | t2 | test.cpp:15:3:15:6 | Phi | -| test.cpp:15:3:15:6 | Phi | test.cpp:15:8:15:9 | t2 | -| test.cpp:15:3:15:6 | Phi | test.cpp:15:8:15:9 | t2 | -| test.cpp:15:8:15:9 | t2 | test.cpp:23:19:23:19 | Phi | -| test.cpp:15:8:15:9 | t2 | test.cpp:23:19:23:19 | Phi | +| test.cpp:13:5:13:8 | Phi input | test.cpp:15:3:15:6 | SSA phi read(t2) | +| test.cpp:13:5:13:8 | Phi input | test.cpp:15:3:15:6 | SSA phi(*t2) | +| test.cpp:13:10:13:11 | t2 | test.cpp:13:5:13:8 | Phi input | +| test.cpp:13:10:13:11 | t2 | test.cpp:13:5:13:8 | Phi input | +| test.cpp:15:3:15:6 | SSA phi read(t2) | test.cpp:15:8:15:9 | t2 | +| test.cpp:15:3:15:6 | SSA phi(*t2) | test.cpp:15:8:15:9 | t2 | +| test.cpp:15:8:15:9 | t2 | test.cpp:23:15:23:16 | Phi input | +| test.cpp:15:8:15:9 | t2 | test.cpp:23:15:23:16 | Phi input | | test.cpp:17:3:17:8 | ... = ... | test.cpp:21:8:21:9 | t1 | | test.cpp:17:8:17:8 | 0 | test.cpp:17:3:17:8 | ... = ... | -| test.cpp:21:8:21:9 | t1 | test.cpp:23:19:23:19 | Phi | -| test.cpp:21:8:21:9 | t1 | test.cpp:23:19:23:19 | Phi | +| test.cpp:21:8:21:9 | t1 | test.cpp:23:15:23:16 | Phi input | +| test.cpp:21:8:21:9 | t1 | test.cpp:23:15:23:16 | Phi input | | test.cpp:23:15:23:16 | 0 | test.cpp:23:15:23:16 | 0 | -| test.cpp:23:15:23:16 | 0 | test.cpp:23:19:23:19 | Phi | -| test.cpp:23:19:23:19 | Phi | test.cpp:23:19:23:19 | i | -| test.cpp:23:19:23:19 | Phi | test.cpp:23:19:23:19 | i | -| test.cpp:23:19:23:19 | Phi | test.cpp:23:23:23:24 | t1 | -| test.cpp:23:19:23:19 | Phi | test.cpp:23:23:23:24 | t1 | -| test.cpp:23:19:23:19 | Phi | test.cpp:24:10:24:11 | t2 | -| test.cpp:23:19:23:19 | Phi | test.cpp:24:10:24:11 | t2 | +| test.cpp:23:15:23:16 | 0 | test.cpp:23:15:23:16 | Phi input | +| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi read(*t2) | +| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi read(i) | +| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi read(t1) | +| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi read(t2) | +| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi(*i) | +| test.cpp:23:15:23:16 | Phi input | test.cpp:23:19:23:19 | SSA phi(*t1) | +| test.cpp:23:19:23:19 | SSA phi read(*t2) | test.cpp:24:10:24:11 | t2 | +| test.cpp:23:19:23:19 | SSA phi read(i) | test.cpp:23:19:23:19 | i | +| test.cpp:23:19:23:19 | SSA phi read(t1) | test.cpp:23:23:23:24 | t1 | +| test.cpp:23:19:23:19 | SSA phi read(t2) | test.cpp:24:10:24:11 | t2 | +| test.cpp:23:19:23:19 | SSA phi(*i) | test.cpp:23:19:23:19 | i | +| test.cpp:23:19:23:19 | SSA phi(*t1) | test.cpp:23:23:23:24 | t1 | | test.cpp:23:19:23:19 | i | test.cpp:23:27:23:27 | i | | test.cpp:23:19:23:19 | i | test.cpp:23:27:23:27 | i | -| test.cpp:23:23:23:24 | t1 | test.cpp:23:19:23:19 | Phi | +| test.cpp:23:23:23:24 | t1 | test.cpp:23:27:23:29 | Phi input | | test.cpp:23:23:23:24 | t1 | test.cpp:26:8:26:9 | t1 | | test.cpp:23:23:23:24 | t1 | test.cpp:26:8:26:9 | t1 | | test.cpp:23:27:23:27 | *i | test.cpp:23:27:23:27 | *i | | test.cpp:23:27:23:27 | *i | test.cpp:23:27:23:27 | i | -| test.cpp:23:27:23:27 | i | test.cpp:23:19:23:19 | Phi | | test.cpp:23:27:23:27 | i | test.cpp:23:27:23:27 | i | | test.cpp:23:27:23:27 | i | test.cpp:23:27:23:27 | i | -| test.cpp:23:27:23:29 | ... ++ | test.cpp:23:19:23:19 | Phi | +| test.cpp:23:27:23:27 | i | test.cpp:23:27:23:29 | Phi input | | test.cpp:23:27:23:29 | ... ++ | test.cpp:23:27:23:29 | ... ++ | -| test.cpp:24:5:24:11 | ... = ... | test.cpp:23:19:23:19 | Phi | -| test.cpp:24:10:24:11 | t2 | test.cpp:23:19:23:19 | Phi | -| test.cpp:24:10:24:11 | t2 | test.cpp:23:19:23:19 | Phi | +| test.cpp:23:27:23:29 | ... ++ | test.cpp:23:27:23:29 | Phi input | +| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi read(*t2) | +| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi read(i) | +| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi read(t1) | +| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi read(t2) | +| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi(*i) | +| test.cpp:23:27:23:29 | Phi input | test.cpp:23:19:23:19 | SSA phi(*t1) | +| test.cpp:24:5:24:11 | ... = ... | test.cpp:23:27:23:29 | Phi input | +| test.cpp:24:10:24:11 | t2 | test.cpp:23:27:23:29 | Phi input | +| test.cpp:24:10:24:11 | t2 | test.cpp:23:27:23:29 | Phi input | | test.cpp:24:10:24:11 | t2 | test.cpp:24:5:24:11 | ... = ... | | test.cpp:382:48:382:54 | source1 | test.cpp:384:16:384:23 | *& ... | | test.cpp:383:12:383:13 | 0 | test.cpp:383:12:383:13 | 0 | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test-source-sink.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test-source-sink.expected index e8afa785492f..85d8682742b1 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test-source-sink.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test-source-sink.expected @@ -12,6 +12,10 @@ astFlow | BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:62:14:62:14 | x | | BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:64:14:64:14 | x | | BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:66:14:66:14 | x | +| BarrierGuard.cpp:81:11:81:16 | call to source | BarrierGuard.cpp:86:8:86:8 | x | +| BarrierGuard.cpp:90:11:90:16 | call to source | BarrierGuard.cpp:101:8:101:8 | x | +| BarrierGuard.cpp:107:11:107:16 | call to source | BarrierGuard.cpp:112:8:112:8 | x | +| BarrierGuard.cpp:116:11:116:16 | call to source | BarrierGuard.cpp:127:8:127:8 | x | | acrossLinkTargets.cpp:19:27:19:32 | call to source | acrossLinkTargets.cpp:12:8:12:8 | x | | clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:18:8:18:19 | sourceArray1 | | clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:22:8:22:20 | & ... | @@ -141,6 +145,8 @@ irFlow | BarrierGuard.cpp:49:10:49:15 | call to source | BarrierGuard.cpp:55:13:55:13 | x | | BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:64:14:64:14 | x | | BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:66:14:66:14 | x | +| BarrierGuard.cpp:81:11:81:16 | call to source | BarrierGuard.cpp:86:8:86:8 | x | +| BarrierGuard.cpp:90:11:90:16 | call to source | BarrierGuard.cpp:101:8:101:8 | x | | acrossLinkTargets.cpp:19:27:19:32 | call to source | acrossLinkTargets.cpp:12:8:12:8 | x | | clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:18:8:18:19 | sourceArray1 | | clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:23:17:23:29 | *& ... | diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected index f49830443ca4..0af54dc85709 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected @@ -184,6 +184,7 @@ postWithInFlow | simple.cpp:65:7:65:7 | i [post update] | PostUpdateNode should not be the target of local flow. | | simple.cpp:83:12:83:13 | f1 [post update] | PostUpdateNode should not be the target of local flow. | | simple.cpp:92:7:92:7 | i [post update] | PostUpdateNode should not be the target of local flow. | +| simple.cpp:118:7:118:7 | i [post update] | PostUpdateNode should not be the target of local flow. | | struct_init.c:24:11:24:12 | ab [inner post update] | PostUpdateNode should not be the target of local flow. | | struct_init.c:36:17:36:24 | nestedAB [inner post update] | PostUpdateNode should not be the target of local flow. | viableImplInCallContextTooLarge diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index 4f9b5b7b853d..98ca0290f472 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -842,6 +842,10 @@ edges | simple.cpp:108:17:108:26 | call to user_input | simple.cpp:108:17:108:26 | call to user_input | provenance | | | simple.cpp:108:17:108:26 | call to user_input | simple.cpp:109:43:109:43 | x | provenance | | | simple.cpp:109:43:109:43 | x | simple.cpp:103:24:103:24 | x | provenance | | +| simple.cpp:118:5:118:5 | *a [post update] [i] | simple.cpp:120:8:120:8 | *a [i] | provenance | | +| simple.cpp:118:5:118:22 | ... = ... | simple.cpp:118:5:118:5 | *a [post update] [i] | provenance | | +| simple.cpp:118:11:118:20 | call to user_input | simple.cpp:118:5:118:22 | ... = ... | provenance | | +| simple.cpp:120:8:120:8 | *a [i] | simple.cpp:120:10:120:10 | i | provenance | | | struct_init.c:14:24:14:25 | *ab [a] | struct_init.c:14:24:14:25 | *ab [a] | provenance | | | struct_init.c:14:24:14:25 | *ab [a] | struct_init.c:15:8:15:9 | *ab [a] | provenance | | | struct_init.c:15:8:15:9 | *ab [a] | struct_init.c:15:12:15:12 | a | provenance | | @@ -1747,6 +1751,11 @@ nodes | simple.cpp:108:17:108:26 | call to user_input | semmle.label | call to user_input | | simple.cpp:108:17:108:26 | call to user_input | semmle.label | call to user_input | | simple.cpp:109:43:109:43 | x | semmle.label | x | +| simple.cpp:118:5:118:5 | *a [post update] [i] | semmle.label | *a [post update] [i] | +| simple.cpp:118:5:118:22 | ... = ... | semmle.label | ... = ... | +| simple.cpp:118:11:118:20 | call to user_input | semmle.label | call to user_input | +| simple.cpp:120:8:120:8 | *a [i] | semmle.label | *a [i] | +| simple.cpp:120:10:120:10 | i | semmle.label | i | | struct_init.c:14:24:14:25 | *ab [a] | semmle.label | *ab [a] | | struct_init.c:14:24:14:25 | *ab [a] | semmle.label | *ab [a] | | struct_init.c:15:8:15:9 | *ab [a] | semmle.label | *ab [a] | @@ -1957,6 +1966,7 @@ subpaths | simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input | | simple.cpp:94:13:94:13 | i | simple.cpp:92:11:92:20 | call to user_input | simple.cpp:94:13:94:13 | i | i flows from $@ | simple.cpp:92:11:92:20 | call to user_input | call to user_input | | simple.cpp:104:14:104:14 | x | simple.cpp:108:17:108:26 | call to user_input | simple.cpp:104:14:104:14 | x | x flows from $@ | simple.cpp:108:17:108:26 | call to user_input | call to user_input | +| simple.cpp:120:10:120:10 | i | simple.cpp:118:11:118:20 | call to user_input | simple.cpp:120:10:120:10 | i | i flows from $@ | simple.cpp:118:11:118:20 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:40:20:40:29 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected index 4c85e26fc79d..4d249e34664b 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected @@ -289,3 +289,5 @@ WARNING: Module DataFlow has been deprecated and may be removed in future (parti | simple.cpp:83:12:83:13 | f1 | AST only | | simple.cpp:92:7:92:7 | i | AST only | | simple.cpp:94:10:94:11 | a2 | IR only | +| simple.cpp:118:7:118:7 | i | AST only | +| simple.cpp:120:8:120:8 | a | IR only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected index 823997fd7d33..fcb70d77d5bf 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected @@ -649,6 +649,8 @@ | simple.cpp:84:14:84:20 | this | | simple.cpp:92:5:92:5 | a | | simple.cpp:94:10:94:11 | a2 | +| simple.cpp:118:5:118:5 | a | +| simple.cpp:120:8:120:8 | a | | struct_init.c:15:8:15:9 | ab | | struct_init.c:15:12:15:12 | a | | struct_init.c:16:8:16:9 | ab | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected index 608f884ddc07..48c0ff8cc9b8 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected @@ -579,6 +579,8 @@ WARNING: Module DataFlow has been deprecated and may be removed in future (parti | simple.cpp:84:14:84:20 | this | | simple.cpp:92:5:92:5 | a | | simple.cpp:92:7:92:7 | i | +| simple.cpp:118:5:118:5 | a | +| simple.cpp:118:7:118:7 | i | | struct_init.c:15:8:15:9 | ab | | struct_init.c:15:12:15:12 | a | | struct_init.c:16:8:16:9 | ab | diff --git a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected index 9def426deb1c..17cd0b7ce020 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected @@ -731,6 +731,10 @@ edges | simple.cpp:92:5:92:22 | ... = ... | simple.cpp:92:5:92:5 | a [post update] [i] | provenance | | | simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | ... = ... | provenance | | | simple.cpp:94:10:94:11 | a2 [i] | simple.cpp:94:13:94:13 | i | provenance | | +| simple.cpp:118:5:118:5 | a [post update] [i] | simple.cpp:120:8:120:8 | a [i] | provenance | | +| simple.cpp:118:5:118:22 | ... = ... | simple.cpp:118:5:118:5 | a [post update] [i] | provenance | | +| simple.cpp:118:11:118:20 | call to user_input | simple.cpp:118:5:118:22 | ... = ... | provenance | | +| simple.cpp:120:8:120:8 | a [i] | simple.cpp:120:10:120:10 | i | provenance | | | struct_init.c:14:24:14:25 | ab [a] | struct_init.c:14:24:14:25 | ab [a] | provenance | | | struct_init.c:14:24:14:25 | ab [a] | struct_init.c:15:8:15:9 | ab [a] | provenance | | | struct_init.c:15:8:15:9 | ab [a] | struct_init.c:15:12:15:12 | a | provenance | | @@ -1538,6 +1542,11 @@ nodes | simple.cpp:92:11:92:20 | call to user_input | semmle.label | call to user_input | | simple.cpp:94:10:94:11 | a2 [i] | semmle.label | a2 [i] | | simple.cpp:94:13:94:13 | i | semmle.label | i | +| simple.cpp:118:5:118:5 | a [post update] [i] | semmle.label | a [post update] [i] | +| simple.cpp:118:5:118:22 | ... = ... | semmle.label | ... = ... | +| simple.cpp:118:11:118:20 | call to user_input | semmle.label | call to user_input | +| simple.cpp:120:8:120:8 | a [i] | semmle.label | a [i] | +| simple.cpp:120:10:120:10 | i | semmle.label | i | | struct_init.c:14:24:14:25 | ab [a] | semmle.label | ab [a] | | struct_init.c:14:24:14:25 | ab [a] | semmle.label | ab [a] | | struct_init.c:15:8:15:9 | ab [a] | semmle.label | ab [a] | @@ -1751,6 +1760,7 @@ subpaths | simple.cpp:67:13:67:13 | i | simple.cpp:65:11:65:20 | call to user_input | simple.cpp:67:13:67:13 | i | i flows from $@ | simple.cpp:65:11:65:20 | call to user_input | call to user_input | | simple.cpp:84:14:84:20 | call to getf2f1 | simple.cpp:83:17:83:26 | call to user_input | simple.cpp:84:14:84:20 | call to getf2f1 | call to getf2f1 flows from $@ | simple.cpp:83:17:83:26 | call to user_input | call to user_input | | simple.cpp:94:13:94:13 | i | simple.cpp:92:11:92:20 | call to user_input | simple.cpp:94:13:94:13 | i | i flows from $@ | simple.cpp:92:11:92:20 | call to user_input | call to user_input | +| simple.cpp:120:10:120:10 | i | simple.cpp:118:11:118:20 | call to user_input | simple.cpp:120:10:120:10 | i | i flows from $@ | simple.cpp:118:11:118:20 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:20:20:20:29 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:27:7:27:16 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:27:7:27:16 | call to user_input | call to user_input | | struct_init.c:15:12:15:12 | a | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:15:12:15:12 | a | a flows from $@ | struct_init.c:40:20:40:29 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp index 36756689855d..9501bdaf63be 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/simple.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/simple.cpp @@ -111,4 +111,13 @@ namespace TestAdditionalCallTargets { } +void post_update_to_phi_input(bool b) +{ + A a; + if(b) { + a.i = user_input(); + } + sink(a.i); // $ ast,ir +} + } // namespace Simple