mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge pull request #1000 from jbj/dataflow-defbyref
C++: Support definition by reference in data flow library
This commit is contained in:
@@ -3,10 +3,14 @@
|
||||
*/
|
||||
import cpp
|
||||
private import semmle.code.cpp.dataflow.internal.FlowVar
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
|
||||
private newtype TNode =
|
||||
TExprNode(Expr e) or
|
||||
TParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or
|
||||
TDefinitionByReferenceNode(VariableAccess va, Expr argument) {
|
||||
definitionByReference(va, argument)
|
||||
} or
|
||||
TUninitializedNode(LocalVariable v) {
|
||||
not v.hasInitializer()
|
||||
}
|
||||
@@ -20,13 +24,7 @@ private newtype TNode =
|
||||
*/
|
||||
class Node extends TNode {
|
||||
/** Gets the function to which this node belongs. */
|
||||
Function getFunction() {
|
||||
result = this.asExpr().getEnclosingFunction()
|
||||
or
|
||||
result = this.asParameter().getFunction()
|
||||
or
|
||||
result = this.asUninitialized().getFunction()
|
||||
}
|
||||
Function getFunction() { none() } // overridden in subclasses
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use. Alternative name for `getFunction`.
|
||||
@@ -36,11 +34,7 @@ class Node extends TNode {
|
||||
}
|
||||
|
||||
/** Gets the type of this node. */
|
||||
Type getType() {
|
||||
result = this.asExpr().getType()
|
||||
or
|
||||
result = asVariable(this).getType()
|
||||
}
|
||||
Type getType() { none() } // overridden in subclasses
|
||||
|
||||
/** Gets the expression corresponding to this node, if any. */
|
||||
Expr asExpr() { result = this.(ExprNode).getExpr() }
|
||||
@@ -48,6 +42,9 @@ class Node extends TNode {
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
Parameter asParameter() { result = this.(ParameterNode).getParameter() }
|
||||
|
||||
/** Gets the argument that defines this `DefinitionByReferenceNode`, if any. */
|
||||
Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() }
|
||||
|
||||
/**
|
||||
* Gets the uninitialized local variable corresponding to this node, if
|
||||
* any.
|
||||
@@ -74,6 +71,8 @@ class Node extends TNode {
|
||||
class ExprNode extends Node, TExprNode {
|
||||
Expr expr;
|
||||
ExprNode() { this = TExprNode(expr) }
|
||||
override Function getFunction() { result = expr.getEnclosingFunction() }
|
||||
override Type getType() { result = expr.getType() }
|
||||
override string toString() { result = expr.toString() }
|
||||
override Location getLocation() { result = expr.getLocation() }
|
||||
/** Gets the expression corresponding to this node. */
|
||||
@@ -87,6 +86,8 @@ class ExprNode extends Node, TExprNode {
|
||||
class ParameterNode extends Node, TParameterNode {
|
||||
Parameter param;
|
||||
ParameterNode() { this = TParameterNode(param) }
|
||||
override Function getFunction() { result = param.getFunction() }
|
||||
override Type getType() { result = param.getType() }
|
||||
override string toString() { result = param.toString() }
|
||||
override Location getLocation() { result = param.getLocation() }
|
||||
/** Gets the parameter corresponding to this node. */
|
||||
@@ -100,6 +101,35 @@ class ParameterNode extends Node, TParameterNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node that represents the value of a variable after a function call that
|
||||
* may have changed the variable because it's passed by reference.
|
||||
*
|
||||
* A typical example would be a call `f(&x)`. Firstly, there will be flow into
|
||||
* `x` from previous definitions of `x`. Secondly, there will be a
|
||||
* `DefinitionByReferenceNode` to represent the value of `x` after the call has
|
||||
* returned. This node will have its `getArgument()` equal to `&x`.
|
||||
*/
|
||||
class DefinitionByReferenceNode extends Node, TDefinitionByReferenceNode {
|
||||
VariableAccess va;
|
||||
Expr argument;
|
||||
|
||||
DefinitionByReferenceNode() { this = TDefinitionByReferenceNode(va, argument) }
|
||||
override Function getFunction() { result = va.getEnclosingFunction() }
|
||||
override Type getType() { result = va.getType() }
|
||||
override string toString() { result = "ref arg " + argument.toString() }
|
||||
override Location getLocation() { result = argument.getLocation() }
|
||||
/** Gets the argument corresponding to this node. */
|
||||
Expr getArgument() { result = argument }
|
||||
/** Gets the parameter through which this value is assigned. */
|
||||
Parameter getParameter() {
|
||||
exists(FunctionCall call, int i |
|
||||
argument = call.getArgument(i) and
|
||||
result = call.getTarget().getParameter(i)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of an uninitialized local variable, viewed as a node in a data
|
||||
* flow graph.
|
||||
@@ -107,6 +137,8 @@ class ParameterNode extends Node, TParameterNode {
|
||||
class UninitializedNode extends Node, TUninitializedNode {
|
||||
LocalVariable v;
|
||||
UninitializedNode() { this = TUninitializedNode(v) }
|
||||
override Function getFunction() { result = v.getFunction() }
|
||||
override Type getType() { result = v.getType() }
|
||||
override string toString() { result = v.toString() }
|
||||
override Location getLocation() { result = v.getLocation() }
|
||||
/** Gets the uninitialized local variable corresponding to this node. */
|
||||
@@ -143,6 +175,14 @@ ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
*/
|
||||
ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to a definition by reference of the variable
|
||||
* that is passed as `argument` of a call.
|
||||
*/
|
||||
DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) {
|
||||
result.getArgument() = argument
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to the value of an uninitialized local
|
||||
* variable `v`.
|
||||
@@ -151,12 +191,6 @@ UninitializedNode uninitializedNode(LocalVariable v) {
|
||||
result.getLocalVariable() = v
|
||||
}
|
||||
|
||||
private Variable asVariable(Node node) {
|
||||
result = node.asParameter()
|
||||
or
|
||||
result = node.asUninitialized()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
* (intra-procedural) step.
|
||||
@@ -170,10 +204,17 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
(
|
||||
exprToVarStep(nodeFrom.asExpr(), var)
|
||||
or
|
||||
varSourceBaseCase(var, asVariable(nodeFrom))
|
||||
varSourceBaseCase(var, nodeFrom.asParameter())
|
||||
or
|
||||
varSourceBaseCase(var, nodeFrom.asUninitialized())
|
||||
or
|
||||
var.definedByReference(nodeFrom.asDefiningArgument())
|
||||
) and
|
||||
varToExprStep(var, nodeTo.asExpr())
|
||||
)
|
||||
or
|
||||
// Expr -> DefinitionByReferenceNode
|
||||
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -232,10 +273,31 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
|
||||
fromExpr = op.getOperand()
|
||||
)
|
||||
or
|
||||
toExpr = any(FunctionCall moveCall |
|
||||
moveCall.getTarget().getNamespace().getName() = "std" and
|
||||
moveCall.getTarget().getName() = "move" and
|
||||
fromExpr = moveCall.getArgument(0)
|
||||
toExpr = any(Call call |
|
||||
exists(DataFlowFunction f, FunctionInput inModel , FunctionOutput outModel, int iIn |
|
||||
call.getTarget() = f and
|
||||
f.hasDataFlow(inModel, outModel) and
|
||||
outModel.isOutReturnValue() and
|
||||
inModel.isInParameter(iIn) and
|
||||
fromExpr = call.getArgument(iIn)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate exprToDefinitionByReferenceStep(Expr exprIn, Expr argOut) {
|
||||
exists(DataFlowFunction f, Call call, FunctionOutput outModel, int argOutIndex |
|
||||
call.getTarget() = f and
|
||||
argOut = call.getArgument(argOutIndex) and
|
||||
outModel.isOutParameterPointer(argOutIndex) and
|
||||
exists(int argInIndex, FunctionInput inModel |
|
||||
f.hasDataFlow(inModel, outModel)
|
||||
|
|
||||
inModel.isInParameterPointer(argInIndex) and
|
||||
call.passesByReference(argInIndex, exprIn)
|
||||
or
|
||||
inModel.isInParameter(argInIndex) and
|
||||
exprIn = call.getArgument(argInIndex)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,12 @@ cached class FlowVar extends TFlowVar {
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Holds if this `FlowVar` corresponds to the initial value of `v`. The following
|
||||
* is an exhaustive list of cases where this may happen.
|
||||
@@ -137,6 +143,8 @@ module FlowVar_internal {
|
||||
or
|
||||
assignmentLikeOperation(sbb, v, _)
|
||||
or
|
||||
blockVarDefinedByReference(sbb, v, _)
|
||||
or
|
||||
blockVarDefinedByVariable(sbb, v)
|
||||
)
|
||||
}
|
||||
@@ -174,6 +182,11 @@ module FlowVar_internal {
|
||||
else node = def.getDefinition())
|
||||
}
|
||||
|
||||
override predicate definedByReference(Expr arg) {
|
||||
definitionByReference(v.getAnAccess(), arg) and
|
||||
arg = def.getDefinition()
|
||||
}
|
||||
|
||||
override predicate definedByInitialValue(LocalScopeVariable param) {
|
||||
def.definedByParameter(param) and
|
||||
param = v
|
||||
@@ -191,6 +204,8 @@ module FlowVar_internal {
|
||||
this.definedByExpr(_, _)
|
||||
or
|
||||
this.definedByInitialValue(_)
|
||||
or
|
||||
this.definedByReference(_)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,7 +236,17 @@ module FlowVar_internal {
|
||||
BlockVar() { this = TBlockVar(sbb, v) }
|
||||
|
||||
override VariableAccess getAnAccess() {
|
||||
variableAccessInSBB(v, getAReachedBlockVarSBB(this), result)
|
||||
exists(SubBasicBlock reached |
|
||||
reached = getAReachedBlockVarSBB(this)
|
||||
|
|
||||
variableAccessInSBB(v, reached, result)
|
||||
or
|
||||
// Allow flow into a `VariableAccess` that is used as definition by
|
||||
// reference. This flow is blocked by `getAReachedBlockVarSBB` because
|
||||
// flow should not propagate past that.
|
||||
result = reached.getASuccessor().(VariableAccess) and
|
||||
blockVarDefinedByReference(result, v, _)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate definedByInitialValue(LocalScopeVariable lsv) {
|
||||
@@ -237,6 +262,10 @@ module FlowVar_internal {
|
||||
node = sbb.getANode()
|
||||
}
|
||||
|
||||
override predicate definedByReference(Expr arg) {
|
||||
blockVarDefinedByReference(sbb, v, arg)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
exists(Expr e |
|
||||
this.definedByExpr(e, _) and
|
||||
@@ -246,9 +275,15 @@ module FlowVar_internal {
|
||||
this.definedByInitialValue(_) and
|
||||
result = "initial value of "+ v
|
||||
or
|
||||
exists(Expr arg |
|
||||
this.definedByReference(arg) and
|
||||
result = "ref def: "+ arg
|
||||
)
|
||||
or
|
||||
// impossible case
|
||||
not this.definedByExpr(_, _) and
|
||||
not this.definedByInitialValue(_) and
|
||||
not this.definedByReference(_) and
|
||||
result = "undefined "+ v
|
||||
}
|
||||
|
||||
@@ -373,7 +408,8 @@ 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, _) and
|
||||
not blockVarDefinedByReference(result, v, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -481,6 +517,9 @@ module FlowVar_internal {
|
||||
*/
|
||||
predicate overwrite(VariableAccess va, ControlFlowNode node) {
|
||||
va = node.(AssignExpr).getLValue()
|
||||
or
|
||||
va = node and
|
||||
definitionByReference(node, _)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -515,6 +554,11 @@ module FlowVar_internal {
|
||||
)
|
||||
}
|
||||
|
||||
predicate blockVarDefinedByReference(ControlFlowNode node, Variable v, Expr argument) {
|
||||
node = v.getAnAccess() and
|
||||
definitionByReference(node, argument)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is initialized by `init` to have value `assignedExpr`.
|
||||
*/
|
||||
@@ -534,8 +578,11 @@ module FlowVar_internal {
|
||||
class DataFlowSubBasicBlockCutNode extends SubBasicBlockCutNode {
|
||||
DataFlowSubBasicBlockCutNode() {
|
||||
exists(Variable v |
|
||||
not fullySupportedSsaVariable(v) and
|
||||
not fullySupportedSsaVariable(v)
|
||||
|
|
||||
assignmentLikeOperation(this, v, _)
|
||||
or
|
||||
blockVarDefinedByReference(this, v, _)
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user