Dataflow: Introduce NodeRegions for use in isUnreachableInCall.

This commit is contained in:
Anders Schack-Mulligen
2024-04-25 15:36:26 +02:00
parent 486eaad566
commit bc8ca1af86
12 changed files with 142 additions and 31 deletions

View File

@@ -264,7 +264,15 @@ class DataFlowCall extends Expr instanceof Call {
Function getEnclosingCallable() { result = this.getEnclosingFunction() }
}
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
class NodeRegion instanceof Unit {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { none() }
int totalOrder() { result = 1 }
}
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) { none() } // stub implementation
/**
* Holds if access paths with `c` at their head always should be tracked at high

View File

@@ -1247,16 +1247,30 @@ module IsUnreachableInCall {
any(G::IRGuardCondition guard).ensuresLt(left, right, k, block, areEqual)
}
predicate isUnreachableInCall(Node n, DataFlowCall call) {
class NodeRegion instanceof IRBlock {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { this = n.getBasicBlock() }
int totalOrder() {
this =
rank[result](IRBlock b, int startline, int startcolumn |
b.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
b order by startline, startcolumn
)
}
}
predicate isUnreachableInCall(NodeRegion block, DataFlowCall call) {
exists(
InstructionDirectParameterNode paramNode, ConstantIntegralTypeArgumentNode arg,
IntegerConstantInstruction constant, int k, Operand left, Operand right, IRBlock block
IntegerConstantInstruction constant, int k, Operand left, Operand right
|
// arg flows into `paramNode`
DataFlowImplCommon::viableParamArg(call, paramNode, arg) and
left = constant.getAUse() and
right = valueNumber(paramNode.getInstruction()).getAUse() and
block = n.getBasicBlock()
right = valueNumber(paramNode.getInstruction()).getAUse()
|
// and there's a guard condition which ensures that the result of `left == right + k` is `areEqual`
exists(boolean areEqual |

View File

@@ -2380,16 +2380,31 @@ predicate expectsContent(Node n, ContentSet c) {
n.asExpr() instanceof SpreadElementExpr and c instanceof ElementContent
}
class NodeRegion instanceof ControlFlow::BasicBlock {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { this = n.getControlFlowNode().getBasicBlock() }
int totalOrder() {
this =
rank[result](ControlFlow::BasicBlock b, int startline, int startcolumn |
b.getLocation().hasLocationInfo(_, startline, startcolumn, _, _)
|
b order by startline, startcolumn
)
}
}
/**
* Holds if the node `n` is unreachable when the call context is `call`.
* Holds if the nodes in `nr` are unreachable when the call context is `call`.
*/
predicate isUnreachableInCall(Node n, DataFlowCall call) {
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) {
exists(
ExplicitParameterNode paramNode, Guard guard, ControlFlow::SuccessorTypes::BooleanSuccessor bs
|
viableConstantBooleanParamArg(paramNode, bs.getValue().booleanNot(), call) and
paramNode.getSsaDefinition().getARead() = guard and
guard.controlsBlock(n.getControlFlowNode().getBasicBlock(), bs, _)
guard.controlsBlock(nr, bs, _)
)
}

View File

@@ -371,11 +371,26 @@ private ControlFlow::ConditionGuardNode getAFalsifiedGuard(DataFlowCall call) {
)
}
class NodeRegion instanceof BasicBlock {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { n.getBasicBlock() = this }
int totalOrder() {
this =
rank[result](BasicBlock b, int startline, int startcolumn |
b.hasLocationInfo(_, startline, startcolumn, _, _)
|
b order by startline, startcolumn
)
}
}
/**
* Holds if the node `n` is unreachable when the call context is `call`.
* Holds if the nodes in `nr` are unreachable when the call context is `call`.
*/
predicate isUnreachableInCall(Node n, DataFlowCall call) {
getAFalsifiedGuard(call).dominates(n.getBasicBlock())
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) {
getAFalsifiedGuard(call).dominates(nr)
}
/**

View File

@@ -502,6 +502,18 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
override Location getLocation() { result = c.getLocation() }
}
private predicate id(BasicBlock x, BasicBlock y) { x = y }
private predicate idOf(BasicBlock x, int y) = equivalenceRelation(id/2)(x, y)
class NodeRegion instanceof BasicBlock {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { n.asExpr().getBasicBlock() = this }
int totalOrder() { idOf(this, result) }
}
/** Holds if `e` is an expression that always has the same Boolean value `val`. */
private predicate constantBooleanExpr(Expr e, boolean val) {
e.(CompileTimeConstantExpr).getBooleanValue() = val
@@ -522,9 +534,9 @@ private class ConstantBooleanArgumentNode extends ArgumentNode, ExprNode {
}
/**
* Holds if the node `n` is unreachable when the call context is `call`.
* Holds if the nodes in `nr` are unreachable when the call context is `call`.
*/
predicate isUnreachableInCall(Node n, DataFlowCall call) {
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) {
exists(
ExplicitParameterNode paramNode, ConstantBooleanArgumentNode arg, SsaImplicitInit param,
Guard guard
@@ -537,7 +549,7 @@ predicate isUnreachableInCall(Node n, DataFlowCall call) {
param.getAUse() = guard and
// which controls `n` with the opposite value of `arg`
guard
.controls(n.asExpr().getBasicBlock(),
.controls(nr,
pragma[only_bind_into](pragma[only_bind_out](arg.getBooleanValue()).booleanNot()))
)
}

View File

@@ -1023,13 +1023,21 @@ predicate attributeClearStep(Node n, AttributeContent c) {
exists(PostUpdateNode post | post.getPreUpdateNode() = n | attributeStoreStep(_, c, post))
}
class NodeRegion instanceof Unit {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { none() }
int totalOrder() { result = 1 }
}
//--------
// Fancy context-sensitive guards
//--------
/**
* Holds if the node `n` is unreachable when the call context is `call`.
* Holds if the nodes in `nr` are unreachable when the call context is `call`.
*/
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() }
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) { none() }
/**
* Holds if access paths with `c` at their head always should be tracked at high

View File

@@ -2170,10 +2170,18 @@ class DataFlowExpr = CfgNodes::ExprCfgNode;
*/
predicate forceHighPrecision(Content c) { c instanceof Content::ElementContent }
class NodeRegion instanceof Unit {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { none() }
int totalOrder() { result = 1 }
}
/**
* Holds if the node `n` is unreachable when the call context is `call`.
* Holds if the nodes in `nr` are unreachable when the call context is `call`.
*/
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() }
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) { none() }
newtype LambdaCallKind =
TYieldCallKind() or

