Skip to content

Commit 9f4c138

Browse files
authored
Merge pull request #16677 from MathiasVP/phi-input-nodes
C++: Extend barrier guards to handle phi inputs
2 parents eae6406 + 8aaa2a1 commit 9f4c138

15 files changed

+361
-103
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,8 @@ predicate nodeIsHidden(Node n) {
13361336
n instanceof FinalGlobalValue
13371337
or
13381338
n instanceof InitialGlobalValue
1339+
or
1340+
n instanceof SsaPhiInputNode
13391341
}
13401342

13411343
predicate neverSkipInPathGraph(Node n) {
@@ -1634,6 +1636,8 @@ private Instruction getAnInstruction(Node n) {
16341636
or
16351637
result = n.(SsaPhiNode).getPhiNode().getBasicBlock().getFirstInstruction()
16361638
or
1639+
result = n.(SsaPhiInputNode).getBasicBlock().getFirstInstruction()
1640+
or
16371641
n.(IndirectInstruction).hasInstructionAndIndirectionIndex(result, _)
16381642
or
16391643
not n instanceof IndirectInstruction and
@@ -1763,7 +1767,7 @@ module IteratorFlow {
17631767
crementCall = def.getValue().asInstruction().(StoreInstruction).getSourceValue() and
17641768
sv = def.getSourceVariable() and
17651769
bb.getInstruction(i) = crementCall and
1766-
Ssa::ssaDefReachesRead(sv, result.asDef(), bb, i)
1770+
Ssa::ssaDefReachesReadExt(sv, result.asDef(), bb, i)
17671771
)
17681772
}
17691773

@@ -1797,7 +1801,7 @@ module IteratorFlow {
17971801
isIteratorWrite(writeToDeref, address) and
17981802
operandForFullyConvertedCall(address, starCall) and
17991803
bbStar.getInstruction(iStar) = starCall and
1800-
Ssa::ssaDefReachesRead(_, def.asDef(), bbStar, iStar) and
1804+
Ssa::ssaDefReachesReadExt(_, def.asDef(), bbStar, iStar) and
18011805
ultimate = getAnUltimateDefinition*(def) and
18021806
beginStore = ultimate.getValue().asInstruction() and
18031807
operandForFullyConvertedCall(beginStore.getSourceValueOperand(), beginCall)

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll

Lines changed: 124 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ private newtype TIRDataFlowNode =
4545
or
4646
Ssa::isModifiableByCall(operand, indirectionIndex)
4747
} or
48+
TSsaPhiInputNode(Ssa::PhiNode phi, IRBlock input) { phi.hasInputFromBlock(_, _, _, _, input) } or
4849
TSsaPhiNode(Ssa::PhiNode phi) or
4950
TSsaIteratorNode(IteratorFlow::IteratorFlowNode n) or
5051
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
@@ -165,6 +166,12 @@ class Node extends TIRDataFlowNode {
165166
/** Gets the operands corresponding to this node, if any. */
166167
Operand asOperand() { result = this.(OperandNode).getOperand() }
167168

169+
/**
170+
* Gets the operand that is indirectly tracked by this node behind `index`
171+
* number of indirections.
172+
*/
173+
Operand asIndirectOperand(int index) { hasOperandAndIndex(this, result, index) }
174+
168175
/**
169176
* Holds if this node is at index `i` in basic block `block`.
170177
*
@@ -177,6 +184,9 @@ class Node extends TIRDataFlowNode {
177184
or
178185
this.(SsaPhiNode).getPhiNode().getBasicBlock() = block and i = -1
179186
or
187+
this.(SsaPhiInputNode).getBlock() = block and
188+
i = block.getInstructionCount()
189+
or
180190
this.(RawIndirectOperand).getOperand().getUse() = block.getInstruction(i)
181191
or
182192
this.(RawIndirectInstruction).getInstruction() = block.getInstruction(i)
@@ -629,7 +639,7 @@ class SsaPhiNode extends Node, TSsaPhiNode {
629639

630640
final override Location getLocationImpl() { result = phi.getBasicBlock().getLocation() }
631641

632-
override string toStringImpl() { result = "Phi" }
642+
override string toStringImpl() { result = phi.toString() }
633643

634644
/**
635645
* Gets a node that is used as input to this phi node.
@@ -638,7 +648,7 @@ class SsaPhiNode extends Node, TSsaPhiNode {
638648
*/
639649
cached
640650
final Node getAnInput(boolean fromBackEdge) {
641-
localFlowStep(result, this) and
651+
result.(SsaPhiInputNode).getPhiNode() = phi and
642652
exists(IRBlock bPhi, IRBlock bResult |
643653
bPhi = phi.getBasicBlock() and bResult = result.getBasicBlock()
644654
|
@@ -661,6 +671,58 @@ class SsaPhiNode extends Node, TSsaPhiNode {
661671
predicate isPhiRead() { phi.isPhiRead() }
662672
}
663673

674+
/**
675+
* INTERNAL: Do not use.
676+
*
677+
* A node that is used as an input to a phi node.
678+
*
679+
* This class exists to allow more powerful barrier guards. Consider this
680+
* example:
681+
*
682+
* ```cpp
683+
* int x = source();
684+
* if(!safe(x)) {
685+
* x = clear();
686+
* }
687+
* // phi node for x here
688+
* sink(x);
689+
* ```
690+
*
691+
* At the phi node for `x` it is neither the case that `x` is dominated by
692+
* `safe(x)`, or is the case that the phi is dominated by a clearing of `x`.
693+
*
694+
* By inserting a "phi input" node as the last entry in the basic block that
695+
* defines the inputs to the phi we can conclude that each of those inputs are
696+
* safe to pass to `sink`.
697+
*/
698+
class SsaPhiInputNode extends Node, TSsaPhiInputNode {
699+
Ssa::PhiNode phi;
700+
IRBlock block;
701+
702+
SsaPhiInputNode() { this = TSsaPhiInputNode(phi, block) }
703+
704+
/** Gets the phi node associated with this node. */
705+
Ssa::PhiNode getPhiNode() { result = phi }
706+
707+
/** Gets the basic block in which this input originates. */
708+
IRBlock getBlock() { result = block }
709+
710+
override Declaration getEnclosingCallable() { result = this.getFunction() }
711+
712+
override Declaration getFunction() { result = phi.getBasicBlock().getEnclosingFunction() }
713+
714+
override DataFlowType getType() { result = this.getSourceVariable().getType() }
715+
716+
override predicate isGLValue() { phi.getSourceVariable().isGLValue() }
717+
718+
final override Location getLocationImpl() { result = block.getLastInstruction().getLocation() }
719+
720+
override string toStringImpl() { result = "Phi input" }
721+
722+
/** Gets the source variable underlying this phi node. */
723+
Ssa::SourceVariable getSourceVariable() { result = phi.getSourceVariable() }
724+
}
725+
664726
/**
665727
* INTERNAL: do not use.
666728
*
@@ -2183,6 +2245,9 @@ private module Cached {
21832245
// Def-use/Use-use flow
21842246
Ssa::ssaFlow(nodeFrom, nodeTo)
21852247
or
2248+
// Phi input -> Phi
2249+
nodeFrom.(SsaPhiInputNode).getPhiNode() = nodeTo.(SsaPhiNode).getPhiNode()
2250+
or
21862251
IteratorFlow::localFlowStep(nodeFrom, nodeTo)
21872252
or
21882253
// Operand -> Instruction flow
@@ -2621,6 +2686,22 @@ class ContentSet instanceof Content {
26212686
}
26222687
}
26232688

2689+
pragma[nomagic]
2690+
private predicate guardControlsPhiInput(
2691+
IRGuardCondition g, boolean branch, Ssa::Definition def, IRBlock input, Ssa::PhiNode phi
2692+
) {
2693+
phi.hasInputFromBlock(def, _, _, _, input) and
2694+
(
2695+
g.controls(input, branch)
2696+
or
2697+
exists(EdgeKind kind |
2698+
g.getBlock() = input and
2699+
kind = getConditionalEdge(branch) and
2700+
input.getSuccessor(kind) = phi.getBasicBlock()
2701+
)
2702+
)
2703+
}
2704+
26242705
/**
26252706
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
26262707
*
@@ -2669,13 +2750,21 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
26692750
*
26702751
* NOTE: If an indirect expression is tracked, use `getAnIndirectBarrierNode` instead.
26712752
*/
2672-
ExprNode getABarrierNode() {
2753+
Node getABarrierNode() {
26732754
exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge |
26742755
e = value.getAnInstruction().getConvertedResultExpression() and
2675-
result.getConvertedExpr() = e and
2756+
result.asConvertedExpr() = e and
26762757
guardChecks(g, value.getAnInstruction().getConvertedResultExpression(), edge) and
26772758
g.controls(result.getBasicBlock(), edge)
26782759
)
2760+
or
2761+
exists(
2762+
IRGuardCondition g, boolean branch, Ssa::DefinitionExt def, IRBlock input, Ssa::PhiNode phi
2763+
|
2764+
guardChecks(g, def.getARead().asOperand().getDef().getConvertedResultExpression(), branch) and
2765+
guardControlsPhiInput(g, branch, def, input, phi) and
2766+
result = TSsaPhiInputNode(phi, input)
2767+
)
26792768
}
26802769

26812770
/**
@@ -2711,7 +2800,7 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
27112800
*
27122801
* NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead.
27132802
*/
2714-
IndirectExprNode getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) }
2803+
Node getAnIndirectBarrierNode() { result = getAnIndirectBarrierNode(_) }
27152804

27162805
/**
27172806
* Gets an indirect expression node with indirection index `indirectionIndex` that is
@@ -2747,13 +2836,23 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
27472836
*
27482837
* NOTE: If a non-indirect expression is tracked, use `getABarrierNode` instead.
27492838
*/
2750-
IndirectExprNode getAnIndirectBarrierNode(int indirectionIndex) {
2839+
Node getAnIndirectBarrierNode(int indirectionIndex) {
27512840
exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge |
27522841
e = value.getAnInstruction().getConvertedResultExpression() and
2753-
result.getConvertedExpr(indirectionIndex) = e and
2842+
result.asIndirectConvertedExpr(indirectionIndex) = e and
27542843
guardChecks(g, value.getAnInstruction().getConvertedResultExpression(), edge) and
27552844
g.controls(result.getBasicBlock(), edge)
27562845
)
2846+
or
2847+
exists(
2848+
IRGuardCondition g, boolean branch, Ssa::DefinitionExt def, IRBlock input, Ssa::PhiNode phi
2849+
|
2850+
guardChecks(g,
2851+
def.getARead().asIndirectOperand(indirectionIndex).getDef().getConvertedResultExpression(),
2852+
branch) and
2853+
guardControlsPhiInput(g, branch, def, input, phi) and
2854+
result = TSsaPhiInputNode(phi, input)
2855+
)
27572856
}
27582857
}
27592858

@@ -2762,6 +2861,14 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
27622861
*/
27632862
signature predicate instructionGuardChecksSig(IRGuardCondition g, Instruction instr, boolean branch);
27642863

2864+
private EdgeKind getConditionalEdge(boolean branch) {
2865+
branch = true and
2866+
result instanceof TrueEdge
2867+
or
2868+
branch = false and
2869+
result instanceof FalseEdge
2870+
}
2871+
27652872
/**
27662873
* Provides a set of barrier nodes for a guard that validates an instruction.
27672874
*
@@ -2770,12 +2877,20 @@ signature predicate instructionGuardChecksSig(IRGuardCondition g, Instruction in
27702877
*/
27712878
module InstructionBarrierGuard<instructionGuardChecksSig/3 instructionGuardChecks> {
27722879
/** Gets a node that is safely guarded by the given guard check. */
2773-
ExprNode getABarrierNode() {
2880+
Node getABarrierNode() {
27742881
exists(IRGuardCondition g, ValueNumber value, boolean edge, Operand use |
27752882
instructionGuardChecks(g, value.getAnInstruction(), edge) and
27762883
use = value.getAnInstruction().getAUse() and
27772884
result.asOperand() = use and
2778-
g.controls(use.getDef().getBlock(), edge)
2885+
g.controls(result.getBasicBlock(), edge)
2886+
)
2887+
or
2888+
exists(
2889+
IRGuardCondition g, boolean branch, Ssa::DefinitionExt def, IRBlock input, Ssa::PhiNode phi
2890+
|
2891+
instructionGuardChecks(g, def.getARead().asOperand().getDef(), branch) and
2892+
guardControlsPhiInput(g, branch, def, input, phi) and
2893+
result = TSsaPhiInputNode(phi, input)
27792894
)
27802895
}
27812896
}

0 commit comments

Comments
 (0)