diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll index a63ea897a13..9e301791a7f 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowPrivate.qll @@ -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() diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 586dfb2fc8e..c7f1ccfef4a 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -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 = " for field access" } -} - -class ImplicitThisForCall extends InstanceAccess, TImplicitThisForCall { - override FunctionCall getBackingExpr() { this = TImplicitThisForCall(result) } - - override string toString() { result = " 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 = " 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) } /** diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll index c5b1fcdba52..ce4850629ec 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/FlowVar.qll @@ -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 */