View File

@@ -251,10 +251,18 @@ signature module InputSig<LocationSig Location> {
*/
predicate expectsContent(Node n, ContentSet c);
/** A set of `Node`s in a `DataFlowCallable`. */
class NodeRegion {
/** Holds if this region contains `n`. */
predicate contains(Node n);
int totalOrder();
}
/**
* Holds if the node `n` is unreachable when the call context is `call`.
* Holds if the nodes in `nr` are unreachable when the call context is `call`.
*/
predicate isUnreachableInCall(Node n, DataFlowCall call);
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call);
default int accessPathLimit() { result = 5 }

View File

@@ -350,7 +350,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
pragma[nomagic]
private predicate isUnreachableInCall1(NodeEx n, LocalCallContextSpecificCall cc) {
isUnreachableInCallCached(n.asNode(), cc.getCall())
exists(NodeRegion nr |
nr.contains(n.asNode()) and
isUnreachableInCallCached(nr, cc.getCall())
)
}
/**
@@ -5245,7 +5248,10 @@ module MakeImpl<LocationSig Location, InputSig<Location> Lang> {
TSummaryCtx2 sc2, TSummaryCtx3 sc3, TSummaryCtx4 sc4, DataFlowType t, PartialAccessPath ap,
boolean isStoreStep
) {
not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and
not exists(NodeRegion nr |
nr.contains(node.asNode()) and
isUnreachableInCallCached(nr, cc.(CallContextSpecificCall).getCall())
) and
(
localFlowStepEx(mid.getNodeEx(), node, _) and
state = mid.getState() and

View File

@@ -465,10 +465,10 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
*/
pragma[nomagic]
predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) {
exists(Node n |
exists(NodeRegion nr |
relevantCallEdgeIn(call, callable) and
getNodeEnclosingCallable(n) = callable and
isUnreachableInCallCached(n, call)
getNodeRegionEnclosingCallable(nr) = callable and
isUnreachableInCallCached(nr, call)
)
}
@@ -659,7 +659,9 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) }
cached
predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) }
predicate isUnreachableInCallCached(NodeRegion nr, DataFlowCall call) {
isUnreachableInCall(nr, call)
}
cached
predicate outNodeExt(Node n) {
@@ -1823,8 +1825,14 @@ module MakeImplCommon<LocationSig Location, InputSig<Location> Lang> {
override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) }
}
private DataFlowCallable getNodeRegionEnclosingCallable(NodeRegion nr) {
exists(Node n | nr.contains(n) | getNodeEnclosingCallable(n) = result)
}
private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) {
exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call))
exists(NodeRegion nr |
getNodeRegionEnclosingCallable(nr) = callable and isUnreachableInCallCached(nr, call)
)
}
/**

View File

@@ -187,8 +187,9 @@ module MakeConsistency<
}
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
isUnreachableInCall(n, call) and
exists(DataFlowCallable c |
exists(DataFlowCallable c, NodeRegion nr |
isUnreachableInCall(nr, call) and
nr.contains(n) and
c = nodeGetEnclosingCallable(n) and
not viableCallable(call) = c
) and

View File

@@ -1371,10 +1371,18 @@ class DataFlowExpr = Expr;
*/
predicate forceHighPrecision(Content c) { c instanceof Content::CollectionContent }
class NodeRegion instanceof Unit {
string toString() { result = "NodeRegion" }
predicate contains(Node n) { none() }
int totalOrder() { result = 1 }
}
/**
* Holds if the node `n` is unreachable when the call context is `call`.
* Holds if the nodes in `nr` are unreachable when the call context is `call`.
*/
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() }
predicate isUnreachableInCall(NodeRegion nr, DataFlowCall call) { none() }
newtype LambdaCallKind = TLambdaCallKind()