Merge pull request #1715 from jbj/ast-field-flow

C++: Initial AST-based flow through fields
This commit is contained in:
Geoffrey White
2019-08-15 13:38:58 +01:00
committed by GitHub
19 changed files with 987 additions and 72 deletions

View File

@@ -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()
)
}
/**

View File

@@ -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

View File

@@ -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

View File

@@ -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 |

View File

@@ -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() {

View File

@@ -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 |

View File

@@ -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 |

View 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;
}
};
};

View 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;
}
};
};

View 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();

View 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

View 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

View 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] |

View 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()

View 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

View File

@@ -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 | |

View File

@@ -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);
}

View File

@@ -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 |

View File

@@ -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 |