mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Introduce partial-definition nodes
This commit is contained in:
committed by
Jonas Jensen
parent
15b56d93bd
commit
623652247d
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user