Introduce partial-definition nodes

This commit is contained in:
Pavel Avgustinov
2019-07-09 19:01:10 +01:00
committed by Jonas Jensen
parent 15b56d93bd
commit 623652247d
3 changed files with 270 additions and 179 deletions

View File

@@ -4,8 +4,7 @@ private import DataFlowDispatch
/** Gets the instance argument of a non-static call. */
private Node getInstanceArgument(Call call) {
result.asExpr() = call.getQualifier() or
result.(ImplicitInstancePostCall).getCall() = call
result.asExpr() = call.getQualifier()
// This does not include the implicit `this` argument on auto-generated
// base class destructor calls as those do not have an AST element.
}
@@ -162,23 +161,6 @@ private class ArrayContent extends Content, TArrayContent {
override Type getType() { none() }
}
/**
* Gets the dataflow node corresponding to the field qualifier of `fa`.
*
* Note that this might be an implicit access to the `this` pointer.
*/
Node getFieldQualifier(FieldAccess fa) {
not fa.getTarget().isStatic() and
(
result.asExpr() = fa.getQualifier() or
result
.(ImplicitInstanceAccess)
.getInstanceAccess()
.(ImplicitThisForFieldAccess)
.getBackingExpr() = fa
)
}
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
* Thus, `node2` references an object with a field `f` that contains the
@@ -191,7 +173,7 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
a.getLValue() = fa
) and
not fa.getTarget().isStatic() and
node2.getPreUpdateNode() = getFieldQualifier(fa) and
node2.getPreUpdateNode().asExpr() = fa.getQualifier() and
f.(FieldContent).getField() = fa.getTarget()
)
}
@@ -203,7 +185,7 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
*/
predicate readStep(Node node1, Content f, Node node2) {
exists(FieldAccess fr |
node1 = getFieldQualifier(fr) and
node1.asExpr() = fr.getQualifier() and
fr.getTarget() = f.(FieldContent).getField() and
fr = node2.asExpr() and
not fr = any(AssignExpr a).getLValue()

View File

@@ -6,57 +6,12 @@ private import cpp
private import semmle.code.cpp.dataflow.internal.FlowVar
private import semmle.code.cpp.models.interfaces.DataFlow
private newtype TInstanceAccess =
TExplicitThisAccess(ThisExpr e) or
TImplicitThisForFieldAccess(FieldAccess fa) {
not exists(fa.getQualifier()) and not fa.getTarget().isStatic()
} or
TImplicitThisForCall(FunctionCall fc) {
fc.getTarget() instanceof MemberFunction and
not exists(fc.getQualifier()) and
not fc.getTarget().isStatic() and
not fc.getTarget() instanceof Constructor
}
private class InstanceAccess extends TInstanceAccess {
abstract Expr getBackingExpr();
Location getLocation() { result = getBackingExpr().getLocation() }
abstract string toString();
}
class ExplicitThisAccess extends InstanceAccess, TExplicitThisAccess {
override Expr getBackingExpr() { this = TExplicitThisAccess(result) }
override string toString() { result = getBackingExpr().toString() }
}
class ImplicitThisForFieldAccess extends InstanceAccess, TImplicitThisForFieldAccess {
override FieldAccess getBackingExpr() { this = TImplicitThisForFieldAccess(result) }
override string toString() { result = "<implicit this> for field access" }
}
class ImplicitThisForCall extends InstanceAccess, TImplicitThisForCall {
override FunctionCall getBackingExpr() { this = TImplicitThisForCall(result) }
override string toString() { result = "<implicit this> for call" }
}
cached
private newtype TNode =
TExprNode(Expr e) or
TParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or
TPartialDefNode(PartialDefinition pd) or
TExplicitParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or
TInstanceParameterNode(MemberFunction f) { exists(f.getBlock()) and not f.isStatic() } or
TImplicitInstanceAccessNode(InstanceAccess ia) { not ia instanceof ExplicitThisAccess } or
TImplicitInstancePostCallNode(InstanceAccess a) { a instanceof ImplicitThisForCall } or
TExplicitArgumentPostCallNode(Expr e) {
e = any(Call c).getAnArgument() or
e = any(Call c).getQualifier()
} or
TImplicitInstancePostStoreNode(InstanceAccess a) { a instanceof ImplicitThisForFieldAccess } or
TExplicitInstancePostStoreNode(Expr e) { e = any(FieldAccess f).getQualifier() } or
TDefinitionByReferenceNode(VariableAccess va, Expr argument) {
definitionByReference(va, argument)
} or
@@ -85,11 +40,23 @@ class Node extends TNode {
Expr asExpr() { result = this.(ExprNode).getExpr() }
/** Gets the parameter corresponding to this node, if any. */
Parameter asParameter() { result = this.(ParameterNode).getParameter() }
Parameter asParameter() { result = this.(ExplicitParameterNode).getParameter() }
/** Gets the argument that defines this `DefinitionByReferenceNode`, if any. */
Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() }
/**
* Gets the expression that is partially defined by this node, if any.
*
* Partial definitions are created for field stores (`x.y = taint();` is a partial
* definition of `x`), and for calls that may change the value of an object (so
* `x.set(taint())` is a partial definition of `x`, annd `transfer(&x, taint())` is
* a partial definition of `&x`).s
*/
Expr asPartialDefinition() {
result = this.(PartialDefNode).getPartialDefinition().getDefinedExpr()
}
/**
* Gets the uninitialized local variable corresponding to this node, if
* any.
@@ -128,14 +95,22 @@ class ExprNode extends Node, TExprNode {
Expr getExpr() { result = expr }
}
abstract class ParameterNode extends Node, TNode {
/**
* Holds if this node is the parameter of `c` at the specified (zero-based)
* position. The implicit `this` parameter is considered to have index `-1`.
*/
abstract predicate isParameterOf(Function f, int i);
}
/**
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParameterNode extends Node, TParameterNode {
class ExplicitParameterNode extends ParameterNode, TExplicitParameterNode {
Parameter param;
ParameterNode() { this = TParameterNode(param) }
ExplicitParameterNode() { this = TExplicitParameterNode(param) }
override Function getFunction() { result = param.getFunction() }
@@ -148,29 +123,23 @@ class ParameterNode extends Node, TParameterNode {
/** Gets the parameter corresponding to this node. */
Parameter getParameter() { result = param }
/**
* Holds if this node is the parameter of `c` at the specified (zero-based)
* position. The implicit `this` parameter is considered to have index `-1`.
*/
predicate isParameterOf(Function f, int i) { f.getParameter(i) = param }
override predicate isParameterOf(Function f, int i) { f.getParameter(i) = param }
}
/**
* The value of the implicit instance parameter (in other words, the `this`
* pointer) at function entry, viewed as a node in a data flow graph.
*/
class InstanceParameterNode extends Node, TInstanceParameterNode {
class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode {
MemberFunction f;
InstanceParameterNode() { this = TInstanceParameterNode(f) }
ImplicitParameterNode() { this = TInstanceParameterNode(f) }
override Function getFunction() { result = f }
override Type getType() { result.(PointerType).getBaseType() = f.getDeclaringType() }
override Type getType() { result = f.getDeclaringType() }
override string toString() { result = "<this> param" }
override string toString() { result = "`this` parameter in " + f.getName() }
override Location getLocation() { result = f.getLocation() }
override predicate isParameterOf(Function fun, int i) { f = fun and i = -1 }
}
/**
@@ -253,40 +222,18 @@ abstract class PostUpdateNode extends Node {
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
}
class ImplicitInstanceAccess extends Node, TImplicitInstanceAccessNode {
InstanceAccess ia;
class PartialDefNode extends PostUpdateNode, TPartialDefNode {
PartialDefinition pd;
ImplicitInstanceAccess() { this = TImplicitInstanceAccessNode(ia) }
PartialDefNode() { this = TPartialDefNode(pd) }
override string toString() { result = ia.toString() }
override Node getPreUpdateNode() { result.asExpr() = pd.getDefinedExpr() }
override Location getLocation() { result = ia.getLocation() }
override string toString() { result = pd.toString() }
InstanceAccess getInstanceAccess() { result = ia }
}
override Location getLocation() { result = pd.getLocation() }
class ImplicitInstancePostCall extends PostUpdateNode, TImplicitInstancePostCallNode {
ImplicitThisForCall ia;
ImplicitInstancePostCall() { this = TImplicitInstancePostCallNode(ia) }
override Node getPreUpdateNode() { ia = result.(ImplicitInstanceAccess).getInstanceAccess() }
FunctionCall getCall() { result = ia.getBackingExpr() }
}
class ExplicitArgPostCall extends PostUpdateNode, TExplicitArgumentPostCallNode {
override Node getPreUpdateNode() { this = TExplicitArgumentPostCallNode(result.asExpr()) }
}
class ImplicitStoreTarget extends PostUpdateNode, TImplicitInstancePostStoreNode {
override Node getPreUpdateNode() {
this = TImplicitInstancePostStoreNode(result.(ImplicitInstanceAccess).getInstanceAccess())
}
}
class ExplicitStoreTarget extends PostUpdateNode, TExplicitInstancePostStoreNode {
override Node getPreUpdateNode() { this = TExplicitInstancePostStoreNode(result.asExpr()) }
PartialDefinition getPartialDefinition() { result = pd }
}
/**
@@ -297,7 +244,7 @@ ExprNode exprNode(Expr e) { result.getExpr() = e }
/**
* Gets the `Node` corresponding to the value of `p` at function entry.
*/
ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
ParameterNode parameterNode(Parameter p) { result.(ExplicitParameterNode).getParameter() = p }
/**
* Gets the `Node` corresponding to a definition by reference of the variable
@@ -313,6 +260,43 @@ DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) {
*/
UninitializedNode uninitializedNode(LocalVariable v) { result.getLocalVariable() = v }
private module ThisFlow {
private Node thisAccessNode(ControlFlowNode cfn) {
result.(ImplicitParameterNode).getFunction().getBlock() = cfn or
result.asExpr().(ThisExpr) = cfn
}
private int basicBlockThisIndex(BasicBlock b, Node thisNode) {
thisNode = thisAccessNode(b.getNode(result))
}
private int thisRank(BasicBlock b, Node thisNode) {
thisNode = rank[result](thisAccessNode(_) as node order by basicBlockThisIndex(b, node))
}
private int lastThisRank(BasicBlock b) { result = max(thisRank(b, _)) }
private predicate thisAccessBlockReaches(BasicBlock b1, BasicBlock b2) {
exists(basicBlockThisIndex(b1, _)) and b2 = b1.getASuccessor()
or
exists(BasicBlock mid |
thisAccessBlockReaches(b1, mid) and
b2 = mid.getASuccessor() and
not exists(basicBlockThisIndex(mid, _))
)
}
predicate adjacentThisRefs(Node n1, Node n2) {
exists(BasicBlock b | thisRank(b, n1) + 1 = thisRank(b, n2))
or
exists(BasicBlock b1, BasicBlock b2 |
lastThisRank(b1) = thisRank(b1, n1) and
thisAccessBlockReaches(b1, b2) and
thisRank(b2, n2) = 1
)
}
}
/**
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
* (intra-procedural) step.
@@ -332,12 +316,20 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
varSourceBaseCase(var, nodeFrom.asUninitialized())
or
var.definedByReference(nodeFrom.asDefiningArgument())
or
var.definedPartiallyAt(nodeFrom.asPartialDefinition())
) and
varToExprStep(var, nodeTo.asExpr())
)
or
// Expr -> DefinitionByReferenceNode
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
or
// `this` -> adjacent-`this`
ThisFlow::adjacentThisRefs(nodeFrom, nodeTo)
or
// post-update-`this` -> following-`this`-ref
ThisFlow::adjacentThisRefs(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
}
/**

View File

@@ -1,6 +1,7 @@
/**
* Provides a class for handling variables in the data flow analysis.
*/
import cpp
private import semmle.code.cpp.controlflow.SSA
private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
@@ -18,7 +19,8 @@ private import semmle.code.cpp.dataflow.internal.SubBasicBlocks
* member predicates explains how a `FlowVar` relates to syntactic constructs of
* the language.
*/
cached class FlowVar extends TFlowVar {
cached
class FlowVar extends TFlowVar {
/**
* Gets a `VariableAccess` that _may_ take its value from `this`. Consider
* the following snippet.
@@ -35,7 +37,8 @@ cached class FlowVar extends TFlowVar {
* corresponding to `x = 1`. The `x` in `x = 1` is not considered to be an
* access.
*/
cached abstract VariableAccess getAnAccess();
cached
abstract VariableAccess getAnAccess();
/**
* Holds if this `FlowVar` corresponds to a modification occurring when `node` is
@@ -49,13 +52,18 @@ cached class FlowVar extends TFlowVar {
* `node instanceof PostCrementOperation` is an exception to the rule that
* `this` contains the value of `e` after the evaluation of `node`.
*/
cached abstract predicate definedByExpr(Expr e, ControlFlowNode node);
cached
abstract predicate definedByExpr(Expr e, ControlFlowNode node);
/**
* Holds if this `FlowVar` corresponds to the data written by a call that
* passes a variable as argument `arg`.
*/
cached abstract predicate definedByReference(Expr arg);
cached
abstract predicate definedByReference(Expr arg);
cached
abstract predicate definedPartiallyAt(Expr e);
/**
* Holds if this `FlowVar` corresponds to the initial value of `v`. The following
@@ -68,15 +76,76 @@ cached class FlowVar extends TFlowVar {
* local variable is always overwritten before it is used, there is no
* `FlowVar` instance for the uninitialized value of that variable.
*/
cached abstract predicate definedByInitialValue(LocalScopeVariable v);
cached
abstract predicate definedByInitialValue(LocalScopeVariable v);
/** Gets a textual representation of this element. */
cached abstract string toString();
cached
abstract string toString();
/** Gets the location of this element. */
cached abstract Location getLocation();
cached
abstract Location getLocation();
}
/**
* Provides classes and predicates dealing with "partial definitions".
*
* In contrast to a normal "definition", which provides a new value for
* something, a partial definition is an expression that may affect a
* value, but does not necessarily replace it entirely. For example,
* `x.y = 1;` is a partial definition of the object `x`.
*/
private module PartialDefinitions {
private newtype TPartialDefinition =
TExplicitFieldStoreQualifier(Expr qualifier, FieldAccess fa, Expr assignment, Expr assigned) {
isInstanceFieldWrite(fa, assignment, assigned) and qualifier = fa.getQualifier()
} or
TExplicitCallQualifier(Expr qualifier, Call call) { qualifier = call.getQualifier() } or
TReferenceArgument(Expr arg, Call call, int i) { isReferenceOrPointerArg(arg, call, i) }
private predicate isInstanceFieldWrite(FieldAccess fa, Expr assignment, Expr assigned) {
not fa.getTarget().isStatic() and
assignmentLikeOperation(assignment, fa.getTarget(), fa, assigned)
}
private predicate isReferenceOrPointerArg(Expr arg, Call call, int i) {
arg = call.getArgument(i) and
exists(Type t | t = arg.getFullyConverted().getType().getUnspecifiedType() |
t instanceof ReferenceType or t instanceof PointerType
)
}
class PartialDefinition extends TPartialDefinition {
Expr definedExpr;
PartialDefinition() {
this = TExplicitFieldStoreQualifier(definedExpr, _, _, _) or
this = TExplicitCallQualifier(definedExpr, _) or
this = TReferenceArgument(definedExpr, _, _)
}
predicate partiallyDefines(Variable v) { definedExpr = v.getAnAccess() }
predicate partiallyDefinesThis(ThisExpr e) { definedExpr = e }
ControlFlowNode getSubBasicBlockStart() { result = definedExpr }
Expr getDefinedExpr() { result = definedExpr }
Location getLocation() {
not exists(definedExpr.getLocation()) and result = definedExpr.getParent().getLocation()
or
definedExpr.getLocation() instanceof UnknownLocation and
result = definedExpr.getParent().getLocation()
or
result = definedExpr.getLocation() and not result instanceof UnknownLocation
}
string toString() { result = "partial def of " + definedExpr }
}
}
import PartialDefinitions
private import FlowVar_internal
/**
@@ -112,9 +181,7 @@ module FlowVar_internal {
// always executes at least once, we give it special treatment in
// `BlockVar`, somewhat analogous to unrolling the first iteration of the
// loop.
not exists(AlwaysTrueUponEntryLoop loop |
loop.alwaysAssignsBeforeLeavingCondition(_, _, v)
) and
not exists(AlwaysTrueUponEntryLoop loop | loop.alwaysAssignsBeforeLeavingCondition(_, _, v)) and
// The SSA library has a theoretically accurate treatment of reference types,
// treating them as immutable, but for data flow it gives better results in
// practice to make the variable synonymous with its contents.
@@ -125,9 +192,7 @@ module FlowVar_internal {
* Holds if `sbb` is the `SubBasicBlock` where `v` receives its initial value.
* See the documentation for `FlowVar.definedByInitialValue`.
*/
predicate blockVarDefinedByVariable(
SubBasicBlock sbb, LocalScopeVariable v)
{
predicate blockVarDefinedByVariable(SubBasicBlock sbb, LocalScopeVariable v) {
sbb = v.(Parameter).getFunction().getEntryPoint()
or
exists(DeclStmt declStmt |
@@ -141,20 +206,27 @@ module FlowVar_internal {
TSsaVar(SsaDefinition def, LocalScopeVariable v) {
fullySupportedSsaVariable(v) and
v = def.getAVariable()
}
or
} or
TBlockVar(SubBasicBlock sbb, Variable v) {
not fullySupportedSsaVariable(v) and
reachable(sbb) and
(
initializer(sbb.getANode(), v, _)
or
assignmentLikeOperation(sbb, v, _)
assignmentLikeOperation(sbb, v, _, _)
or
blockVarDefinedByReference(sbb, v, _)
or
sbb = any(PartialDefinitions::PartialDefinition p | p.partiallyDefines(v))
.getSubBasicBlockStart()
or
blockVarDefinedByVariable(sbb, v)
)
} or
TThisVar(SubBasicBlock sbb, ThisExpr t) {
reachable(sbb) and
sbb = any(PartialDefinitions::PartialDefinition p | p.partiallyDefinesThis(t))
.getSubBasicBlockStart()
}
/**
@@ -162,6 +234,7 @@ module FlowVar_internal {
*/
class SsaVar extends TSsaVar, FlowVar {
SsaDefinition def;
LocalScopeVariable v;
SsaVar() { this = TSsaVar(def, v) }
@@ -171,7 +244,8 @@ module FlowVar_internal {
// the data flow library will never see those, and the `FlowVar` library
// is only meant to be used by the data flow library.
this.isNonPhi() and
( // This base case could be included in the transitive case by changing
(
// This base case could be included in the transitive case by changing
// `+` to `*`, but that's slower because it goes through the `TSsaVar`
// indirection.
result = def.getAUse(v)
@@ -185,15 +259,19 @@ module FlowVar_internal {
override predicate definedByExpr(Expr e, ControlFlowNode node) {
e = def.getDefiningValue(v) and
(if def.getDefinition() = v.getInitializer().getExpr()
then node = v.getInitializer()
else node = def.getDefinition())
(
if def.getDefinition() = v.getInitializer().getExpr()
then node = v.getInitializer()
else node = def.getDefinition()
)
}
override predicate definedByReference(Expr arg) {
none() // Not supported for SSA. See `fullySupportedSsaVariable`.
}
override predicate definedPartiallyAt(Expr e) { none() }
override predicate definedByInitialValue(LocalScopeVariable param) {
def.definedByParameter(param) and
param = v
@@ -236,6 +314,7 @@ module FlowVar_internal {
*/
class BlockVar extends TBlockVar, FlowVar {
SubBasicBlock sbb;
Variable v;
BlockVar() { this = TBlockVar(sbb, v) }
@@ -250,46 +329,84 @@ module FlowVar_internal {
}
override predicate definedByExpr(Expr e, ControlFlowNode node) {
assignmentLikeOperation(node, v, e) and
assignmentLikeOperation(node, v, _, e) and
node = sbb
or
initializer(node, v, e) and
node = sbb.getANode()
}
override predicate definedByReference(Expr arg) {
blockVarDefinedByReference(sbb, v, arg)
override predicate definedByReference(Expr arg) { blockVarDefinedByReference(sbb, v, arg) }
override predicate definedPartiallyAt(Expr e) {
exists(PartialDefinitions::PartialDefinition p |
p.partiallyDefines(v) and
sbb = p.getSubBasicBlockStart() and
e = p.getDefinedExpr()
)
}
override string toString() {
exists(Expr e |
this.definedByExpr(e, _) and
result = "assignment to "+ v
result = "assignment to " + v
)
or
this.definedByInitialValue(_) and
result = "initial value of "+ v
result = "initial value of " + v
or
exists(Expr arg |
this.definedByReference(arg) and
result = "definition by reference of "+ v
result = "definition by reference of " + v
)
or
exists(Expr partialDef |
this.definedPartiallyAt(partialDef) and
result = "partial definition at " + partialDef
)
or
// impossible case
not this.definedByExpr(_, _) and
not this.definedByInitialValue(_) and
not this.definedByReference(_) and
result = "undefined "+ v
not this.definedPartiallyAt(_) and
result = "undefined " + v
}
override Location getLocation() { result = sbb.getStart().getLocation() }
}
/** Type-specialized version of `getEnclosingElement`. */
private ControlFlowNode getCFNParent(ControlFlowNode node) {
result = node.getEnclosingElement()
class ThisVar extends TThisVar, FlowVar {
SubBasicBlock sbb;
ThisExpr t;
PartialDefinitions::PartialDefinition pd;
ThisVar() { this = TThisVar(sbb, t) and pd.partiallyDefinesThis(t) }
override VariableAccess getAnAccess() {
none() // TODO: Widen type to `Expr`.
}
override predicate definedByExpr(Expr e, ControlFlowNode node) { none() }
override predicate definedByReference(Expr arg) { none() }
override predicate definedPartiallyAt(Expr arg) {
none() // TODO
}
override predicate definedByInitialValue(LocalScopeVariable param) { none() }
override string toString() { result = pd.toString() }
override Location getLocation() { result = pd.getLocation() }
}
/** Type-specialized version of `getEnclosingElement`. */
private ControlFlowNode getCFNParent(ControlFlowNode node) { result = node.getEnclosingElement() }
/**
* A for-loop or while-loop whose condition is always true upon entry but not
* always true after the first iteration.
@@ -334,7 +451,7 @@ module FlowVar_internal {
pragma[noinline]
private Variable getAVariableAssignedInLoop() {
exists(BasicBlock bbAssign |
assignmentLikeOperation(bbAssign.getANode(), result, _) and
assignmentLikeOperation(bbAssign.getANode(), result, _, _) and
this.bbInLoop(bbAssign)
)
}
@@ -368,7 +485,7 @@ module FlowVar_internal {
reachesWithoutAssignment(bb.getAPredecessor(), v) and
this.bbInLoop(bb)
) and
not assignmentLikeOperation(bb.getANode(), v, _)
not assignmentLikeOperation(bb.getANode(), v, _, _)
}
}
@@ -379,11 +496,9 @@ module FlowVar_internal {
* be used outside the loop.
*/
predicate skipLoop(
SubBasicBlock sbbInside, SubBasicBlock sbbOutside,
SubBasicBlock sbbDef, Variable v
SubBasicBlock sbbInside, SubBasicBlock sbbOutside, SubBasicBlock sbbDef, Variable v
) {
exists(AlwaysTrueUponEntryLoop loop,
BasicBlock bbInside, BasicBlock bbOutside |
exists(AlwaysTrueUponEntryLoop loop, BasicBlock bbInside, BasicBlock bbOutside |
loop.alwaysAssignsBeforeLeavingCondition(bbInside, bbOutside, v) and
bbInside = sbbInside.getBasicBlock() and
bbOutside = sbbOutside.getBasicBlock() and
@@ -403,7 +518,7 @@ module FlowVar_internal {
mid = getAReachedBlockVarSBB(start) and
result = mid.getASuccessor() and
not skipLoop(mid, result, sbbDef, v) and
not assignmentLikeOperation(result, v, _)
not assignmentLikeOperation(result, v, _, _)
)
}
@@ -468,18 +583,18 @@ module FlowVar_internal {
forall(VariableAccess va |
va.getTarget() = v and
readAccess(va)
| dominatedByOverwrite(v, va)
|
dominatedByOverwrite(v, va)
)
}
/** Holds if `va` accesses `v` and is dominated by an overwrite of `v`. */
predicate dominatedByOverwrite(UninitializedLocalVariable v,
VariableAccess va)
{
predicate dominatedByOverwrite(UninitializedLocalVariable v, VariableAccess va) {
exists(BasicBlock bb, int vaIndex |
va = bb.getNode(vaIndex) and
va.getTarget() = v
| vaIndex > indexOfFirstOverwriteInBB(v, bb)
|
vaIndex > indexOfFirstOverwriteInBB(v, bb)
or
bbStrictlyDominates(getAnOverwritingBB(v), bb)
)
@@ -520,29 +635,32 @@ module FlowVar_internal {
* `initializer` instead of this predicate.
*/
predicate assignmentLikeOperation(
ControlFlowNode node, Variable v, Expr assignedExpr)
{
ControlFlowNode node, Variable v, VariableAccess va, Expr assignedExpr
) {
// Together, the two following cases cover `Assignment`
node = any(AssignExpr ae |
v = ae.getLValue().(VariableAccess).getTarget() and
assignedExpr = ae.getRValue()
)
va = ae.getLValue() and
v = va.getTarget() and
assignedExpr = ae.getRValue()
)
or
node = any(AssignOperation ao |
v = ao.getLValue().(VariableAccess).getTarget() and
// Here and in the `PrefixCrementOperation` case, we say that the assigned
// expression is the operation itself. For example, we say that `x += 1`
// assigns `x += 1` to `x`. The justification is that after this operation,
// `x` will contain the same value that `x += 1` evaluated to.
assignedExpr = ao
)
va = ao.getLValue() and
v = va.getTarget() and
// Here and in the `PrefixCrementOperation` case, we say that the assigned
// expression is the operation itself. For example, we say that `x += 1`
// assigns `x += 1` to `x`. The justification is that after this operation,
// `x` will contain the same value that `x += 1` evaluated to.
assignedExpr = ao
)
or
// This case does not add further data flow paths, except if a
// `PrefixCrementOperation` is itself a source
node = any(CrementOperation op |
v = op.getOperand().(VariableAccess).getTarget() and
assignedExpr = op
)
va = op.getOperand() and
v = va.getTarget() and
assignedExpr = op
)
}
predicate blockVarDefinedByReference(ControlFlowNode node, Variable v, Expr argument) {
@@ -553,9 +671,7 @@ module FlowVar_internal {
/**
* Holds if `v` is initialized by `init` to have value `assignedExpr`.
*/
predicate initializer(
Initializer init, LocalVariable v, Expr assignedExpr)
{
predicate initializer(Initializer init, LocalVariable v, Expr assignedExpr) {
v = init.getDeclaration() and
assignedExpr = init.getExpr()
}
@@ -568,12 +684,12 @@ module FlowVar_internal {
*/
class DataFlowSubBasicBlockCutNode extends SubBasicBlockCutNode {
DataFlowSubBasicBlockCutNode() {
exists(Variable v |
not fullySupportedSsaVariable(v)
|
assignmentLikeOperation(this, v, _)
exists(Variable v | not fullySupportedSsaVariable(v) |
assignmentLikeOperation(this, v, _, _)
or
blockVarDefinedByReference(this, v, _)
or
this = any(PartialDefinitions::PartialDefinition p).getSubBasicBlockStart()
// It is not necessary to cut the basic blocks at `Initializer` nodes
// because the affected variable can have no _other_ value before its
// initializer. It is not necessary to cut basic blocks at procedure
@@ -582,4 +698,5 @@ module FlowVar_internal {
)
}
}
} /* module FlowVar_internal */
}
/* module FlowVar_internal */