mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Merge pull request #1715 from jbj/ast-field-flow
C++: Initial AST-based flow through fields
This commit is contained in:
@@ -5,6 +5,8 @@ private import DataFlowDispatch
|
||||
/** Gets the instance argument of a non-static call. */
|
||||
private Node getInstanceArgument(Call call) {
|
||||
result.asExpr() = call.getQualifier()
|
||||
or
|
||||
result.(PreConstructorCallNode).getConstructorCall() = call
|
||||
// This does not include the implicit `this` argument on auto-generated
|
||||
// base class destructor calls as those do not have an AST element.
|
||||
}
|
||||
@@ -167,7 +169,15 @@ private class ArrayContent extends Content, TArrayContent {
|
||||
* value of `node1`.
|
||||
*/
|
||||
predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
none() // stub implementation
|
||||
exists(FieldAccess fa |
|
||||
exists(Assignment a |
|
||||
node1.asExpr() = a and
|
||||
a.getLValue() = fa
|
||||
) and
|
||||
not fa.getTarget().isStatic() and
|
||||
node2.getPreUpdateNode().asExpr() = fa.getQualifier() and
|
||||
f.(FieldContent).getField() = fa.getTarget()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,7 +186,12 @@ predicate storeStep(Node node1, Content f, PostUpdateNode node2) {
|
||||
* `node2`.
|
||||
*/
|
||||
predicate readStep(Node node1, Content f, Node node2) {
|
||||
none() // stub implementation
|
||||
exists(FieldAccess fr |
|
||||
node1.asExpr() = fr.getQualifier() and
|
||||
fr.getTarget() = f.(FieldContent).getField() and
|
||||
fr = node2.asExpr() and
|
||||
not fr = any(AssignExpr a).getLValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,10 +9,10 @@ private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
cached
|
||||
private newtype TNode =
|
||||
TExprNode(Expr e) or
|
||||
TParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or
|
||||
TDefinitionByReferenceNode(VariableAccess va, Expr argument) {
|
||||
definitionByReference(va, argument)
|
||||
} or
|
||||
TPartialDefinitionNode(PartialDefinition pd) or
|
||||
TPreConstructorCallNode(ConstructorCall call) or
|
||||
TExplicitParameterNode(Parameter p) { exists(p.getFunction().getBlock()) } or
|
||||
TInstanceParameterNode(MemberFunction f) { exists(f.getBlock()) and not f.isStatic() } or
|
||||
TUninitializedNode(LocalVariable v) { not v.hasInitializer() }
|
||||
|
||||
/**
|
||||
@@ -38,11 +38,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`, and `transfer(&x, taint())` is
|
||||
* a partial definition of `&x`).
|
||||
*/
|
||||
Expr asPartialDefinition() {
|
||||
result = this.(PartialDefinitionNode).getPartialDefinition().getDefinedExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the uninitialized local variable corresponding to this node, if
|
||||
* any.
|
||||
@@ -94,14 +106,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() }
|
||||
|
||||
@@ -114,11 +134,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 }
|
||||
}
|
||||
|
||||
class ImplicitParameterNode extends ParameterNode, TInstanceParameterNode {
|
||||
MemberFunction f;
|
||||
|
||||
ImplicitParameterNode() { this = TInstanceParameterNode(f) }
|
||||
|
||||
override Function getFunction() { result = f }
|
||||
|
||||
override Type getType() { result = f.getDeclaringType() }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,12 +162,18 @@ class ParameterNode extends Node, TParameterNode {
|
||||
* `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 {
|
||||
class DefinitionByReferenceNode extends PartialDefinitionNode {
|
||||
VariableAccess va;
|
||||
|
||||
Expr argument;
|
||||
|
||||
DefinitionByReferenceNode() { this = TDefinitionByReferenceNode(va, argument) }
|
||||
DefinitionByReferenceNode() {
|
||||
exists(DefinitionByReference def |
|
||||
def = this.getPartialDefinition() and
|
||||
argument = def.getDefinedExpr() and
|
||||
va = def.getVariableAccess()
|
||||
)
|
||||
}
|
||||
|
||||
override Function getFunction() { result = va.getEnclosingFunction() }
|
||||
|
||||
@@ -190,13 +228,64 @@ class UninitializedNode extends Node, TUninitializedNode {
|
||||
* to the value before the update with the exception of `ClassInstanceExpr`,
|
||||
* which represents the value after the constructor has run.
|
||||
*/
|
||||
class PostUpdateNode extends Node {
|
||||
PostUpdateNode() { none() } // stub implementation
|
||||
|
||||
abstract class PostUpdateNode extends Node {
|
||||
/**
|
||||
* Gets the node before the state update.
|
||||
*/
|
||||
Node getPreUpdateNode() { none() } // stub implementation
|
||||
abstract Node getPreUpdateNode();
|
||||
|
||||
override Function getFunction() { result = getPreUpdateNode().getFunction() }
|
||||
|
||||
override Type getType() { result = getPreUpdateNode().getType() }
|
||||
|
||||
override Location getLocation() { result = getPreUpdateNode().getLocation() }
|
||||
}
|
||||
|
||||
class PartialDefinitionNode extends PostUpdateNode, TPartialDefinitionNode {
|
||||
PartialDefinition pd;
|
||||
|
||||
PartialDefinitionNode() { this = TPartialDefinitionNode(pd) }
|
||||
|
||||
override Node getPreUpdateNode() { result.asExpr() = pd.getDefinedExpr() }
|
||||
|
||||
override Location getLocation() { result = pd.getLocation() }
|
||||
|
||||
PartialDefinition getPartialDefinition() { result = pd }
|
||||
|
||||
override string toString() { result = getPreUpdateNode().toString() + " [post update]" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the object that was just constructed and is identified
|
||||
* with the "return value" of the constructor call.
|
||||
*/
|
||||
private class PostConstructorCallNode extends PostUpdateNode, TExprNode {
|
||||
PostConstructorCallNode() { this = TExprNode(any(ConstructorCall c)) }
|
||||
|
||||
override PreConstructorCallNode getPreUpdateNode() {
|
||||
TExprNode(result.getConstructorCall()) = this
|
||||
}
|
||||
|
||||
// No override of `toString` since these nodes already have a `toString` from
|
||||
// their overlap with `ExprNode`.
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
* A synthetic data-flow node that plays the role of the qualifier (or
|
||||
* `this`-argument) to a constructor call.
|
||||
*/
|
||||
class PreConstructorCallNode extends Node, TPreConstructorCallNode {
|
||||
ConstructorCall getConstructorCall() { this = TPreConstructorCallNode(result) }
|
||||
|
||||
override Function getFunction() { result = getConstructorCall().getEnclosingFunction() }
|
||||
|
||||
override Type getType() { result = getConstructorCall().getType() }
|
||||
|
||||
override Location getLocation() { result = getConstructorCall().getLocation() }
|
||||
|
||||
override string toString() { result = getConstructorCall().toString() + " [pre constructor call]" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +296,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
|
||||
@@ -223,6 +312,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.
|
||||
@@ -232,6 +358,8 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
// Expr -> Expr
|
||||
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
|
||||
or
|
||||
exprToExprStep_nocfg(nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr(), nodeTo.asExpr())
|
||||
or
|
||||
// Node -> FlowVar -> VariableAccess
|
||||
exists(FlowVar var |
|
||||
(
|
||||
@@ -241,13 +369,19 @@ predicate localFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
or
|
||||
varSourceBaseCase(var, nodeFrom.asUninitialized())
|
||||
or
|
||||
var.definedByReference(nodeFrom.asDefiningArgument())
|
||||
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)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -294,6 +428,18 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
|
||||
or
|
||||
toExpr = any(StmtExpr stmtExpr | fromExpr = stmtExpr.getResultExpr())
|
||||
or
|
||||
// The following case is needed to track the qualifier object for flow
|
||||
// through fields. It gives flow from `T(x)` to `new T(x)`. That's not
|
||||
// strictly _data_ flow but _taint_ flow because the type of `fromExpr` is
|
||||
// `T` while the type of `toExpr` is `T*`.
|
||||
//
|
||||
// This discrepancy is an artifact of how `new`-expressions are represented
|
||||
// in the database in a way that slightly varies from what the standard
|
||||
// specifies. In the C++ standard, there is no constructor call expression
|
||||
// `T(x)` after `new`. Instead there is a type `T` and an optional
|
||||
// initializer `(x)`.
|
||||
toExpr.(NewExpr).getInitializer() = fromExpr
|
||||
or
|
||||
toExpr = any(Call call |
|
||||
exists(DataFlowFunction f, FunctionInput inModel, FunctionOutput outModel, int iIn |
|
||||
call.getTarget() = f and
|
||||
|
||||
@@ -57,6 +57,8 @@ cached class FlowVar extends TFlowVar {
|
||||
*/
|
||||
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
|
||||
* is an exhaustive list of cases where this may happen.
|
||||
@@ -77,6 +79,74 @@ cached class FlowVar extends TFlowVar {
|
||||
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, ControlFlowNode node) {
|
||||
exists(FieldAccess fa |
|
||||
isInstanceFieldWrite(fa, node) and qualifier = fa.getQualifier()
|
||||
)
|
||||
} or
|
||||
TExplicitCallQualifier(Expr qualifier, Call call) { qualifier = call.getQualifier() } or
|
||||
TReferenceArgument(Expr arg, VariableAccess va) { definitionByReference(va, arg) }
|
||||
|
||||
private predicate isInstanceFieldWrite(FieldAccess fa, ControlFlowNode node) {
|
||||
not fa.getTarget().isStatic() and
|
||||
assignmentLikeOperation(node, fa.getTarget(), fa, _)
|
||||
}
|
||||
|
||||
class PartialDefinition extends TPartialDefinition {
|
||||
Expr definedExpr;
|
||||
ControlFlowNode node;
|
||||
|
||||
PartialDefinition() {
|
||||
this = TExplicitFieldStoreQualifier(definedExpr, node) or
|
||||
this = TExplicitCallQualifier(definedExpr, _) and node = definedExpr or
|
||||
this = TReferenceArgument(definedExpr, node)
|
||||
}
|
||||
|
||||
predicate partiallyDefines(Variable v) { definedExpr = v.getAnAccess() }
|
||||
|
||||
predicate partiallyDefinesThis(ThisExpr e) { definedExpr = e }
|
||||
|
||||
ControlFlowNode getSubBasicBlockStart() { result = node }
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
/**
|
||||
* A partial definition that's a definition by reference (in the sense of the
|
||||
* `definitionByReference` predicate).
|
||||
*/
|
||||
class DefinitionByReference extends PartialDefinition, TReferenceArgument {
|
||||
VariableAccess va;
|
||||
|
||||
DefinitionByReference() { definitionByReference(va, definedExpr) }
|
||||
|
||||
VariableAccess getVariableAccess() { result = va }
|
||||
|
||||
override predicate partiallyDefines(Variable v) { va = v.getAnAccess() }
|
||||
}
|
||||
}
|
||||
import PartialDefinitions
|
||||
private import FlowVar_internal
|
||||
|
||||
/**
|
||||
@@ -99,11 +169,8 @@ module FlowVar_internal {
|
||||
*/
|
||||
predicate fullySupportedSsaVariable(Variable v) {
|
||||
v = any(SsaDefinition def).getAVariable() and
|
||||
// After `foo(&x.field)` we need for there to be two definitions of `x`:
|
||||
// the one that existed before the call to `foo` and the def-by-ref from
|
||||
// the call. It's fundamental in SSA that each use is only associated with
|
||||
// one def, so we can't easily get this effect with SSA.
|
||||
not definitionByReference(v.getAnAccess(), _) and
|
||||
// A partially-defined variable is handled using the partial definitions logic.
|
||||
not any(PartialDefinition p).partiallyDefines(v) and
|
||||
// SSA variables do not exist before their first assignment, but one
|
||||
// feature of this data flow library is to track where uninitialized data
|
||||
// ends up.
|
||||
@@ -149,9 +216,9 @@ module FlowVar_internal {
|
||||
(
|
||||
initializer(sbb.getANode(), v, _)
|
||||
or
|
||||
assignmentLikeOperation(sbb, v, _)
|
||||
assignmentLikeOperation(sbb, v, _, _)
|
||||
or
|
||||
blockVarDefinedByReference(sbb, v, _)
|
||||
sbb = any(PartialDefinition p | p.partiallyDefines(v)).getSubBasicBlockStart()
|
||||
or
|
||||
blockVarDefinedByVariable(sbb, v)
|
||||
)
|
||||
@@ -162,6 +229,7 @@ module FlowVar_internal {
|
||||
*/
|
||||
class SsaVar extends TSsaVar, FlowVar {
|
||||
SsaDefinition def;
|
||||
|
||||
LocalScopeVariable v;
|
||||
|
||||
SsaVar() { this = TSsaVar(def, v) }
|
||||
@@ -194,6 +262,8 @@ module FlowVar_internal {
|
||||
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,12 +306,14 @@ module FlowVar_internal {
|
||||
*/
|
||||
class BlockVar extends TBlockVar, FlowVar {
|
||||
SubBasicBlock sbb;
|
||||
|
||||
Variable v;
|
||||
|
||||
BlockVar() { this = TBlockVar(sbb, v) }
|
||||
|
||||
override VariableAccess getAnAccess() {
|
||||
variableAccessInSBB(v, getAReachedBlockVarSBB(this), result)
|
||||
variableAccessInSBB(v, getAReachedBlockVarSBB(this), result) and
|
||||
result != sbb
|
||||
}
|
||||
|
||||
override predicate definedByInitialValue(LocalScopeVariable lsv) {
|
||||
@@ -250,7 +322,7 @@ 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
|
||||
@@ -258,7 +330,19 @@ module FlowVar_internal {
|
||||
}
|
||||
|
||||
override predicate definedByReference(Expr arg) {
|
||||
blockVarDefinedByReference(sbb, v, arg)
|
||||
exists(DefinitionByReference def |
|
||||
def.partiallyDefines(v) and
|
||||
sbb = def.getSubBasicBlockStart() and
|
||||
arg = def.getDefinedExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate definedPartiallyAt(Expr e) {
|
||||
exists(PartialDefinition p |
|
||||
p.partiallyDefines(v) and
|
||||
sbb = p.getSubBasicBlockStart() and
|
||||
e = p.getDefinedExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
@@ -275,10 +359,16 @@ module FlowVar_internal {
|
||||
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
|
||||
not this.definedPartiallyAt(_) and
|
||||
result = "undefined "+ v
|
||||
}
|
||||
|
||||
@@ -334,7 +424,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 +458,7 @@ module FlowVar_internal {
|
||||
reachesWithoutAssignment(bb.getAPredecessor(), v) and
|
||||
this.bbInLoop(bb)
|
||||
) and
|
||||
not assignmentLikeOperation(bb.getANode(), v, _)
|
||||
not assignmentLikeOperation(bb.getANode(), v, _, _)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -394,19 +484,59 @@ module FlowVar_internal {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// The noopt is needed to avoid getting `variableLiveInSBB` joined in before
|
||||
// `getASuccessor`.
|
||||
pragma[noopt]
|
||||
private SubBasicBlock getAReachedBlockVarSBB(TBlockVar start) {
|
||||
start = TBlockVar(result, _)
|
||||
exists(Variable v |
|
||||
start = TBlockVar(result, v) and
|
||||
variableLiveInSBB(result, v) and
|
||||
not largeVariable(v, _, _)
|
||||
)
|
||||
or
|
||||
exists(SubBasicBlock mid, SubBasicBlock sbbDef, Variable v |
|
||||
start = TBlockVar(sbbDef, v) and
|
||||
mid = getAReachedBlockVarSBB(start) and
|
||||
start = TBlockVar(sbbDef, v) and
|
||||
result = mid.getASuccessor() and
|
||||
variableLiveInSBB(result, v) and
|
||||
not skipLoop(mid, result, sbbDef, v) and
|
||||
not assignmentLikeOperation(result, v, _)
|
||||
not assignmentLikeOperation(result, v, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` may have too many combinations of definitions and reached
|
||||
* blocks for us the feasibly compute its def-use relation.
|
||||
*/
|
||||
private predicate largeVariable(Variable v, int liveBlocks, int defs) {
|
||||
liveBlocks = strictcount(SubBasicBlock sbb | variableLiveInSBB(sbb, v)) and
|
||||
defs = strictcount(SubBasicBlock sbb | exists(TBlockVar(sbb, v))) and
|
||||
liveBlocks * defs > 1000000
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a value held in `v` at the start of `sbb` (or after the first
|
||||
* assignment, if that assignment is to `v`) might later be read.
|
||||
*/
|
||||
private predicate variableLiveInSBB(SubBasicBlock sbb, Variable v) {
|
||||
variableAccessInSBB(v, sbb, _)
|
||||
or
|
||||
exists(SubBasicBlock succ | succ = sbb.getASuccessor() |
|
||||
variableLiveInSBB(succ, v) and
|
||||
not variableNotLiveBefore(succ, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if liveness of `v` should stop propagating backwards from `sbb`.
|
||||
*/
|
||||
private predicate variableNotLiveBefore(SubBasicBlock sbb, Variable v) {
|
||||
assignmentLikeOperation(sbb, v, _, _)
|
||||
or
|
||||
// Liveness of `v` is killed when going backwards from a block that declares it
|
||||
exists(DeclStmt ds | ds.getADeclaration().(LocalVariable) = v and sbb.contains(ds))
|
||||
}
|
||||
|
||||
/** Holds if `va` is a read access to `v` in `sbb`, where `v` is modeled by `BlockVar`. */
|
||||
pragma[noinline]
|
||||
private predicate variableAccessInSBB(Variable v, SubBasicBlock sbb, VariableAccess va) {
|
||||
@@ -514,22 +644,28 @@ module FlowVar_internal {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is modified as a side effect of evaluating `node`, receiving a
|
||||
* value best described by `e`. This corresponds to `FlowVar::definedByExpr`,
|
||||
* except that the case where `node instanceof Initializer` is covered by
|
||||
* `initializer` instead of this predicate.
|
||||
* Holds if `v` is modified through `va` as a side effect of evaluating
|
||||
* `node`, receiving a value best described by `assignedExpr`.
|
||||
* Assignment-like operations are those that desugar to a non-overloaded `=`
|
||||
* assignment: `=`, `+=`, `++`, `--`, etc.
|
||||
*
|
||||
* This corresponds to `FlowVar::definedByExpr`, except that the case where
|
||||
* `node instanceof Initializer` is covered by `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
|
||||
va = ae.getLValue() and
|
||||
v = va.getTarget() and
|
||||
assignedExpr = ae.getRValue()
|
||||
)
|
||||
or
|
||||
node = any(AssignOperation ao |
|
||||
v = ao.getLValue().(VariableAccess).getTarget() and
|
||||
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,
|
||||
@@ -540,16 +676,12 @@ module FlowVar_internal {
|
||||
// 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
|
||||
va = op.getOperand() and
|
||||
v = va.getTarget() and
|
||||
assignedExpr = op
|
||||
)
|
||||
}
|
||||
|
||||
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`.
|
||||
*/
|
||||
@@ -571,9 +703,9 @@ module FlowVar_internal {
|
||||
exists(Variable v |
|
||||
not fullySupportedSsaVariable(v)
|
||||
|
|
||||
assignmentLikeOperation(this, v, _)
|
||||
assignmentLikeOperation(this, v, _, _)
|
||||
or
|
||||
blockVarDefinedByReference(this, v, _)
|
||||
this = any(PartialDefinition p | p.partiallyDefines(v)).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
|
||||
|
||||
@@ -5,12 +5,15 @@
|
||||
| example.c:17:19:17:22 | {...} | example.c:24:13:24:18 | coords |
|
||||
| example.c:17:19:17:22 | {...} | example.c:26:2:26:7 | coords |
|
||||
| example.c:17:19:17:22 | {...} | example.c:26:19:26:24 | coords |
|
||||
| example.c:24:2:24:7 | coords [post update] | example.c:26:2:26:7 | coords |
|
||||
| example.c:24:2:24:7 | coords [post update] | example.c:26:19:26:24 | coords |
|
||||
| example.c:24:13:24:18 | coords [post update] | example.c:24:2:24:7 | coords |
|
||||
| example.c:24:13:24:18 | coords [post update] | example.c:26:2:26:7 | coords |
|
||||
| example.c:24:13:24:18 | coords [post update] | example.c:26:19:26:24 | coords |
|
||||
| example.c:24:13:24:30 | ... = ... | example.c:24:2:24:30 | ... = ... |
|
||||
| example.c:24:24:24:30 | ... + ... | example.c:24:13:24:30 | ... = ... |
|
||||
| example.c:26:13:26:16 | call to getX | example.c:26:2:26:25 | ... = ... |
|
||||
| example.c:26:18:26:24 | ref arg & ... | example.c:26:2:26:7 | coords |
|
||||
| example.c:26:18:26:24 | ref arg & ... | example.c:26:19:26:24 | coords |
|
||||
| example.c:28:22:28:25 | ref arg & ... | example.c:28:23:28:25 | pos |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:7:8:7:9 | t1 |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:8:8:8:9 | t1 |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:9:8:9:9 | t1 |
|
||||
@@ -39,7 +42,7 @@
|
||||
| test.cpp:431:12:431:13 | 0 | test.cpp:432:33:432:35 | tmp |
|
||||
| test.cpp:431:12:431:13 | 0 | test.cpp:433:8:433:10 | tmp |
|
||||
| test.cpp:432:10:432:13 | & ... | test.cpp:432:3:432:8 | call to memcpy |
|
||||
| test.cpp:432:10:432:13 | ref arg & ... | test.cpp:432:11:432:13 | tmp |
|
||||
| test.cpp:432:10:432:13 | ref arg & ... | test.cpp:432:3:432:8 | call to memcpy |
|
||||
| test.cpp:432:10:432:13 | ref arg & ... | test.cpp:432:33:432:35 | tmp |
|
||||
| test.cpp:432:10:432:13 | ref arg & ... | test.cpp:433:8:433:10 | tmp |
|
||||
| test.cpp:432:17:432:23 | source1 | test.cpp:432:10:432:13 | ref arg & ... |
|
||||
@@ -51,7 +54,7 @@
|
||||
| test.cpp:437:12:437:13 | 0 | test.cpp:440:8:440:10 | tmp |
|
||||
| test.cpp:437:12:437:13 | 0 | test.cpp:442:10:442:12 | tmp |
|
||||
| test.cpp:439:10:439:13 | & ... | test.cpp:439:3:439:8 | call to memcpy |
|
||||
| test.cpp:439:10:439:13 | ref arg & ... | test.cpp:439:11:439:13 | tmp |
|
||||
| test.cpp:439:10:439:13 | ref arg & ... | test.cpp:439:3:439:8 | call to memcpy |
|
||||
| test.cpp:439:10:439:13 | ref arg & ... | test.cpp:439:33:439:35 | tmp |
|
||||
| test.cpp:439:10:439:13 | ref arg & ... | test.cpp:440:8:440:10 | tmp |
|
||||
| test.cpp:439:10:439:13 | ref arg & ... | test.cpp:442:10:442:12 | tmp |
|
||||
|
||||
@@ -135,7 +135,7 @@ void following_pointers(
|
||||
|
||||
sourceStruct1_ptr->m1 = source();
|
||||
sink(sourceStruct1_ptr->m1); // flow
|
||||
sink(sourceStruct1_ptr->getFirst()); // no flow (due to limitations of the analysis)
|
||||
sink(sourceStruct1_ptr->getFirst()); // flow [NOT DETECTED with IR]
|
||||
sink(sourceStruct1_ptr->m2); // no flow
|
||||
sink(sourceStruct1.m1); // flow (due to lack of no-alias tracking)
|
||||
|
||||
@@ -410,11 +410,11 @@ class FlowThroughFields {
|
||||
int f() {
|
||||
sink(field); // tainted or clean? Not sure.
|
||||
taintField();
|
||||
sink(field); // tainted (FALSE NEGATIVE)
|
||||
sink(field); // tainted [NOT DETECTED with IR]
|
||||
}
|
||||
|
||||
int calledAfterTaint() {
|
||||
sink(field); // tainted (FALSE NEGATIVE)
|
||||
sink(field); // tainted [NOT DETECTED with IR]
|
||||
}
|
||||
|
||||
int taintAndCall() {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
| test.cpp:103:10:103:12 | ref | test.cpp:100:13:100:18 | call to source |
|
||||
| test.cpp:126:8:126:19 | sourceArray1 | test.cpp:120:9:120:20 | sourceArray1 |
|
||||
| test.cpp:137:27:137:28 | m1 | test.cpp:136:27:136:32 | call to source |
|
||||
| test.cpp:138:27:138:34 | call to getFirst | test.cpp:136:27:136:32 | call to source |
|
||||
| test.cpp:140:22:140:23 | m1 | test.cpp:136:27:136:32 | call to source |
|
||||
| test.cpp:188:8:188:8 | y | test.cpp:186:27:186:32 | call to source |
|
||||
| test.cpp:192:8:192:8 | s | test.cpp:199:33:199:38 | call to source |
|
||||
@@ -27,6 +28,8 @@
|
||||
| test.cpp:337:14:337:14 | x | test.cpp:353:17:353:22 | call to source |
|
||||
| test.cpp:366:7:366:7 | x | test.cpp:362:4:362:9 | call to source |
|
||||
| test.cpp:397:10:397:18 | globalVar | test.cpp:395:17:395:22 | call to source |
|
||||
| test.cpp:413:10:413:14 | field | test.cpp:407:13:407:18 | call to source |
|
||||
| test.cpp:417:10:417:14 | field | test.cpp:421:13:421:18 | call to source |
|
||||
| test.cpp:423:10:423:14 | field | test.cpp:421:13:421:18 | call to source |
|
||||
| test.cpp:433:8:433:10 | tmp | test.cpp:430:48:430:54 | source1 |
|
||||
| test.cpp:440:8:440:10 | tmp | test.cpp:436:53:436:59 | source1 |
|
||||
|
||||
@@ -2,10 +2,13 @@
|
||||
| test.cpp:100:13:100:18 | test.cpp:103:10:103:12 | AST only |
|
||||
| test.cpp:109:9:109:14 | test.cpp:110:10:110:12 | IR only |
|
||||
| test.cpp:136:27:136:32 | test.cpp:137:27:137:28 | AST only |
|
||||
| test.cpp:136:27:136:32 | test.cpp:138:27:138:34 | AST only |
|
||||
| test.cpp:136:27:136:32 | test.cpp:140:22:140:23 | AST only |
|
||||
| test.cpp:142:32:142:37 | test.cpp:145:10:145:11 | IR only |
|
||||
| test.cpp:151:35:151:40 | test.cpp:153:17:153:18 | IR only |
|
||||
| test.cpp:395:17:395:22 | test.cpp:397:10:397:18 | AST only |
|
||||
| test.cpp:407:13:407:18 | test.cpp:413:10:413:14 | AST only |
|
||||
| test.cpp:421:13:421:18 | test.cpp:417:10:417:14 | AST only |
|
||||
| test.cpp:421:13:421:18 | test.cpp:423:10:423:14 | AST only |
|
||||
| test.cpp:430:48:430:54 | test.cpp:433:8:433:10 | AST only |
|
||||
| test.cpp:436:53:436:59 | test.cpp:440:8:440:10 | AST only |
|
||||
|
||||
187
cpp/ql/test/library-tests/dataflow/fields/A.cpp
Normal file
187
cpp/ql/test/library-tests/dataflow/fields/A.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
class A
|
||||
{
|
||||
|
||||
class C
|
||||
{
|
||||
public:
|
||||
virtual void insert(void *) {}
|
||||
};
|
||||
class C1 : public C
|
||||
{
|
||||
public:
|
||||
A *a;
|
||||
};
|
||||
class C2 : public C
|
||||
{
|
||||
};
|
||||
|
||||
class B
|
||||
{
|
||||
public:
|
||||
C *c;
|
||||
B() {}
|
||||
B(C *c)
|
||||
{
|
||||
this->c = c;
|
||||
}
|
||||
void set(C *c) { this->c = c; }
|
||||
C *get() { return this->c; }
|
||||
static B *make(C *c)
|
||||
{
|
||||
return new B(c);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
void f0()
|
||||
{
|
||||
C cc;
|
||||
C ct;
|
||||
cc.insert(nullptr);
|
||||
ct.insert(new C());
|
||||
sink(&cc); // no flow
|
||||
sink(&ct); // flow
|
||||
}
|
||||
void f1()
|
||||
{
|
||||
C *c = new C();
|
||||
B *b = B::make(c);
|
||||
sink(b->c); // flow
|
||||
}
|
||||
|
||||
void f2()
|
||||
{
|
||||
B *b = new B();
|
||||
b->set(new C1());
|
||||
sink(b->get()); // flow
|
||||
sink((new B(new C()))->get()); // flow
|
||||
}
|
||||
|
||||
void f3()
|
||||
{
|
||||
B *b1 = new B();
|
||||
B *b2;
|
||||
b2 = setOnB(b1, new C2());
|
||||
sink(b1->c); // no flow
|
||||
sink(b2->c); // flow
|
||||
}
|
||||
|
||||
void f4()
|
||||
{
|
||||
B *b1 = new B();
|
||||
B *b2;
|
||||
b2 = setOnBWrap(b1, new C2());
|
||||
sink(b1->c); // no flow
|
||||
sink(b2->c); // flow
|
||||
}
|
||||
|
||||
B *setOnBWrap(B *b1, C *c)
|
||||
{
|
||||
B *b2;
|
||||
b2 = setOnB(b1, c);
|
||||
return r() ? b1 : b2;
|
||||
}
|
||||
|
||||
A::B *setOnB(B *b1, C *c)
|
||||
{
|
||||
if (r())
|
||||
{
|
||||
B *b2 = new B();
|
||||
b2->set(c);
|
||||
return b2;
|
||||
}
|
||||
return b1;
|
||||
}
|
||||
|
||||
void f5()
|
||||
{
|
||||
A *a = new A();
|
||||
C1 *c1 = new C1();
|
||||
c1->a = a;
|
||||
f6(c1);
|
||||
}
|
||||
void f6(C *c)
|
||||
{
|
||||
if (C1 *c1 = dynamic_cast<C1 *>(c))
|
||||
{
|
||||
sink(c1->a); // flow
|
||||
}
|
||||
C *cc;
|
||||
if (C2 *c2 = dynamic_cast<C2 *>(c))
|
||||
{
|
||||
cc = c2;
|
||||
}
|
||||
else
|
||||
{
|
||||
cc = new C1();
|
||||
}
|
||||
if (C1 *c1 = dynamic_cast<C1 *>(cc))
|
||||
{
|
||||
sink(c1->a); // no flow, stopped by cast to C2
|
||||
}
|
||||
}
|
||||
|
||||
void f7(B *b)
|
||||
{
|
||||
b->set(new C());
|
||||
}
|
||||
void f8()
|
||||
{
|
||||
B *b = new B();
|
||||
f7(b);
|
||||
sink(b->c); // flow
|
||||
}
|
||||
|
||||
class D
|
||||
{
|
||||
public:
|
||||
A::B *b;
|
||||
|
||||
D(A::B *b, bool x)
|
||||
{
|
||||
b->c = new C();
|
||||
this->b = x ? b : new B();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
f9()
|
||||
{
|
||||
B *b = new B();
|
||||
D *d = new D(b, r());
|
||||
sink(d->b); // flow x2
|
||||
sink(d->b->c); // flow
|
||||
sink(b->c); // flow
|
||||
}
|
||||
|
||||
void f10()
|
||||
{
|
||||
B *b = new B();
|
||||
MyList *l1 = new MyList(b, new MyList(nullptr, nullptr));
|
||||
MyList *l2 = new MyList(nullptr, l1);
|
||||
MyList *l3 = new MyList(nullptr, l2);
|
||||
sink(l3->head); // no flow, b is nested beneath at least one ->next
|
||||
sink(l3->next->head); // flow, the precise nesting depth isn't tracked
|
||||
sink(l3->next->next->head); // flow
|
||||
sink(l3->next->next->next->head); // flow
|
||||
for (MyList *l = l3; l != nullptr; l = l->next)
|
||||
{
|
||||
sink(l->head); // flow
|
||||
}
|
||||
}
|
||||
|
||||
static void sink(void *o) {}
|
||||
bool r() { return reinterpret_cast<long long>(this) % 10 > 5; }
|
||||
|
||||
class MyList
|
||||
{
|
||||
public:
|
||||
B *head;
|
||||
MyList *next;
|
||||
MyList(B *newHead, MyList *next)
|
||||
{
|
||||
head = newHead;
|
||||
this->next = next;
|
||||
}
|
||||
};
|
||||
};
|
||||
49
cpp/ql/test/library-tests/dataflow/fields/B.cpp
Normal file
49
cpp/ql/test/library-tests/dataflow/fields/B.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
class B
|
||||
{
|
||||
|
||||
void f1()
|
||||
{
|
||||
Elem *e = new Elem();
|
||||
Box1 *b1 = new Box1(e, nullptr);
|
||||
Box2 *b2 = new Box2(b1);
|
||||
sink(b2->box1->elem1); // flow
|
||||
sink(b2->box1->elem2); // FP due to flow in f2 below
|
||||
}
|
||||
|
||||
void f2()
|
||||
{
|
||||
Elem *e = new B::Elem();
|
||||
Box1 *b1 = new B::Box1(nullptr, e);
|
||||
Box2 *b2 = new Box2(b1);
|
||||
sink(b2->box1->elem1); // FP due to flow in f1 above
|
||||
sink(b2->box1->elem2); // flow
|
||||
}
|
||||
|
||||
static void sink(void *o) {}
|
||||
|
||||
class Elem
|
||||
{
|
||||
};
|
||||
|
||||
class Box1
|
||||
{
|
||||
public:
|
||||
Elem *elem1;
|
||||
Elem *elem2;
|
||||
Box1(Elem *e1, Elem *e2)
|
||||
{
|
||||
this->elem1 = e1;
|
||||
this->elem2 = e2;
|
||||
}
|
||||
};
|
||||
|
||||
class Box2
|
||||
{
|
||||
public:
|
||||
Box1 *box1;
|
||||
Box2(Box1 *b1)
|
||||
{
|
||||
this->box1 = b1;
|
||||
}
|
||||
};
|
||||
};
|
||||
37
cpp/ql/test/library-tests/dataflow/fields/C.cpp
Normal file
37
cpp/ql/test/library-tests/dataflow/fields/C.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
class C
|
||||
{
|
||||
class Elem
|
||||
{
|
||||
};
|
||||
|
||||
private:
|
||||
Elem *s1 = new Elem();
|
||||
const Elem *s2 = new Elem();
|
||||
Elem *s3;
|
||||
|
||||
public:
|
||||
const static Elem *s4;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
C *c = new C();
|
||||
c->func();
|
||||
}
|
||||
|
||||
C() : s1(new Elem())
|
||||
{
|
||||
this->s3 = new Elem();
|
||||
}
|
||||
|
||||
void func()
|
||||
{
|
||||
sink(s1);
|
||||
sink(s2);
|
||||
sink(s3);
|
||||
sink(s4);
|
||||
}
|
||||
|
||||
static void sink(const void *o) {}
|
||||
};
|
||||
const C::Elem *C::s4 = new Elem();
|
||||
64
cpp/ql/test/library-tests/dataflow/fields/complex.cpp
Normal file
64
cpp/ql/test/library-tests/dataflow/fields/complex.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
namespace Complex
|
||||
{
|
||||
class Foo
|
||||
{
|
||||
int a_;
|
||||
int b_;
|
||||
|
||||
public:
|
||||
int a() { return a_; }
|
||||
int b() { return b_; }
|
||||
void setA(int a) { a_ = a; }
|
||||
void setB(int b) { b_ = b; }
|
||||
|
||||
Foo(int a, int b) : a_(a), b_(b){};
|
||||
};
|
||||
|
||||
class Bar
|
||||
{
|
||||
public:
|
||||
Foo f;
|
||||
|
||||
Bar() : f(0, 0) {}
|
||||
};
|
||||
|
||||
int user_input()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
void sink(int x)
|
||||
{
|
||||
}
|
||||
|
||||
void bar(Bar &b)
|
||||
{
|
||||
sink(b.f.a()); // flow (through `b1.f.setA` and `b3.f.setA`) [NOT DETECTED]
|
||||
sink(b.f.b()); // flow (through `b2.f.setB` and `b3.f.setB`) [NOT DETECTED]
|
||||
}
|
||||
|
||||
void foo()
|
||||
{
|
||||
Bar b1;
|
||||
Bar b2;
|
||||
Bar b3;
|
||||
Bar b4;
|
||||
|
||||
b1.f.setA(user_input());
|
||||
b2.f.setB(user_input());
|
||||
b3.f.setA(user_input());
|
||||
b3.f.setB(user_input());
|
||||
|
||||
// Only a() should alert
|
||||
bar(b1);
|
||||
|
||||
// Only b() should alert
|
||||
bar(b2);
|
||||
|
||||
// Both a() and b() should alert
|
||||
bar(b3);
|
||||
|
||||
// Nothing should alert
|
||||
bar(b4);
|
||||
}
|
||||
}; // namespace Complex
|
||||
51
cpp/ql/test/library-tests/dataflow/fields/constructors.cpp
Normal file
51
cpp/ql/test/library-tests/dataflow/fields/constructors.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
namespace Constructors
|
||||
{
|
||||
int user_input()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
void sink(int x)
|
||||
{
|
||||
}
|
||||
|
||||
class Foo
|
||||
{
|
||||
int a_;
|
||||
int b_;
|
||||
|
||||
public:
|
||||
int a() { return a_; }
|
||||
int b() { return b_; }
|
||||
void setA(int a) { a_ = a; }
|
||||
void setB(int b) { b_ = b; }
|
||||
|
||||
Foo(int a, int b) : a_(a), b_(b){};
|
||||
};
|
||||
|
||||
void bar(Foo &f)
|
||||
{
|
||||
sink(f.a()); // flow (through `f` and `h`) [NOT DETECTED]
|
||||
sink(f.b()); // flow (through `g` and `h`) [NOT DETECTED]
|
||||
}
|
||||
|
||||
void foo()
|
||||
{
|
||||
Foo f(user_input(), 0);
|
||||
Foo g(0, user_input());
|
||||
Foo h(user_input(), user_input());
|
||||
Foo i(0, 0);
|
||||
|
||||
// Only a() should alert
|
||||
bar(f);
|
||||
|
||||
// Only b() should alert
|
||||
bar(g);
|
||||
|
||||
// Both a() and b() should alert
|
||||
bar(h);
|
||||
|
||||
// Nothing should alert
|
||||
bar(i);
|
||||
}
|
||||
}; // namespace Constructors
|
||||
123
cpp/ql/test/library-tests/dataflow/fields/flow.expected
Normal file
123
cpp/ql/test/library-tests/dataflow/fields/flow.expected
Normal file
@@ -0,0 +1,123 @@
|
||||
edges
|
||||
| A.cpp:41:15:41:21 | new [void] | A.cpp:43:10:43:12 | & ... |
|
||||
| A.cpp:47:12:47:18 | new [void] | A.cpp:48:20:48:20 | c [void] |
|
||||
| A.cpp:48:12:48:18 | call to make [c, ... (1)] | A.cpp:49:10:49:10 | b [c, ... (1)] |
|
||||
| A.cpp:48:20:48:20 | c [void] | A.cpp:48:12:48:18 | call to make [c, ... (1)] |
|
||||
| A.cpp:49:10:49:10 | b [c, ... (1)] | A.cpp:49:13:49:13 | c |
|
||||
| A.cpp:55:5:55:5 | b [post update] [c, ... (1)] | A.cpp:56:10:56:10 | b [c, ... (1)] |
|
||||
| A.cpp:55:12:55:19 | new [void] | A.cpp:55:5:55:5 | b [post update] [c, ... (1)] |
|
||||
| A.cpp:56:10:56:10 | b [c, ... (1)] | A.cpp:56:13:56:15 | call to get |
|
||||
| A.cpp:57:11:57:24 | call to B [c, ... (1)] | A.cpp:57:11:57:24 | new [c, ... (1)] |
|
||||
| A.cpp:57:11:57:24 | new [c, ... (1)] | A.cpp:57:28:57:30 | call to get |
|
||||
| A.cpp:57:17:57:23 | new [void] | A.cpp:57:11:57:24 | call to B [c, ... (1)] |
|
||||
| A.cpp:64:10:64:15 | call to setOnB [c, ... (1)] | A.cpp:66:10:66:11 | b2 [c, ... (1)] |
|
||||
| A.cpp:64:21:64:28 | new [void] | A.cpp:64:10:64:15 | call to setOnB [c, ... (1)] |
|
||||
| A.cpp:66:10:66:11 | b2 [c, ... (1)] | A.cpp:66:14:66:14 | c |
|
||||
| A.cpp:73:10:73:19 | call to setOnBWrap [c, ... (1)] | A.cpp:75:10:75:11 | b2 [c, ... (1)] |
|
||||
| A.cpp:73:25:73:32 | new [void] | A.cpp:73:10:73:19 | call to setOnBWrap [c, ... (1)] |
|
||||
| A.cpp:75:10:75:11 | b2 [c, ... (1)] | A.cpp:75:14:75:14 | c |
|
||||
| A.cpp:98:12:98:18 | new [void] | A.cpp:100:5:100:13 | ... = ... [void] |
|
||||
| A.cpp:100:5:100:6 | c1 [post update] [a, ... (1)] | A.cpp:101:8:101:9 | c1 [a, ... (1)] |
|
||||
| A.cpp:100:5:100:13 | ... = ... [void] | A.cpp:100:5:100:6 | c1 [post update] [a, ... (1)] |
|
||||
| A.cpp:101:8:101:9 | c1 [a, ... (1)] | A.cpp:103:14:103:14 | c [a, ... (1)] |
|
||||
| A.cpp:103:14:103:14 | c [a, ... (1)] | A.cpp:107:12:107:13 | c1 [a, ... (1)] |
|
||||
| A.cpp:103:14:103:14 | c [a, ... (1)] | A.cpp:120:12:120:13 | c1 [a, ... (1)] |
|
||||
| A.cpp:107:12:107:13 | c1 [a, ... (1)] | A.cpp:107:16:107:16 | a |
|
||||
| A.cpp:120:12:120:13 | c1 [a, ... (1)] | A.cpp:120:16:120:16 | a |
|
||||
| A.cpp:142:7:142:7 | b [post update] [c, ... (1)] | A.cpp:143:7:143:31 | ... = ... [c, ... (1)] |
|
||||
| A.cpp:142:7:142:20 | ... = ... [void] | A.cpp:142:7:142:7 | b [post update] [c, ... (1)] |
|
||||
| A.cpp:142:14:142:20 | new [void] | A.cpp:142:7:142:20 | ... = ... [void] |
|
||||
| A.cpp:143:7:143:10 | this [post update] [b, ... (1)] | A.cpp:151:12:151:24 | call to D [b, ... (1)] |
|
||||
| A.cpp:143:7:143:10 | this [post update] [b, ... (2)] | A.cpp:151:12:151:24 | call to D [b, ... (2)] |
|
||||
| A.cpp:143:7:143:31 | ... = ... [c, ... (1)] | A.cpp:143:7:143:10 | this [post update] [b, ... (2)] |
|
||||
| A.cpp:143:7:143:31 | ... = ... [void] | A.cpp:143:7:143:10 | this [post update] [b, ... (1)] |
|
||||
| A.cpp:143:25:143:31 | new [void] | A.cpp:143:7:143:31 | ... = ... [void] |
|
||||
| A.cpp:150:12:150:18 | new [void] | A.cpp:151:18:151:18 | b [void] |
|
||||
| A.cpp:151:12:151:24 | call to D [b, ... (1)] | A.cpp:152:10:152:10 | d [b, ... (1)] |
|
||||
| A.cpp:151:12:151:24 | call to D [b, ... (2)] | A.cpp:153:10:153:10 | d [b, ... (2)] |
|
||||
| A.cpp:151:18:151:18 | b [void] | A.cpp:151:12:151:24 | call to D [b, ... (1)] |
|
||||
| A.cpp:152:10:152:10 | d [b, ... (1)] | A.cpp:152:13:152:13 | b |
|
||||
| A.cpp:153:10:153:10 | d [b, ... (2)] | A.cpp:153:13:153:13 | b [c, ... (1)] |
|
||||
| A.cpp:153:13:153:13 | b [c, ... (1)] | A.cpp:153:16:153:16 | c |
|
||||
| A.cpp:159:12:159:18 | new [void] | A.cpp:160:29:160:29 | b [void] |
|
||||
| A.cpp:160:18:160:60 | call to MyList [head, ... (1)] | A.cpp:161:38:161:39 | l1 [head, ... (1)] |
|
||||
| A.cpp:160:29:160:29 | b [void] | A.cpp:160:18:160:60 | call to MyList [head, ... (1)] |
|
||||
| A.cpp:161:18:161:40 | call to MyList [next, ... (2)] | A.cpp:162:38:162:39 | l2 [next, ... (2)] |
|
||||
| A.cpp:161:38:161:39 | l1 [head, ... (1)] | A.cpp:161:18:161:40 | call to MyList [next, ... (2)] |
|
||||
| A.cpp:162:18:162:40 | call to MyList [next, ... (3)] | A.cpp:165:10:165:11 | l3 [next, ... (3)] |
|
||||
| A.cpp:162:18:162:40 | call to MyList [next, ... (3)] | A.cpp:167:44:167:44 | l [next, ... (3)] |
|
||||
| A.cpp:162:38:162:39 | l2 [next, ... (2)] | A.cpp:162:18:162:40 | call to MyList [next, ... (3)] |
|
||||
| A.cpp:165:10:165:11 | l3 [next, ... (3)] | A.cpp:165:14:165:17 | next [next, ... (2)] |
|
||||
| A.cpp:165:14:165:17 | next [next, ... (2)] | A.cpp:165:20:165:23 | next [head, ... (1)] |
|
||||
| A.cpp:165:20:165:23 | next [head, ... (1)] | A.cpp:165:26:165:29 | head |
|
||||
| A.cpp:167:44:167:44 | l [next, ... (2)] | A.cpp:167:47:167:50 | next [head, ... (1)] |
|
||||
| A.cpp:167:44:167:44 | l [next, ... (3)] | A.cpp:167:47:167:50 | next [next, ... (2)] |
|
||||
| A.cpp:167:47:167:50 | next [head, ... (1)] | A.cpp:169:12:169:12 | l [head, ... (1)] |
|
||||
| A.cpp:167:47:167:50 | next [next, ... (2)] | A.cpp:167:44:167:44 | l [next, ... (2)] |
|
||||
| A.cpp:169:12:169:12 | l [head, ... (1)] | A.cpp:169:15:169:18 | head |
|
||||
| B.cpp:6:15:6:24 | new [void] | B.cpp:7:25:7:25 | e [void] |
|
||||
| B.cpp:7:16:7:35 | call to Box1 [elem1, ... (1)] | B.cpp:8:25:8:26 | b1 [elem1, ... (1)] |
|
||||
| B.cpp:7:25:7:25 | e [void] | B.cpp:7:16:7:35 | call to Box1 [elem1, ... (1)] |
|
||||
| B.cpp:8:16:8:27 | call to Box2 [box1, ... (2)] | B.cpp:9:10:9:11 | b2 [box1, ... (2)] |
|
||||
| B.cpp:8:16:8:27 | call to Box2 [box1, ... (2)] | B.cpp:10:10:10:11 | b2 [box1, ... (2)] |
|
||||
| B.cpp:8:25:8:26 | b1 [elem1, ... (1)] | B.cpp:8:16:8:27 | call to Box2 [box1, ... (2)] |
|
||||
| B.cpp:9:10:9:11 | b2 [box1, ... (2)] | B.cpp:9:14:9:17 | box1 [elem1, ... (1)] |
|
||||
| B.cpp:9:14:9:17 | box1 [elem1, ... (1)] | B.cpp:9:20:9:24 | elem1 |
|
||||
| B.cpp:10:10:10:11 | b2 [box1, ... (2)] | B.cpp:10:14:10:17 | box1 [elem2, ... (1)] |
|
||||
| B.cpp:10:14:10:17 | box1 [elem2, ... (1)] | B.cpp:10:20:10:24 | elem2 |
|
||||
| B.cpp:15:15:15:27 | new [void] | B.cpp:16:37:16:37 | e [void] |
|
||||
| B.cpp:16:16:16:38 | call to Box1 [elem2, ... (1)] | B.cpp:17:25:17:26 | b1 [elem2, ... (1)] |
|
||||
| B.cpp:16:37:16:37 | e [void] | B.cpp:16:16:16:38 | call to Box1 [elem2, ... (1)] |
|
||||
| B.cpp:17:16:17:27 | call to Box2 [box1, ... (2)] | B.cpp:18:10:18:11 | b2 [box1, ... (2)] |
|
||||
| B.cpp:17:16:17:27 | call to Box2 [box1, ... (2)] | B.cpp:19:10:19:11 | b2 [box1, ... (2)] |
|
||||
| B.cpp:17:25:17:26 | b1 [elem2, ... (1)] | B.cpp:17:16:17:27 | call to Box2 [box1, ... (2)] |
|
||||
| B.cpp:18:10:18:11 | b2 [box1, ... (2)] | B.cpp:18:14:18:17 | box1 [elem1, ... (1)] |
|
||||
| B.cpp:18:14:18:17 | box1 [elem1, ... (1)] | B.cpp:18:20:18:24 | elem1 |
|
||||
| B.cpp:19:10:19:11 | b2 [box1, ... (2)] | B.cpp:19:14:19:17 | box1 [elem2, ... (1)] |
|
||||
| B.cpp:19:14:19:17 | box1 [elem2, ... (1)] | B.cpp:19:20:19:24 | elem2 |
|
||||
| C.cpp:18:12:18:18 | call to C [s3, ... (1)] | C.cpp:19:5:19:5 | c [s3, ... (1)] |
|
||||
| C.cpp:19:5:19:5 | c [s3, ... (1)] | C.cpp:27:8:27:11 | `this` parameter in func [s3, ... (1)] |
|
||||
| C.cpp:24:5:24:8 | this [post update] [s3, ... (1)] | C.cpp:18:12:18:18 | call to C [s3, ... (1)] |
|
||||
| C.cpp:24:5:24:25 | ... = ... [void] | C.cpp:24:5:24:8 | this [post update] [s3, ... (1)] |
|
||||
| C.cpp:24:16:24:25 | new [void] | C.cpp:24:5:24:25 | ... = ... [void] |
|
||||
| C.cpp:27:8:27:11 | `this` parameter in func [s3, ... (1)] | file://:0:0:0:0 | this [s3, ... (1)] |
|
||||
| file://:0:0:0:0 | this [s3, ... (1)] | C.cpp:31:10:31:11 | s3 |
|
||||
| simple.cpp:26:15:26:15 | f [a_, ... (1)] | simple.cpp:28:10:28:10 | f [a_, ... (1)] |
|
||||
| simple.cpp:26:15:26:15 | f [b_, ... (1)] | simple.cpp:29:10:29:10 | f [b_, ... (1)] |
|
||||
| simple.cpp:28:10:28:10 | f [a_, ... (1)] | simple.cpp:28:12:28:12 | call to a |
|
||||
| simple.cpp:29:10:29:10 | f [b_, ... (1)] | simple.cpp:29:12:29:12 | call to b |
|
||||
| simple.cpp:39:5:39:5 | f [post update] [a_, ... (1)] | simple.cpp:45:9:45:9 | f [a_, ... (1)] |
|
||||
| simple.cpp:39:12:39:21 | call to user_input [void] | simple.cpp:39:5:39:5 | f [post update] [a_, ... (1)] |
|
||||
| simple.cpp:40:5:40:5 | g [post update] [b_, ... (1)] | simple.cpp:48:9:48:9 | g [b_, ... (1)] |
|
||||
| simple.cpp:40:12:40:21 | call to user_input [void] | simple.cpp:40:5:40:5 | g [post update] [b_, ... (1)] |
|
||||
| simple.cpp:41:5:41:5 | h [post update] [a_, ... (1)] | simple.cpp:51:9:51:9 | h [a_, ... (1)] |
|
||||
| simple.cpp:41:12:41:21 | call to user_input [void] | simple.cpp:41:5:41:5 | h [post update] [a_, ... (1)] |
|
||||
| simple.cpp:42:5:42:5 | h [post update] [b_, ... (1)] | simple.cpp:51:9:51:9 | h [b_, ... (1)] |
|
||||
| simple.cpp:42:12:42:21 | call to user_input [void] | simple.cpp:42:5:42:5 | h [post update] [b_, ... (1)] |
|
||||
| simple.cpp:45:9:45:9 | f [a_, ... (1)] | simple.cpp:26:15:26:15 | f [a_, ... (1)] |
|
||||
| simple.cpp:48:9:48:9 | g [b_, ... (1)] | simple.cpp:26:15:26:15 | f [b_, ... (1)] |
|
||||
| simple.cpp:51:9:51:9 | h [a_, ... (1)] | simple.cpp:26:15:26:15 | f [a_, ... (1)] |
|
||||
| simple.cpp:51:9:51:9 | h [b_, ... (1)] | simple.cpp:26:15:26:15 | f [b_, ... (1)] |
|
||||
#select
|
||||
| A.cpp:43:10:43:12 | & ... | A.cpp:41:15:41:21 | new [void] | A.cpp:43:10:43:12 | & ... | & ... flows from $@ | A.cpp:41:15:41:21 | new [void] | new [void] |
|
||||
| A.cpp:49:13:49:13 | c | A.cpp:47:12:47:18 | new [void] | A.cpp:49:13:49:13 | c | c flows from $@ | A.cpp:47:12:47:18 | new [void] | new [void] |
|
||||
| A.cpp:56:13:56:15 | call to get | A.cpp:55:12:55:19 | new [void] | A.cpp:56:13:56:15 | call to get | call to get flows from $@ | A.cpp:55:12:55:19 | new [void] | new [void] |
|
||||
| A.cpp:57:28:57:30 | call to get | A.cpp:57:17:57:23 | new [void] | A.cpp:57:28:57:30 | call to get | call to get flows from $@ | A.cpp:57:17:57:23 | new [void] | new [void] |
|
||||
| A.cpp:66:14:66:14 | c | A.cpp:64:21:64:28 | new [void] | A.cpp:66:14:66:14 | c | c flows from $@ | A.cpp:64:21:64:28 | new [void] | new [void] |
|
||||
| A.cpp:75:14:75:14 | c | A.cpp:73:25:73:32 | new [void] | A.cpp:75:14:75:14 | c | c flows from $@ | A.cpp:73:25:73:32 | new [void] | new [void] |
|
||||
| A.cpp:107:16:107:16 | a | A.cpp:98:12:98:18 | new [void] | A.cpp:107:16:107:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new [void] | new [void] |
|
||||
| A.cpp:120:16:120:16 | a | A.cpp:98:12:98:18 | new [void] | A.cpp:120:16:120:16 | a | a flows from $@ | A.cpp:98:12:98:18 | new [void] | new [void] |
|
||||
| A.cpp:152:13:152:13 | b | A.cpp:143:25:143:31 | new [void] | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:143:25:143:31 | new [void] | new [void] |
|
||||
| A.cpp:152:13:152:13 | b | A.cpp:150:12:150:18 | new [void] | A.cpp:152:13:152:13 | b | b flows from $@ | A.cpp:150:12:150:18 | new [void] | new [void] |
|
||||
| A.cpp:153:16:153:16 | c | A.cpp:142:14:142:20 | new [void] | A.cpp:153:16:153:16 | c | c flows from $@ | A.cpp:142:14:142:20 | new [void] | new [void] |
|
||||
| A.cpp:165:26:165:29 | head | A.cpp:159:12:159:18 | new [void] | A.cpp:165:26:165:29 | head | head flows from $@ | A.cpp:159:12:159:18 | new [void] | new [void] |
|
||||
| A.cpp:169:15:169:18 | head | A.cpp:159:12:159:18 | new [void] | A.cpp:169:15:169:18 | head | head flows from $@ | A.cpp:159:12:159:18 | new [void] | new [void] |
|
||||
| B.cpp:9:20:9:24 | elem1 | B.cpp:6:15:6:24 | new [void] | B.cpp:9:20:9:24 | elem1 | elem1 flows from $@ | B.cpp:6:15:6:24 | new [void] | new [void] |
|
||||
| B.cpp:10:20:10:24 | elem2 | B.cpp:6:15:6:24 | new [void] | B.cpp:10:20:10:24 | elem2 | elem2 flows from $@ | B.cpp:6:15:6:24 | new [void] | new [void] |
|
||||
| B.cpp:18:20:18:24 | elem1 | B.cpp:15:15:15:27 | new [void] | B.cpp:18:20:18:24 | elem1 | elem1 flows from $@ | B.cpp:15:15:15:27 | new [void] | new [void] |
|
||||
| B.cpp:19:20:19:24 | elem2 | B.cpp:15:15:15:27 | new [void] | B.cpp:19:20:19:24 | elem2 | elem2 flows from $@ | B.cpp:15:15:15:27 | new [void] | new [void] |
|
||||
| C.cpp:31:10:31:11 | s3 | C.cpp:24:16:24:25 | new [void] | C.cpp:31:10:31:11 | s3 | s3 flows from $@ | C.cpp:24:16:24:25 | new [void] | new [void] |
|
||||
| simple.cpp:28:12:28:12 | call to a | simple.cpp:39:12:39:21 | call to user_input [void] | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:39:12:39:21 | call to user_input [void] | call to user_input [void] |
|
||||
| simple.cpp:28:12:28:12 | call to a | simple.cpp:41:12:41:21 | call to user_input [void] | simple.cpp:28:12:28:12 | call to a | call to a flows from $@ | simple.cpp:41:12:41:21 | call to user_input [void] | call to user_input [void] |
|
||||
| simple.cpp:29:12:29:12 | call to b | simple.cpp:40:12:40:21 | call to user_input [void] | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:40:12:40:21 | call to user_input [void] | call to user_input [void] |
|
||||
| simple.cpp:29:12:29:12 | call to b | simple.cpp:42:12:42:21 | call to user_input [void] | simple.cpp:29:12:29:12 | call to b | call to b flows from $@ | simple.cpp:42:12:42:21 | call to user_input [void] | call to user_input [void] |
|
||||
37
cpp/ql/test/library-tests/dataflow/fields/flow.ql
Normal file
37
cpp/ql/test/library-tests/dataflow/fields/flow.ql
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* @kind path-problem
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import DataFlow::PathGraph
|
||||
import DataFlow
|
||||
import cpp
|
||||
|
||||
class Conf extends Configuration {
|
||||
Conf() { this = "FieldFlowConf" }
|
||||
|
||||
override predicate isSource(Node src) {
|
||||
src.asExpr() instanceof NewExpr
|
||||
or
|
||||
src.asExpr().(Call).getTarget().hasName("user_input")
|
||||
}
|
||||
|
||||
override predicate isSink(Node sink) {
|
||||
exists(Call c |
|
||||
c.getTarget().hasName("sink") and
|
||||
c.getAnArgument() = sink.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(Node a, Node b) {
|
||||
b.asPartialDefinition() = any(Call c |
|
||||
c.getTarget().hasName("insert") and c.getAnArgument() = a.asExpr()
|
||||
).getQualifier()
|
||||
or
|
||||
b.asExpr().(AddressOfExpr).getOperand() = a.asExpr()
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode src, DataFlow::PathNode sink, Conf conf
|
||||
where conf.hasFlowPath(src, sink)
|
||||
select sink, src, sink, sink + " flows from $@", src, src.toString()
|
||||
56
cpp/ql/test/library-tests/dataflow/fields/simple.cpp
Normal file
56
cpp/ql/test/library-tests/dataflow/fields/simple.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
namespace Simple
|
||||
{
|
||||
int user_input()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
void sink(int x)
|
||||
{
|
||||
}
|
||||
|
||||
class Foo
|
||||
{
|
||||
int a_;
|
||||
int b_;
|
||||
|
||||
public:
|
||||
int a() { return a_; }
|
||||
int b() { return b_; }
|
||||
void setA(int a) { a_ = a; }
|
||||
void setB(int b) { b_ = b; }
|
||||
|
||||
Foo(int a, int b) : a_(a), b_(b){};
|
||||
};
|
||||
|
||||
void bar(Foo &f)
|
||||
{
|
||||
sink(f.a()); // flow (through `f.setA` and `h.setA`)
|
||||
sink(f.b()); // flow (through `g.setB` and `h.setB`)
|
||||
}
|
||||
|
||||
void foo()
|
||||
{
|
||||
Foo f(0, 0);
|
||||
Foo g(0, 0);
|
||||
Foo h(0, 0);
|
||||
Foo i(0, 0);
|
||||
|
||||
f.setA(user_input());
|
||||
g.setB(user_input());
|
||||
h.setA(user_input());
|
||||
h.setB(user_input());
|
||||
|
||||
// Only a() should alert
|
||||
bar(f);
|
||||
|
||||
// Only b() should alert
|
||||
bar(g);
|
||||
|
||||
// Both a() and b() should alert
|
||||
bar(h);
|
||||
|
||||
// Nothing should alert
|
||||
bar(i);
|
||||
}
|
||||
} // namespace Simple
|
||||
@@ -1,3 +1,4 @@
|
||||
| file://:0:0:0:0 | this | file://:0:0:0:0 | this | |
|
||||
| file://:0:0:0:0 | this | taint.cpp:72:3:72:3 | c | TAINT |
|
||||
| file://:0:0:0:0 | this | taint.cpp:73:3:73:3 | d | TAINT |
|
||||
| file://:0:0:0:0 | this | taint.cpp:77:3:77:3 | d | TAINT |
|
||||
@@ -43,10 +44,13 @@
|
||||
| taint.cpp:37:12:37:20 | call to increment | taint.cpp:43:7:43:13 | global9 | |
|
||||
| taint.cpp:38:13:38:16 | call to zero | taint.cpp:38:2:38:26 | ... = ... | |
|
||||
| taint.cpp:38:13:38:16 | call to zero | taint.cpp:44:7:44:14 | global10 | |
|
||||
| taint.cpp:71:2:71:8 | `this` parameter in MyClass | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:71:14:71:17 | 0 | taint.cpp:71:14:71:17 | constructor init of field a | TAINT |
|
||||
| taint.cpp:71:22:71:27 | call to source | taint.cpp:71:20:71:30 | constructor init of field b | TAINT |
|
||||
| taint.cpp:72:3:72:3 | this [post update] | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:72:7:72:12 | call to source | taint.cpp:72:3:72:14 | ... = ... | |
|
||||
| taint.cpp:73:7:73:7 | 0 | taint.cpp:73:3:73:7 | ... = ... | |
|
||||
| taint.cpp:76:7:76:14 | `this` parameter in myMethod | file://:0:0:0:0 | this | |
|
||||
| taint.cpp:77:7:77:12 | call to source | taint.cpp:77:3:77:14 | ... = ... | |
|
||||
| taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:86:2:86:4 | mc1 | |
|
||||
| taint.cpp:84:10:84:12 | call to MyClass | taint.cpp:88:7:88:9 | mc1 | |
|
||||
@@ -57,6 +61,10 @@
|
||||
| taint.cpp:84:15:84:17 | call to MyClass | taint.cpp:93:7:93:9 | mc2 | |
|
||||
| taint.cpp:84:15:84:17 | call to MyClass | taint.cpp:94:7:94:9 | mc2 | |
|
||||
| taint.cpp:84:15:84:17 | call to MyClass | taint.cpp:95:7:95:9 | mc2 | |
|
||||
| taint.cpp:86:2:86:4 | mc1 [post update] | taint.cpp:88:7:88:9 | mc1 | |
|
||||
| taint.cpp:86:2:86:4 | mc1 [post update] | taint.cpp:89:7:89:9 | mc1 | |
|
||||
| taint.cpp:86:2:86:4 | mc1 [post update] | taint.cpp:90:7:90:9 | mc1 | |
|
||||
| taint.cpp:86:2:86:4 | mc1 [post update] | taint.cpp:91:7:91:9 | mc1 | |
|
||||
| taint.cpp:88:7:88:9 | mc1 | taint.cpp:88:11:88:11 | a | TAINT |
|
||||
| taint.cpp:89:7:89:9 | mc1 | taint.cpp:89:11:89:11 | b | TAINT |
|
||||
| taint.cpp:90:7:90:9 | mc1 | taint.cpp:90:11:90:11 | c | TAINT |
|
||||
@@ -133,30 +141,27 @@
|
||||
| taint.cpp:165:22:165:25 | {...} | taint.cpp:173:8:173:13 | buffer | |
|
||||
| taint.cpp:165:24:165:24 | 0 | taint.cpp:165:22:165:25 | {...} | TAINT |
|
||||
| taint.cpp:170:10:170:15 | buffer | taint.cpp:170:3:170:8 | call to strcpy | |
|
||||
| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:170:10:170:15 | buffer | |
|
||||
| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:170:3:170:8 | call to strcpy | |
|
||||
| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:171:8:171:13 | buffer | |
|
||||
| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:172:10:172:15 | buffer | |
|
||||
| taint.cpp:170:10:170:15 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
|
||||
| taint.cpp:170:18:170:26 | Hello, | taint.cpp:170:10:170:15 | ref arg buffer | TAINT |
|
||||
| taint.cpp:171:8:171:13 | ref arg buffer | taint.cpp:171:8:171:13 | buffer | |
|
||||
| taint.cpp:171:8:171:13 | ref arg buffer | taint.cpp:172:10:172:15 | buffer | |
|
||||
| taint.cpp:171:8:171:13 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
|
||||
| taint.cpp:172:10:172:15 | buffer | taint.cpp:172:3:172:8 | call to strcat | |
|
||||
| taint.cpp:172:10:172:15 | buffer | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
|
||||
| taint.cpp:172:10:172:15 | ref arg buffer | taint.cpp:172:10:172:15 | buffer | |
|
||||
| taint.cpp:172:10:172:15 | ref arg buffer | taint.cpp:172:3:172:8 | call to strcat | |
|
||||
| taint.cpp:172:10:172:15 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
|
||||
| taint.cpp:172:18:172:24 | tainted | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
|
||||
| taint.cpp:173:8:173:13 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
|
||||
| taint.cpp:180:19:180:19 | p | taint.cpp:181:9:181:9 | p | |
|
||||
| taint.cpp:181:9:181:9 | p | taint.cpp:181:8:181:9 | * ... | TAINT |
|
||||
| taint.cpp:185:11:185:16 | call to source | taint.cpp:186:11:186:11 | x | |
|
||||
| taint.cpp:186:10:186:11 | ref arg & ... | taint.cpp:186:11:186:11 | x | |
|
||||
| taint.cpp:186:11:186:11 | x | taint.cpp:186:10:186:11 | & ... | TAINT |
|
||||
| taint.cpp:192:23:192:28 | source | taint.cpp:194:13:194:18 | source | |
|
||||
| taint.cpp:193:6:193:6 | x | taint.cpp:194:10:194:10 | x | |
|
||||
| taint.cpp:193:6:193:6 | x | taint.cpp:195:7:195:7 | x | |
|
||||
| taint.cpp:194:9:194:10 | & ... | taint.cpp:194:2:194:7 | call to memcpy | |
|
||||
| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:194:10:194:10 | x | |
|
||||
| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:194:2:194:7 | call to memcpy | |
|
||||
| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:195:7:195:7 | x | |
|
||||
| taint.cpp:194:10:194:10 | x | taint.cpp:194:9:194:10 | & ... | TAINT |
|
||||
| taint.cpp:194:13:194:18 | source | taint.cpp:194:9:194:10 | ref arg & ... | TAINT |
|
||||
@@ -169,9 +174,7 @@
|
||||
| taint.cpp:208:6:208:6 | 0 | taint.cpp:211:7:211:7 | y | |
|
||||
| taint.cpp:208:6:208:6 | 0 | taint.cpp:213:15:213:15 | y | |
|
||||
| taint.cpp:208:6:208:6 | 0 | taint.cpp:216:7:216:7 | y | |
|
||||
| taint.cpp:213:12:213:12 | ref arg x | taint.cpp:213:12:213:12 | x | |
|
||||
| taint.cpp:213:12:213:12 | ref arg x | taint.cpp:215:7:215:7 | x | |
|
||||
| taint.cpp:213:12:213:12 | x | taint.cpp:213:15:213:15 | ref arg y | |
|
||||
| taint.cpp:213:15:213:15 | ref arg y | taint.cpp:213:15:213:15 | y | |
|
||||
| taint.cpp:213:15:213:15 | ref arg y | taint.cpp:216:7:216:7 | y | |
|
||||
| taint.cpp:213:15:213:15 | y | taint.cpp:213:12:213:12 | ref arg x | |
|
||||
|
||||
@@ -87,11 +87,11 @@ void class_field_test() {
|
||||
|
||||
sink(mc1.a);
|
||||
sink(mc1.b); // tainted [NOT DETECTED]
|
||||
sink(mc1.c); // tainted [NOT DETECTED]
|
||||
sink(mc1.d); // tainted [NOT DETECTED]
|
||||
sink(mc1.c); // tainted [NOT DETECTED with IR]
|
||||
sink(mc1.d); // tainted [NOT DETECTED with IR]
|
||||
sink(mc2.a);
|
||||
sink(mc2.b); // tainted [NOT DETECTED]
|
||||
sink(mc2.c); // tainted [NOT DETECTED]
|
||||
sink(mc2.c); // tainted [NOT DETECTED with IR]
|
||||
sink(mc2.d);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
| taint.cpp:41:7:41:13 | global7 | taint.cpp:35:12:35:17 | call to source |
|
||||
| taint.cpp:42:7:42:13 | global8 | taint.cpp:35:12:35:17 | call to source |
|
||||
| taint.cpp:43:7:43:13 | global9 | taint.cpp:37:22:37:27 | call to source |
|
||||
| taint.cpp:90:11:90:11 | c | taint.cpp:72:7:72:12 | call to source |
|
||||
| taint.cpp:91:11:91:11 | d | taint.cpp:77:7:77:12 | call to source |
|
||||
| taint.cpp:94:11:94:11 | c | taint.cpp:72:7:72:12 | call to source |
|
||||
| taint.cpp:129:7:129:9 | * ... | taint.cpp:120:11:120:16 | call to source |
|
||||
| taint.cpp:134:7:134:9 | * ... | taint.cpp:120:11:120:16 | call to source |
|
||||
| taint.cpp:137:7:137:9 | * ... | taint.cpp:120:11:120:16 | call to source |
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
| taint.cpp:41:7:41:13 | taint.cpp:35:12:35:17 | AST only |
|
||||
| taint.cpp:42:7:42:13 | taint.cpp:35:12:35:17 | AST only |
|
||||
| taint.cpp:43:7:43:13 | taint.cpp:37:22:37:27 | AST only |
|
||||
| taint.cpp:90:11:90:11 | taint.cpp:72:7:72:12 | AST only |
|
||||
| taint.cpp:91:11:91:11 | taint.cpp:77:7:77:12 | AST only |
|
||||
| taint.cpp:94:11:94:11 | taint.cpp:72:7:72:12 | AST only |
|
||||
| taint.cpp:130:7:130:9 | taint.cpp:127:8:127:13 | IR only |
|
||||
| taint.cpp:137:7:137:9 | taint.cpp:120:11:120:16 | AST only |
|
||||
| taint.cpp:173:8:173:13 | taint.cpp:164:19:164:24 | AST only |
|
||||
|
||||
Reference in New Issue
Block a user