Merge from master

This commit is contained in:
Dave Bartolomeo
2020-02-04 18:33:10 -07:00
66 changed files with 3896 additions and 849 deletions

View File

@@ -6,7 +6,6 @@ private import cpp
private import semmle.code.cpp.dataflow.internal.FlowVar
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.controlflow.Guards
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
cached
private newtype TNode =
@@ -689,9 +688,9 @@ class BarrierGuard extends GuardCondition {
/** Gets a node guarded by this guard. */
final ExprNode getAGuardedNode() {
exists(GVN value, boolean branch |
result.getExpr() = value.getAnExpr() and
this.checks(value.getAnExpr(), branch) and
exists(SsaDefinition def, Variable v, boolean branch |
result.getExpr() = def.getAUse(v) and
this.checks(def.getAUse(v), branch) and
this.controls(result.getExpr().getBasicBlock(), branch)
)
}

View File

@@ -151,6 +151,22 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// from `a`.
i2.(PointerAddInstruction).getLeft() = i1
or
// Until we have from through indirections across calls, we'll take flow out
// of the parameter and into its indirection.
exists(IRFunction f, Parameter parameter |
i1 = getInitializeParameter(f, parameter) and
i2 = getInitializeIndirection(f, parameter)
)
or
// Until we have flow through indirections across calls, we'll take flow out
// of the indirection and into the argument.
// When we get proper flow through indirections across calls, this code can be
// moved to `adjusedSink` or possibly into the `DataFlow::ExprNode` class.
exists(ReadSideEffectInstruction read |
read.getAnOperand().(SideEffectOperand).getAnyDef() = i1 and
read.getArgumentDef() = i2
)
or
// Flow from argument to return value
i2 =
any(CallInstruction call |
@@ -176,6 +192,18 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
)
}
pragma[noinline]
private InitializeIndirectionInstruction getInitializeIndirection(IRFunction f, Parameter p) {
result.getParameter() = p and
result.getEnclosingIRFunction() = f
}
pragma[noinline]
private InitializeParameterInstruction getInitializeParameter(IRFunction f, Parameter p) {
result.getParameter() = p and
result.getEnclosingIRFunction() = f
}
/**
* Get an instruction that goes into argument `argumentIndex` of `call`. This
* can be either directly or through one pointer indirection.
@@ -273,23 +301,6 @@ private Element adjustedSink(DataFlow::Node sink) {
// For compatibility, send flow into a `NotExpr` even if it's part of a
// short-circuiting condition and thus might get skipped.
result.(NotExpr).getOperand() = sink.asExpr()
or
// For compatibility, send flow from argument read side effects to their
// corresponding argument expression
exists(IndirectReadSideEffectInstruction read |
read.getAnOperand().(SideEffectOperand).getAnyDef() = sink.asInstruction() and
read.getArgumentDef().getUnconvertedResultExpression() = result
)
or
exists(BufferReadSideEffectInstruction read |
read.getAnOperand().(SideEffectOperand).getAnyDef() = sink.asInstruction() and
read.getArgumentDef().getUnconvertedResultExpression() = result
)
or
exists(SizedBufferReadSideEffectInstruction read |
read.getAnOperand().(SideEffectOperand).getAnyDef() = sink.asInstruction() and
read.getArgumentDef().getUnconvertedResultExpression() = result
)
}
predicate tainted(Expr source, Element tainted) {

View File

@@ -26,7 +26,7 @@ private predicate hasResultMemoryAccess(
type = languageType.getIRType() and
isIndirectOrBufferMemoryAccess(instr.getResultMemoryAccess()) and
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
if exists(type.getByteSize())
if type.getByteSize() > 0
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
else endBitOffset = Ints::unknown()
)
@@ -43,7 +43,7 @@ private predicate hasOperandMemoryAccess(
type = languageType.getIRType() and
isIndirectOrBufferMemoryAccess(operand.getMemoryAccess()) and
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
if exists(type.getByteSize())
if type.getByteSize() > 0
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
else endBitOffset = Ints::unknown()
)

View File

@@ -1,608 +1 @@
/**
* Provides an implementation of Global Value Numbering.
* See https://en.wikipedia.org/wiki/Global_value_numbering
*
* The predicate `globalValueNumber` converts an expression into a `GVN`,
* which is an abstract type representing the value of the expression. If
* two expressions have the same `GVN` then they compute the same value.
* For example:
*
* ```
* void f(int x, int y) {
* g(x+y, x+y);
* }
* ```
*
* In this example, both arguments in the call to `g` compute the same value,
* so both arguments have the same `GVN`. In other words, we can find
* this call with the following query:
*
* ```
* from FunctionCall call, GVN v
* where v = globalValueNumber(call.getArgument(0))
* and v = globalValueNumber(call.getArgument(1))
* select call
* ```
*
* The analysis is conservative, so two expressions might have different
* `GVN`s even though the actually always compute the same value. The most
* common reason for this is that the analysis cannot prove that there
* are no side-effects that might cause the computed value to change.
*/
/*
* Note to developers: the correctness of this module depends on the
* definitions of GVN, globalValueNumber, and analyzableExpr being kept in
* sync with each other. If you change this module then make sure that the
* change is symmetric across all three.
*/
import cpp
private import semmle.code.cpp.controlflow.SSA
/**
* Holds if the result is a control flow node that might change the
* value of any global variable. This is used in the implementation
* of `GVN_OtherVariable`, because we need to be quite conservative when
* we assign a value number to a global variable. For example:
*
* ```
* x = g+1;
* dosomething();
* y = g+1;
* ```
*
* It is not safe to assign the same value number to both instances
* of `g+1` in this example, because the call to `dosomething` might
* change the value of `g`.
*/
private ControlFlowNode nodeWithPossibleSideEffect() {
result instanceof Call
or
// If the lhs of an assignment is not analyzable by SSA, then
// we need to treat the assignment as having a possible side-effect.
result instanceof Assignment and not result instanceof SsaDefinition
or
result instanceof CrementOperation and not result instanceof SsaDefinition
or
exists(LocalVariable v |
result = v.getInitializer().getExpr() and not result instanceof SsaDefinition
)
or
result instanceof AsmStmt
}
/**
* Gets the entry node of the control flow graph of which `node` is a
* member.
*/
cached
private ControlFlowNode getControlFlowEntry(ControlFlowNode node) {
result = node.getControlFlowScope().getEntryPoint() and
result.getASuccessor*() = node
}
/**
* Holds if there is a control flow edge from `src` to `dst` or
* if `dst` is an expression with a possible side-effect. The idea
* is to treat side effects as entry points in the control flow
* graph so that we can use the dominator tree to find the most recent
* side-effect.
*/
private predicate sideEffectCFG(ControlFlowNode src, ControlFlowNode dst) {
src.getASuccessor() = dst
or
// Add an edge from the entry point to any node that might have a side
// effect.
dst = nodeWithPossibleSideEffect() and
src = getControlFlowEntry(dst)
}
/**
* Holds if `dominator` is the immediate dominator of `node` in
* the side-effect CFG.
*/
private predicate iDomEffect(ControlFlowNode dominator, ControlFlowNode node) =
idominance(functionEntry/1, sideEffectCFG/2)(_, dominator, node)
/**
* Gets the most recent side effect. To be more precise, `result` is a
* dominator of `node` and no side-effects can occur between `result` and
* `node`.
*
* `sideEffectCFG` has an edge from the function entry to every node with a
* side-effect. This means that every node with a side-effect has the
* function entry as its immediate dominator. So if node `x` dominates node
* `y` then there can be no side effects between `x` and `y` unless `x` is
* the function entry. So the optimal choice for `result` has the function
* entry as its immediate dominator.
*
* Example:
*
* ```
* 000: int f(int a, int b, int *p) {
* 001: int r = 0;
* 002: if (a) {
* 003: if (b) {
* 004: sideEffect1();
* 005: }
* 006: } else {
* 007: sideEffect2();
* 008: }
* 009: if (a) {
* 010: r++; // Not a side-effect, because r is an SSA variable.
* 011: }
* 012: if (b) {
* 013: r++; // Not a side-effect, because r is an SSA variable.
* 014: }
* 015: return *p;
* 016: }
* ```
*
* Suppose we want to find the most recent side-effect for the dereference
* of `p` on line 015. The `sideEffectCFG` has an edge from the function
* entry (line 000) to the side effects at lines 004 and 007. Therefore,
* the immediate dominator tree looks like this:
*
* 000 - 001 - 002 - 003
* - 004
* - 007
* - 009 - 010
* - 012 - 013
* - 015
*
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
* Therefore, the most recent side effect for line 015 is line 009.
*/
cached
private ControlFlowNode mostRecentSideEffect(ControlFlowNode node) {
exists(ControlFlowNode entry |
functionEntry(entry) and
iDomEffect(entry, result) and
iDomEffect*(result, node)
)
}
/** Used to represent the "global value number" of an expression. */
cached
private newtype GVNBase =
GVN_IntConst(int val, Type t) { mk_IntConst(val, t, _) } or
GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or
// If the local variable does not have a defining value, then
// we use the SsaDefinition as its global value number.
GVN_UndefinedStackVariable(StackVariable x, SsaDefinition def) {
mk_UndefinedStackVariable(x, def, _)
} or
// Variables with no SSA information. As a crude (but safe)
// approximation, we use `mostRecentSideEffect` to compute a definition
// location for the variable. This ensures that two instances of the same
// global variable will only get the same value number if they are
// guaranteed to have the same value.
GVN_OtherVariable(Variable x, ControlFlowNode dominator) { mk_OtherVariable(x, dominator, _) } or
GVN_FieldAccess(GVN s, Field f) {
mk_DotFieldAccess(s, f, _) or
mk_PointerFieldAccess_with_deref(s, f, _) or
mk_ImplicitThisFieldAccess_with_deref(s, f, _)
} or
// Dereference a pointer. The value might have changed since the last
// time the pointer was dereferenced, so we need to include a definition
// location. As a crude (but safe) approximation, we use
// `mostRecentSideEffect` to compute a definition location.
GVN_Deref(GVN p, ControlFlowNode dominator) {
mk_Deref(p, dominator, _) or
mk_PointerFieldAccess(p, _, dominator, _) or
mk_ImplicitThisFieldAccess_with_qualifier(p, _, dominator, _)
} or
GVN_ThisExpr(Function fcn) {
mk_ThisExpr(fcn, _) or
mk_ImplicitThisFieldAccess(fcn, _, _, _)
} or
GVN_Conversion(Type t, GVN child) { mk_Conversion(t, child, _) } or
GVN_BinaryOp(GVN lhs, GVN rhs, string opname) { mk_BinaryOp(lhs, rhs, opname, _) } or
GVN_UnaryOp(GVN child, string opname) { mk_UnaryOp(child, opname, _) } or
GVN_ArrayAccess(GVN x, GVN i, ControlFlowNode dominator) { mk_ArrayAccess(x, i, dominator, _) } or
// Any expression that is not handled by the cases above is
// given a unique number based on the expression itself.
GVN_Unanalyzable(Expr e) { not analyzableExpr(e) }
/**
* A Global Value Number. A GVN is an abstract representation of the value
* computed by an expression. The relationship between `Expr` and `GVN` is
* many-to-one: every `Expr` has exactly one `GVN`, but multiple
* expressions can have the same `GVN`. If two expressions have the same
* `GVN`, it means that they compute the same value at run time. The `GVN`
* is an opaque value, so you cannot deduce what the run-time value of an
* expression will be from its `GVN`. The only use for the `GVN` of an
* expression is to find other expressions that compute the same value.
* Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`.
*
* Note: `GVN` has `toString` and `getLocation` methods, so that it can be
* displayed in a results list. These work by picking an arbitrary
* expression with this `GVN` and using its `toString` and `getLocation`
* methods.
*/
class GVN extends GVNBase {
GVN() { this instanceof GVNBase }
/** Gets an expression that has this GVN. */
Expr getAnExpr() { this = globalValueNumber(result) }
/** Gets the kind of the GVN. This can be useful for debugging. */
string getKind() {
if this instanceof GVN_IntConst
then result = "IntConst"
else
if this instanceof GVN_FloatConst
then result = "FloatConst"
else
if this instanceof GVN_UndefinedStackVariable
then result = "UndefinedStackVariable"
else
if this instanceof GVN_OtherVariable
then result = "OtherVariable"
else
if this instanceof GVN_FieldAccess
then result = "FieldAccess"
else
if this instanceof GVN_Deref
then result = "Deref"
else
if this instanceof GVN_ThisExpr
then result = "ThisExpr"
else
if this instanceof GVN_Conversion
then result = "Conversion"
else
if this instanceof GVN_BinaryOp
then result = "BinaryOp"
else
if this instanceof GVN_UnaryOp
then result = "UnaryOp"
else
if this instanceof GVN_ArrayAccess
then result = "ArrayAccess"
else
if this instanceof GVN_Unanalyzable
then result = "Unanalyzable"
else result = "error"
}
/**
* Gets an example of an expression with this GVN.
* This is useful for things like implementing toString().
*/
private Expr exampleExpr() {
// Pick the expression with the minimum source location string. This is
// just an arbitrary way to pick an expression with this `GVN`.
result = min(Expr e | this = globalValueNumber(e) | e order by e.getLocation().toString())
}
/** Gets a textual representation of this element. */
string toString() { result = exampleExpr().toString() }
/** Gets the primary location of this element. */
Location getLocation() { result = exampleExpr().getLocation() }
}
private predicate analyzableIntConst(Expr e) {
strictcount(e.getValue().toInt()) = 1 and
strictcount(e.getUnspecifiedType()) = 1
}
private predicate mk_IntConst(int val, Type t, Expr e) {
analyzableIntConst(e) and
val = e.getValue().toInt() and
t = e.getUnspecifiedType()
}
private predicate analyzableFloatConst(Expr e) {
strictcount(e.getValue().toFloat()) = 1 and
strictcount(e.getUnspecifiedType()) = 1 and
not analyzableIntConst(e)
}
private predicate mk_FloatConst(float val, Type t, Expr e) {
analyzableFloatConst(e) and
val = e.getValue().toFloat() and
t = e.getUnspecifiedType()
}
private predicate analyzableStackVariable(VariableAccess access) {
strictcount(SsaDefinition def | def.getAUse(_) = access | def) = 1 and
strictcount(SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and
count(SsaDefinition def, Variable v |
def.getAUse(v) = access
|
def.getDefiningValue(v).getFullyConverted()
) <= 1 and
not analyzableConst(access)
}
// Note: this predicate only has a result if the access has no
// defining value. If there is a defining value, then there is no
// need to generate a fresh `GVN` for the access because `globalValueNumber`
// will follow the chain and use the GVN of the defining value.
private predicate mk_UndefinedStackVariable(
StackVariable x, SsaDefinition def, VariableAccess access
) {
analyzableStackVariable(access) and
access = def.getAUse(x) and
not exists(def.getDefiningValue(x))
}
private predicate analyzableDotFieldAccess(DotFieldAccess access) {
strictcount(access.getTarget()) = 1 and
strictcount(access.getQualifier().getFullyConverted()) = 1 and
not analyzableConst(access)
}
private predicate mk_DotFieldAccess(GVN qualifier, Field target, DotFieldAccess access) {
analyzableDotFieldAccess(access) and
target = access.getTarget() and
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
}
private predicate analyzablePointerFieldAccess(PointerFieldAccess access) {
strictcount(mostRecentSideEffect(access)) = 1 and
strictcount(access.getTarget()) = 1 and
strictcount(access.getQualifier().getFullyConverted()) = 1 and
not analyzableConst(access)
}
private predicate mk_PointerFieldAccess(
GVN qualifier, Field target, ControlFlowNode dominator, PointerFieldAccess access
) {
analyzablePointerFieldAccess(access) and
dominator = mostRecentSideEffect(access) and
target = access.getTarget() and
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
}
/**
* `obj->field` is equivalent to `(*obj).field`, so we need to wrap an
* extra `GVN_Deref` around the qualifier.
*/
private predicate mk_PointerFieldAccess_with_deref(
GVN new_qualifier, Field target, PointerFieldAccess access
) {
exists(GVN qualifier, ControlFlowNode dominator |
mk_PointerFieldAccess(qualifier, target, dominator, access) and
new_qualifier = GVN_Deref(qualifier, dominator)
)
}
private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) {
strictcount(mostRecentSideEffect(access)) = 1 and
strictcount(access.getTarget()) = 1 and
strictcount(access.getEnclosingFunction()) = 1 and
not analyzableConst(access)
}
private predicate mk_ImplicitThisFieldAccess(
Function fcn, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
) {
analyzableImplicitThisFieldAccess(access) and
dominator = mostRecentSideEffect(access) and
target = access.getTarget() and
fcn = access.getEnclosingFunction()
}
private predicate mk_ImplicitThisFieldAccess_with_qualifier(
GVN qualifier, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
) {
exists(Function fcn |
mk_ImplicitThisFieldAccess(fcn, target, dominator, access) and
qualifier = GVN_ThisExpr(fcn)
)
}
private predicate mk_ImplicitThisFieldAccess_with_deref(
GVN new_qualifier, Field target, ImplicitThisFieldAccess access
) {
exists(GVN qualifier, ControlFlowNode dominator |
mk_ImplicitThisFieldAccess_with_qualifier(qualifier, target, dominator, access) and
new_qualifier = GVN_Deref(qualifier, dominator)
)
}
/**
* Holds if `access` is an access of a variable that does
* not have SSA information. (For example, because the variable
* is global.)
*/
private predicate analyzableOtherVariable(VariableAccess access) {
not access instanceof FieldAccess and
not exists(SsaDefinition def | access = def.getAUse(_)) and
strictcount(access.getTarget()) = 1 and
strictcount(mostRecentSideEffect(access)) = 1 and
not analyzableConst(access)
}
private predicate mk_OtherVariable(Variable x, ControlFlowNode dominator, VariableAccess access) {
analyzableOtherVariable(access) and
x = access.getTarget() and
dominator = mostRecentSideEffect(access)
}
private predicate analyzableConversion(Conversion conv) {
strictcount(conv.getUnspecifiedType()) = 1 and
strictcount(conv.getExpr()) = 1 and
not analyzableConst(conv)
}
private predicate mk_Conversion(Type t, GVN child, Conversion conv) {
analyzableConversion(conv) and
t = conv.getUnspecifiedType() and
child = globalValueNumber(conv.getExpr())
}
private predicate analyzableBinaryOp(BinaryOperation op) {
op.isPure() and
strictcount(op.getLeftOperand().getFullyConverted()) = 1 and
strictcount(op.getRightOperand().getFullyConverted()) = 1 and
strictcount(op.getOperator()) = 1 and
not analyzableConst(op)
}
private predicate mk_BinaryOp(GVN lhs, GVN rhs, string opname, BinaryOperation op) {
analyzableBinaryOp(op) and
lhs = globalValueNumber(op.getLeftOperand().getFullyConverted()) and
rhs = globalValueNumber(op.getRightOperand().getFullyConverted()) and
opname = op.getOperator()
}
private predicate analyzableUnaryOp(UnaryOperation op) {
not op instanceof PointerDereferenceExpr and
op.isPure() and
strictcount(op.getOperand().getFullyConverted()) = 1 and
strictcount(op.getOperator()) = 1 and
not analyzableConst(op)
}
private predicate mk_UnaryOp(GVN child, string opname, UnaryOperation op) {
analyzableUnaryOp(op) and
child = globalValueNumber(op.getOperand().getFullyConverted()) and
opname = op.getOperator()
}
private predicate analyzableThisExpr(ThisExpr thisExpr) {
strictcount(thisExpr.getEnclosingFunction()) = 1 and
not analyzableConst(thisExpr)
}
private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) {
analyzableThisExpr(thisExpr) and
fcn = thisExpr.getEnclosingFunction()
}
private predicate analyzableArrayAccess(ArrayExpr ae) {
strictcount(ae.getArrayBase().getFullyConverted()) = 1 and
strictcount(ae.getArrayOffset().getFullyConverted()) = 1 and
strictcount(mostRecentSideEffect(ae)) = 1 and
not analyzableConst(ae)
}
private predicate mk_ArrayAccess(GVN base, GVN offset, ControlFlowNode dominator, ArrayExpr ae) {
analyzableArrayAccess(ae) and
base = globalValueNumber(ae.getArrayBase().getFullyConverted()) and
offset = globalValueNumber(ae.getArrayOffset().getFullyConverted()) and
dominator = mostRecentSideEffect(ae)
}
private predicate analyzablePointerDereferenceExpr(PointerDereferenceExpr deref) {
strictcount(deref.getOperand().getFullyConverted()) = 1 and
strictcount(mostRecentSideEffect(deref)) = 1 and
not analyzableConst(deref)
}
private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceExpr deref) {
analyzablePointerDereferenceExpr(deref) and
p = globalValueNumber(deref.getOperand().getFullyConverted()) and
dominator = mostRecentSideEffect(deref)
}
/** Gets the global value number of expression `e`. */
cached
GVN globalValueNumber(Expr e) {
exists(int val, Type t |
mk_IntConst(val, t, e) and
result = GVN_IntConst(val, t)
)
or
exists(float val, Type t |
mk_FloatConst(val, t, e) and
result = GVN_FloatConst(val, t)
)
or
// Local variable with a defining value.
exists(StackVariable x, SsaDefinition def |
analyzableStackVariable(e) and
e = def.getAUse(x) and
result = globalValueNumber(def.getDefiningValue(x).getFullyConverted())
)
or
// Local variable without a defining value.
exists(StackVariable x, SsaDefinition def |
mk_UndefinedStackVariable(x, def, e) and
result = GVN_UndefinedStackVariable(x, def)
)
or
// Variable with no SSA information.
exists(Variable x, ControlFlowNode dominator |
mk_OtherVariable(x, dominator, e) and
result = GVN_OtherVariable(x, dominator)
)
or
exists(GVN qualifier, Field target |
mk_DotFieldAccess(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(GVN qualifier, Field target |
mk_PointerFieldAccess_with_deref(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(GVN qualifier, Field target |
mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(Function fcn |
mk_ThisExpr(fcn, e) and
result = GVN_ThisExpr(fcn)
)
or
exists(Type t, GVN child |
mk_Conversion(t, child, e) and
result = GVN_Conversion(t, child)
)
or
exists(GVN lhs, GVN rhs, string opname |
mk_BinaryOp(lhs, rhs, opname, e) and
result = GVN_BinaryOp(lhs, rhs, opname)
)
or
exists(GVN child, string opname |
mk_UnaryOp(child, opname, e) and
result = GVN_UnaryOp(child, opname)
)
or
exists(GVN x, GVN i, ControlFlowNode dominator |
mk_ArrayAccess(x, i, dominator, e) and
result = GVN_ArrayAccess(x, i, dominator)
)
or
exists(GVN p, ControlFlowNode dominator |
mk_Deref(p, dominator, e) and
result = GVN_Deref(p, dominator)
)
or
not analyzableExpr(e) and result = GVN_Unanalyzable(e)
}
private predicate analyzableConst(Expr e) {
analyzableIntConst(e) or
analyzableFloatConst(e)
}
/**
* Holds if the expression is explicitly handled by `globalValueNumber`.
* Unanalyzable expressions still need to be given a global value number,
* but it will be a unique number that is not shared with any other
* expression.
*/
private predicate analyzableExpr(Expr e) {
analyzableConst(e) or
analyzableStackVariable(e) or
analyzableDotFieldAccess(e) or
analyzablePointerFieldAccess(e) or
analyzableImplicitThisFieldAccess(e) or
analyzableOtherVariable(e) or
analyzableConversion(e) or
analyzableBinaryOp(e) or
analyzableUnaryOp(e) or
analyzableThisExpr(e) or
analyzableArrayAccess(e) or
analyzablePointerDereferenceExpr(e)
}
import GlobalValueNumberingImpl

View File

@@ -0,0 +1,608 @@
/**
* Provides an implementation of Global Value Numbering.
* See https://en.wikipedia.org/wiki/Global_value_numbering
*
* The predicate `globalValueNumber` converts an expression into a `GVN`,
* which is an abstract type representing the value of the expression. If
* two expressions have the same `GVN` then they compute the same value.
* For example:
*
* ```
* void f(int x, int y) {
* g(x+y, x+y);
* }
* ```
*
* In this example, both arguments in the call to `g` compute the same value,
* so both arguments have the same `GVN`. In other words, we can find
* this call with the following query:
*
* ```
* from FunctionCall call, GVN v
* where v = globalValueNumber(call.getArgument(0))
* and v = globalValueNumber(call.getArgument(1))
* select call
* ```
*
* The analysis is conservative, so two expressions might have different
* `GVN`s even though the actually always compute the same value. The most
* common reason for this is that the analysis cannot prove that there
* are no side-effects that might cause the computed value to change.
*/
/*
* Note to developers: the correctness of this module depends on the
* definitions of GVN, globalValueNumber, and analyzableExpr being kept in
* sync with each other. If you change this module then make sure that the
* change is symmetric across all three.
*/
import cpp
private import semmle.code.cpp.controlflow.SSA
/**
* Holds if the result is a control flow node that might change the
* value of any global variable. This is used in the implementation
* of `GVN_OtherVariable`, because we need to be quite conservative when
* we assign a value number to a global variable. For example:
*
* ```
* x = g+1;
* dosomething();
* y = g+1;
* ```
*
* It is not safe to assign the same value number to both instances
* of `g+1` in this example, because the call to `dosomething` might
* change the value of `g`.
*/
private ControlFlowNode nodeWithPossibleSideEffect() {
result instanceof Call
or
// If the lhs of an assignment is not analyzable by SSA, then
// we need to treat the assignment as having a possible side-effect.
result instanceof Assignment and not result instanceof SsaDefinition
or
result instanceof CrementOperation and not result instanceof SsaDefinition
or
exists(LocalVariable v |
result = v.getInitializer().getExpr() and not result instanceof SsaDefinition
)
or
result instanceof AsmStmt
}
/**
* Gets the entry node of the control flow graph of which `node` is a
* member.
*/
cached
private ControlFlowNode getControlFlowEntry(ControlFlowNode node) {
result = node.getControlFlowScope().getEntryPoint() and
result.getASuccessor*() = node
}
/**
* Holds if there is a control flow edge from `src` to `dst` or
* if `dst` is an expression with a possible side-effect. The idea
* is to treat side effects as entry points in the control flow
* graph so that we can use the dominator tree to find the most recent
* side-effect.
*/
private predicate sideEffectCFG(ControlFlowNode src, ControlFlowNode dst) {
src.getASuccessor() = dst
or
// Add an edge from the entry point to any node that might have a side
// effect.
dst = nodeWithPossibleSideEffect() and
src = getControlFlowEntry(dst)
}
/**
* Holds if `dominator` is the immediate dominator of `node` in
* the side-effect CFG.
*/
private predicate iDomEffect(ControlFlowNode dominator, ControlFlowNode node) =
idominance(functionEntry/1, sideEffectCFG/2)(_, dominator, node)
/**
* Gets the most recent side effect. To be more precise, `result` is a
* dominator of `node` and no side-effects can occur between `result` and
* `node`.
*
* `sideEffectCFG` has an edge from the function entry to every node with a
* side-effect. This means that every node with a side-effect has the
* function entry as its immediate dominator. So if node `x` dominates node
* `y` then there can be no side effects between `x` and `y` unless `x` is
* the function entry. So the optimal choice for `result` has the function
* entry as its immediate dominator.
*
* Example:
*
* ```
* 000: int f(int a, int b, int *p) {
* 001: int r = 0;
* 002: if (a) {
* 003: if (b) {
* 004: sideEffect1();
* 005: }
* 006: } else {
* 007: sideEffect2();
* 008: }
* 009: if (a) {
* 010: r++; // Not a side-effect, because r is an SSA variable.
* 011: }
* 012: if (b) {
* 013: r++; // Not a side-effect, because r is an SSA variable.
* 014: }
* 015: return *p;
* 016: }
* ```
*
* Suppose we want to find the most recent side-effect for the dereference
* of `p` on line 015. The `sideEffectCFG` has an edge from the function
* entry (line 000) to the side effects at lines 004 and 007. Therefore,
* the immediate dominator tree looks like this:
*
* 000 - 001 - 002 - 003
* - 004
* - 007
* - 009 - 010
* - 012 - 013
* - 015
*
* The immediate dominator path to line 015 is 000 - 009 - 012 - 015.
* Therefore, the most recent side effect for line 015 is line 009.
*/
cached
private ControlFlowNode mostRecentSideEffect(ControlFlowNode node) {
exists(ControlFlowNode entry |
functionEntry(entry) and
iDomEffect(entry, result) and
iDomEffect*(result, node)
)
}
/** Used to represent the "global value number" of an expression. */
cached
private newtype GVNBase =
GVN_IntConst(int val, Type t) { mk_IntConst(val, t, _) } or
GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or
// If the local variable does not have a defining value, then
// we use the SsaDefinition as its global value number.
GVN_UndefinedStackVariable(StackVariable x, SsaDefinition def) {
mk_UndefinedStackVariable(x, def, _)
} or
// Variables with no SSA information. As a crude (but safe)
// approximation, we use `mostRecentSideEffect` to compute a definition
// location for the variable. This ensures that two instances of the same
// global variable will only get the same value number if they are
// guaranteed to have the same value.
GVN_OtherVariable(Variable x, ControlFlowNode dominator) { mk_OtherVariable(x, dominator, _) } or
GVN_FieldAccess(GVN s, Field f) {
mk_DotFieldAccess(s, f, _) or
mk_PointerFieldAccess_with_deref(s, f, _) or
mk_ImplicitThisFieldAccess_with_deref(s, f, _)
} or
// Dereference a pointer. The value might have changed since the last
// time the pointer was dereferenced, so we need to include a definition
// location. As a crude (but safe) approximation, we use
// `mostRecentSideEffect` to compute a definition location.
GVN_Deref(GVN p, ControlFlowNode dominator) {
mk_Deref(p, dominator, _) or
mk_PointerFieldAccess(p, _, dominator, _) or
mk_ImplicitThisFieldAccess_with_qualifier(p, _, dominator, _)
} or
GVN_ThisExpr(Function fcn) {
mk_ThisExpr(fcn, _) or
mk_ImplicitThisFieldAccess(fcn, _, _, _)
} or
GVN_Conversion(Type t, GVN child) { mk_Conversion(t, child, _) } or
GVN_BinaryOp(GVN lhs, GVN rhs, string opname) { mk_BinaryOp(lhs, rhs, opname, _) } or
GVN_UnaryOp(GVN child, string opname) { mk_UnaryOp(child, opname, _) } or
GVN_ArrayAccess(GVN x, GVN i, ControlFlowNode dominator) { mk_ArrayAccess(x, i, dominator, _) } or
// Any expression that is not handled by the cases above is
// given a unique number based on the expression itself.
GVN_Unanalyzable(Expr e) { not analyzableExpr(e) }
/**
* A Global Value Number. A GVN is an abstract representation of the value
* computed by an expression. The relationship between `Expr` and `GVN` is
* many-to-one: every `Expr` has exactly one `GVN`, but multiple
* expressions can have the same `GVN`. If two expressions have the same
* `GVN`, it means that they compute the same value at run time. The `GVN`
* is an opaque value, so you cannot deduce what the run-time value of an
* expression will be from its `GVN`. The only use for the `GVN` of an
* expression is to find other expressions that compute the same value.
* Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`.
*
* Note: `GVN` has `toString` and `getLocation` methods, so that it can be
* displayed in a results list. These work by picking an arbitrary
* expression with this `GVN` and using its `toString` and `getLocation`
* methods.
*/
class GVN extends GVNBase {
GVN() { this instanceof GVNBase }
/** Gets an expression that has this GVN. */
Expr getAnExpr() { this = globalValueNumber(result) }
/** Gets the kind of the GVN. This can be useful for debugging. */
string getKind() {
if this instanceof GVN_IntConst
then result = "IntConst"
else
if this instanceof GVN_FloatConst
then result = "FloatConst"
else
if this instanceof GVN_UndefinedStackVariable
then result = "UndefinedStackVariable"
else
if this instanceof GVN_OtherVariable
then result = "OtherVariable"
else
if this instanceof GVN_FieldAccess
then result = "FieldAccess"
else
if this instanceof GVN_Deref
then result = "Deref"
else
if this instanceof GVN_ThisExpr
then result = "ThisExpr"
else
if this instanceof GVN_Conversion
then result = "Conversion"
else
if this instanceof GVN_BinaryOp
then result = "BinaryOp"
else
if this instanceof GVN_UnaryOp
then result = "UnaryOp"
else
if this instanceof GVN_ArrayAccess
then result = "ArrayAccess"
else
if this instanceof GVN_Unanalyzable
then result = "Unanalyzable"
else result = "error"
}
/**
* Gets an example of an expression with this GVN.
* This is useful for things like implementing toString().
*/
private Expr exampleExpr() {
// Pick the expression with the minimum source location string. This is
// just an arbitrary way to pick an expression with this `GVN`.
result = min(Expr e | this = globalValueNumber(e) | e order by e.getLocation().toString())
}
/** Gets a textual representation of this element. */
string toString() { result = exampleExpr().toString() }
/** Gets the primary location of this element. */
Location getLocation() { result = exampleExpr().getLocation() }
}
private predicate analyzableIntConst(Expr e) {
strictcount(e.getValue().toInt()) = 1 and
strictcount(e.getUnspecifiedType()) = 1
}
private predicate mk_IntConst(int val, Type t, Expr e) {
analyzableIntConst(e) and
val = e.getValue().toInt() and
t = e.getUnspecifiedType()
}
private predicate analyzableFloatConst(Expr e) {
strictcount(e.getValue().toFloat()) = 1 and
strictcount(e.getUnspecifiedType()) = 1 and
not analyzableIntConst(e)
}
private predicate mk_FloatConst(float val, Type t, Expr e) {
analyzableFloatConst(e) and
val = e.getValue().toFloat() and
t = e.getUnspecifiedType()
}
private predicate analyzableStackVariable(VariableAccess access) {
strictcount(SsaDefinition def | def.getAUse(_) = access | def) = 1 and
strictcount(SsaDefinition def, Variable v | def.getAUse(v) = access | v) = 1 and
count(SsaDefinition def, Variable v |
def.getAUse(v) = access
|
def.getDefiningValue(v).getFullyConverted()
) <= 1 and
not analyzableConst(access)
}
// Note: this predicate only has a result if the access has no
// defining value. If there is a defining value, then there is no
// need to generate a fresh `GVN` for the access because `globalValueNumber`
// will follow the chain and use the GVN of the defining value.
private predicate mk_UndefinedStackVariable(
StackVariable x, SsaDefinition def, VariableAccess access
) {
analyzableStackVariable(access) and
access = def.getAUse(x) and
not exists(def.getDefiningValue(x))
}
private predicate analyzableDotFieldAccess(DotFieldAccess access) {
strictcount(access.getTarget()) = 1 and
strictcount(access.getQualifier().getFullyConverted()) = 1 and
not analyzableConst(access)
}
private predicate mk_DotFieldAccess(GVN qualifier, Field target, DotFieldAccess access) {
analyzableDotFieldAccess(access) and
target = access.getTarget() and
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
}
private predicate analyzablePointerFieldAccess(PointerFieldAccess access) {
strictcount(mostRecentSideEffect(access)) = 1 and
strictcount(access.getTarget()) = 1 and
strictcount(access.getQualifier().getFullyConverted()) = 1 and
not analyzableConst(access)
}
private predicate mk_PointerFieldAccess(
GVN qualifier, Field target, ControlFlowNode dominator, PointerFieldAccess access
) {
analyzablePointerFieldAccess(access) and
dominator = mostRecentSideEffect(access) and
target = access.getTarget() and
qualifier = globalValueNumber(access.getQualifier().getFullyConverted())
}
/**
* `obj->field` is equivalent to `(*obj).field`, so we need to wrap an
* extra `GVN_Deref` around the qualifier.
*/
private predicate mk_PointerFieldAccess_with_deref(
GVN new_qualifier, Field target, PointerFieldAccess access
) {
exists(GVN qualifier, ControlFlowNode dominator |
mk_PointerFieldAccess(qualifier, target, dominator, access) and
new_qualifier = GVN_Deref(qualifier, dominator)
)
}
private predicate analyzableImplicitThisFieldAccess(ImplicitThisFieldAccess access) {
strictcount(mostRecentSideEffect(access)) = 1 and
strictcount(access.getTarget()) = 1 and
strictcount(access.getEnclosingFunction()) = 1 and
not analyzableConst(access)
}
private predicate mk_ImplicitThisFieldAccess(
Function fcn, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
) {
analyzableImplicitThisFieldAccess(access) and
dominator = mostRecentSideEffect(access) and
target = access.getTarget() and
fcn = access.getEnclosingFunction()
}
private predicate mk_ImplicitThisFieldAccess_with_qualifier(
GVN qualifier, Field target, ControlFlowNode dominator, ImplicitThisFieldAccess access
) {
exists(Function fcn |
mk_ImplicitThisFieldAccess(fcn, target, dominator, access) and
qualifier = GVN_ThisExpr(fcn)
)
}
private predicate mk_ImplicitThisFieldAccess_with_deref(
GVN new_qualifier, Field target, ImplicitThisFieldAccess access
) {
exists(GVN qualifier, ControlFlowNode dominator |
mk_ImplicitThisFieldAccess_with_qualifier(qualifier, target, dominator, access) and
new_qualifier = GVN_Deref(qualifier, dominator)
)
}
/**
* Holds if `access` is an access of a variable that does
* not have SSA information. (For example, because the variable
* is global.)
*/
private predicate analyzableOtherVariable(VariableAccess access) {
not access instanceof FieldAccess and
not exists(SsaDefinition def | access = def.getAUse(_)) and
strictcount(access.getTarget()) = 1 and
strictcount(mostRecentSideEffect(access)) = 1 and
not analyzableConst(access)
}
private predicate mk_OtherVariable(Variable x, ControlFlowNode dominator, VariableAccess access) {
analyzableOtherVariable(access) and
x = access.getTarget() and
dominator = mostRecentSideEffect(access)
}
private predicate analyzableConversion(Conversion conv) {
strictcount(conv.getUnspecifiedType()) = 1 and
strictcount(conv.getExpr()) = 1 and
not analyzableConst(conv)
}
private predicate mk_Conversion(Type t, GVN child, Conversion conv) {
analyzableConversion(conv) and
t = conv.getUnspecifiedType() and
child = globalValueNumber(conv.getExpr())
}
private predicate analyzableBinaryOp(BinaryOperation op) {
op.isPure() and
strictcount(op.getLeftOperand().getFullyConverted()) = 1 and
strictcount(op.getRightOperand().getFullyConverted()) = 1 and
strictcount(op.getOperator()) = 1 and
not analyzableConst(op)
}
private predicate mk_BinaryOp(GVN lhs, GVN rhs, string opname, BinaryOperation op) {
analyzableBinaryOp(op) and
lhs = globalValueNumber(op.getLeftOperand().getFullyConverted()) and
rhs = globalValueNumber(op.getRightOperand().getFullyConverted()) and
opname = op.getOperator()
}
private predicate analyzableUnaryOp(UnaryOperation op) {
not op instanceof PointerDereferenceExpr and
op.isPure() and
strictcount(op.getOperand().getFullyConverted()) = 1 and
strictcount(op.getOperator()) = 1 and
not analyzableConst(op)
}
private predicate mk_UnaryOp(GVN child, string opname, UnaryOperation op) {
analyzableUnaryOp(op) and
child = globalValueNumber(op.getOperand().getFullyConverted()) and
opname = op.getOperator()
}
private predicate analyzableThisExpr(ThisExpr thisExpr) {
strictcount(thisExpr.getEnclosingFunction()) = 1 and
not analyzableConst(thisExpr)
}
private predicate mk_ThisExpr(Function fcn, ThisExpr thisExpr) {
analyzableThisExpr(thisExpr) and
fcn = thisExpr.getEnclosingFunction()
}
private predicate analyzableArrayAccess(ArrayExpr ae) {
strictcount(ae.getArrayBase().getFullyConverted()) = 1 and
strictcount(ae.getArrayOffset().getFullyConverted()) = 1 and
strictcount(mostRecentSideEffect(ae)) = 1 and
not analyzableConst(ae)
}
private predicate mk_ArrayAccess(GVN base, GVN offset, ControlFlowNode dominator, ArrayExpr ae) {
analyzableArrayAccess(ae) and
base = globalValueNumber(ae.getArrayBase().getFullyConverted()) and
offset = globalValueNumber(ae.getArrayOffset().getFullyConverted()) and
dominator = mostRecentSideEffect(ae)
}
private predicate analyzablePointerDereferenceExpr(PointerDereferenceExpr deref) {
strictcount(deref.getOperand().getFullyConverted()) = 1 and
strictcount(mostRecentSideEffect(deref)) = 1 and
not analyzableConst(deref)
}
private predicate mk_Deref(GVN p, ControlFlowNode dominator, PointerDereferenceExpr deref) {
analyzablePointerDereferenceExpr(deref) and
p = globalValueNumber(deref.getOperand().getFullyConverted()) and
dominator = mostRecentSideEffect(deref)
}
/** Gets the global value number of expression `e`. */
cached
GVN globalValueNumber(Expr e) {
exists(int val, Type t |
mk_IntConst(val, t, e) and
result = GVN_IntConst(val, t)
)
or
exists(float val, Type t |
mk_FloatConst(val, t, e) and
result = GVN_FloatConst(val, t)
)
or
// Local variable with a defining value.
exists(StackVariable x, SsaDefinition def |
analyzableStackVariable(e) and
e = def.getAUse(x) and
result = globalValueNumber(def.getDefiningValue(x).getFullyConverted())
)
or
// Local variable without a defining value.
exists(StackVariable x, SsaDefinition def |
mk_UndefinedStackVariable(x, def, e) and
result = GVN_UndefinedStackVariable(x, def)
)
or
// Variable with no SSA information.
exists(Variable x, ControlFlowNode dominator |
mk_OtherVariable(x, dominator, e) and
result = GVN_OtherVariable(x, dominator)
)
or
exists(GVN qualifier, Field target |
mk_DotFieldAccess(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(GVN qualifier, Field target |
mk_PointerFieldAccess_with_deref(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(GVN qualifier, Field target |
mk_ImplicitThisFieldAccess_with_deref(qualifier, target, e) and
result = GVN_FieldAccess(qualifier, target)
)
or
exists(Function fcn |
mk_ThisExpr(fcn, e) and
result = GVN_ThisExpr(fcn)
)
or
exists(Type t, GVN child |
mk_Conversion(t, child, e) and
result = GVN_Conversion(t, child)
)
or
exists(GVN lhs, GVN rhs, string opname |
mk_BinaryOp(lhs, rhs, opname, e) and
result = GVN_BinaryOp(lhs, rhs, opname)
)
or
exists(GVN child, string opname |
mk_UnaryOp(child, opname, e) and
result = GVN_UnaryOp(child, opname)
)
or
exists(GVN x, GVN i, ControlFlowNode dominator |
mk_ArrayAccess(x, i, dominator, e) and
result = GVN_ArrayAccess(x, i, dominator)
)
or
exists(GVN p, ControlFlowNode dominator |
mk_Deref(p, dominator, e) and
result = GVN_Deref(p, dominator)
)
or
not analyzableExpr(e) and result = GVN_Unanalyzable(e)
}
private predicate analyzableConst(Expr e) {
analyzableIntConst(e) or
analyzableFloatConst(e)
}
/**
* Holds if the expression is explicitly handled by `globalValueNumber`.
* Unanalyzable expressions still need to be given a global value number,
* but it will be a unique number that is not shared with any other
* expression.
*/
private predicate analyzableExpr(Expr e) {
analyzableConst(e) or
analyzableStackVariable(e) or
analyzableDotFieldAccess(e) or
analyzablePointerFieldAccess(e) or
analyzableImplicitThisFieldAccess(e) or
analyzableOtherVariable(e) or
analyzableConversion(e) or
analyzableBinaryOp(e) or
analyzableUnaryOp(e) or
analyzableThisExpr(e) or
analyzableArrayAccess(e) or
analyzablePointerDereferenceExpr(e)
}

View File

@@ -21,14 +21,18 @@
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:8:22:33 | (const char *)... |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:20:22:25 | call to getenv |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:20:22:32 | (const char *)... |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | (const char *)... |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | array to pointer conversion |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | buf |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:31:40:31:53 | dotted_address |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:32:11:32:26 | p#0 |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:11:38:21 | env_pointer |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:25:38:30 | call to getenv |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:38:25:38:37 | (void *)... |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:22:39:22 | a |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:26:39:34 | call to inet_addr |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:36:39:61 | (const char *)... |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:50:39:61 | & ... |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:40:10:40:10 | a |
| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 |

View File

@@ -5,8 +5,8 @@
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:3:21:3:22 | s1 | AST only |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:21:8:21:10 | buf | AST only |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:22:15:22:17 | buf | AST only |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:31:40:31:53 | dotted_address | AST only |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:36:39:61 | (const char *)... | AST only |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | (const char *)... | IR only |
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | array to pointer conversion | IR only |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:51:39:61 | env_pointer | AST only |
| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p | IR only |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |

View File

@@ -48,7 +48,7 @@ struct XY {
void bg_stackstruct(XY s1, XY s2) {
s1.x = source();
if (guarded(s1.x)) {
sink(s1.x); // no flow
sink(s1.x); // no flow [FALSE POSITIVE in AST]
} else if (guarded(s1.y)) {
sink(s1.x); // flow
} else if (guarded(s2.y)) {

View File

@@ -3,6 +3,7 @@
| BarrierGuard.cpp:25:10:25:15 | source | BarrierGuard.cpp:21:17:21:22 | source |
| BarrierGuard.cpp:31:10:31:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
| BarrierGuard.cpp:33:10:33:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
| BarrierGuard.cpp:51:13:51:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
| BarrierGuard.cpp:53:13:53:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
| BarrierGuard.cpp:55:13:55:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
| BarrierGuard.cpp:62:14:62:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source |

View File

@@ -1,3 +1,4 @@
| BarrierGuard.cpp:49:10:49:15 | BarrierGuard.cpp:51:13:51:13 | AST only |
| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:62:14:62:14 | AST only |
| clang.cpp:12:9:12:20 | clang.cpp:22:8:22:20 | AST only |
| clang.cpp:28:27:28:32 | clang.cpp:30:27:30:34 | AST only |

View File

@@ -0,0 +1,17 @@
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:6:40:33 | ! ... | IR only |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:12 | call to strcmp | IR only |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:33 | (bool)... | IR only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:15:50:24 | envStr_ptr | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:28:50:40 | & ... | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:29:50:40 | envStrGlobal | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:2:52:12 | * ... | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:3:52:12 | envStr_ptr | AST only |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:10:64:14 | bytes | IR only |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:23 | call to strlen | IR only |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | (int)... | IR only |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | ... + ... | IR only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:67:7:67:13 | copying | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:69:10:69:13 | copy | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:12:70:15 | copy | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:71:12:71:15 | copy | AST only |

View File

@@ -0,0 +1,16 @@
import semmle.code.cpp.security.TaintTracking as AST
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IR
import cpp
from Expr source, Element tainted, string side
where
AST::taintedIncludingGlobalVars(source, tainted, _) and
not IR::taintedIncludingGlobalVars(source, tainted, _) and
not tainted.getLocation().getFile().getExtension() = "h" and
side = "AST only"
or
IR::taintedIncludingGlobalVars(source, tainted, _) and
not AST::taintedIncludingGlobalVars(source, tainted, _) and
not tainted.getLocation().getFile().getExtension() = "h" and
side = "IR only"
select source, tainted, side

View File

@@ -0,0 +1,49 @@
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:40 | (const char *)... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:6:25:29 | ! ... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:12 | call to strcmp | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:29 | (bool)... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:14:25:19 | envStr | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:6:29:28 | ! ... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:12 | call to strcmp | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:28 | (bool)... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:14:29:19 | envStr | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:14:38:19 | envStr | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:28 | call to getenv | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:40 | (const char *)... | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:6:40:33 | ! ... | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:12 | call to strcmp | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:7:40:33 | (bool)... | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:14:40:19 | envStr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:45:13:45:24 | envStrGlobal | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:14:49:19 | envStr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:28 | call to getenv | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:40 | (const char *)... | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:16:52:21 | envStr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:6:54:35 | ! ... | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:10:27:10:27 | s | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:18:60:25 | userName | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:34 | call to getenv | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:47 | (const char *)... | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:10:64:14 | bytes | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:23 | call to strlen | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | (int)... | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:18:64:37 | ... + ... | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:25:64:32 | userName | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:17:68:24 | userName | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:33 | call to getenv | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:46 | (const char *)... | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:5:70:10 | call to strcpy | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:18:70:25 | userName | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:15:22:15:25 | nptr | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:15:75:18 | call to atoi | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:25 | call to getenv | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:45 | (const char *)... | |

View File

@@ -0,0 +1,7 @@
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
from Expr source, Element tainted, string globalVar
where
taintedIncludingGlobalVars(source, tainted, globalVar) and
not tainted.getLocation().getFile().getExtension() = "h"
select source, tainted, globalVar

View File

@@ -1136,50 +1136,105 @@ ssa.cpp:
# 239| v239_8(void) = AliasedUse : ~m244_5
# 239| v239_9(void) = ExitFunction :
# 247| char StringLiteralAliasing2(bool)
# 247| char* VoidStarIndirectParameters(char*, int)
# 247| Block 0
# 247| v247_1(void) = EnterFunction :
# 247| m247_2(unknown) = AliasedDefinition :
# 247| m247_3(unknown) = InitializeNonLocal :
# 247| m247_4(unknown) = Chi : total:m247_2, partial:m247_3
# 247| mu247_5(unknown) = UnmodeledDefinition :
# 247| r247_6(glval<bool>) = VariableAddress[b] :
# 247| m247_7(bool) = InitializeParameter[b] : &:r247_6
# 248| r248_1(glval<bool>) = VariableAddress[b] :
# 248| r248_2(bool) = Load : &:r248_1, m247_7
# 248| v248_3(void) = ConditionalBranch : r248_2
# 247| v247_1(void) = EnterFunction :
# 247| m247_2(unknown) = AliasedDefinition :
# 247| m247_3(unknown) = InitializeNonLocal :
# 247| m247_4(unknown) = Chi : total:m247_2, partial:m247_3
# 247| mu247_5(unknown) = UnmodeledDefinition :
# 247| r247_6(glval<char *>) = VariableAddress[src] :
# 247| m247_7(char *) = InitializeParameter[src] : &:r247_6
# 247| r247_8(char *) = Load : &:r247_6, m247_7
# 247| m247_9(unknown) = InitializeIndirection[src] : &:r247_8
# 247| r247_10(glval<int>) = VariableAddress[size] :
# 247| m247_11(int) = InitializeParameter[size] : &:r247_10
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
# 248| r248_3(glval<int>) = VariableAddress[size] :
# 248| r248_4(int) = Load : &:r248_3, m247_11
# 248| r248_5(unsigned long) = Convert : r248_4
# 248| r248_6(unsigned long) = Constant[1] :
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
# 248| m248_9(unknown) = ^CallSideEffect : ~m247_9
# 248| m248_10(unknown) = Chi : total:m247_9, partial:m248_9
# 248| r248_11(char *) = Convert : r248_8
# 248| m248_12(char *) = Store : &:r248_1, r248_11
# 249| r249_1(char) = Constant[97] :
# 249| r249_2(glval<char *>) = VariableAddress[src] :
# 249| r249_3(char *) = Load : &:r249_2, m247_7
# 249| r249_4(glval<char>) = CopyValue : r249_3
# 249| m249_5(char) = Store : &:r249_4, r249_1
# 249| m249_6(unknown) = Chi : total:m248_10, partial:m249_5
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
# 250| r250_3(char *) = Load : &:r250_2, m248_12
# 250| r250_4(void *) = Convert : r250_3
# 250| r250_5(glval<char *>) = VariableAddress[src] :
# 250| r250_6(char *) = Load : &:r250_5, m247_7
# 250| r250_7(void *) = Convert : r250_6
# 250| r250_8(glval<int>) = VariableAddress[size] :
# 250| r250_9(int) = Load : &:r250_8, m247_11
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6
# 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
# 250| m250_13(unknown) = Chi : total:m249_6, partial:m250_12
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
# 251| r251_3(char *) = Load : &:r251_2, m248_12
# 251| m251_4(char *) = Store : &:r251_1, r251_3
# 247| v247_12(void) = ReturnIndirection : &:r247_8, ~m250_13
# 247| r247_13(glval<char *>) = VariableAddress[#return] :
# 247| v247_14(void) = ReturnValue : &:r247_13, m251_4
# 247| v247_15(void) = UnmodeledUse : mu*
# 247| v247_16(void) = AliasedUse : ~m250_13
# 247| v247_17(void) = ExitFunction :
# 254| char StringLiteralAliasing2(bool)
# 254| Block 0
# 254| v254_1(void) = EnterFunction :
# 254| m254_2(unknown) = AliasedDefinition :
# 254| m254_3(unknown) = InitializeNonLocal :
# 254| m254_4(unknown) = Chi : total:m254_2, partial:m254_3
# 254| mu254_5(unknown) = UnmodeledDefinition :
# 254| r254_6(glval<bool>) = VariableAddress[b] :
# 254| m254_7(bool) = InitializeParameter[b] : &:r254_6
# 255| r255_1(glval<bool>) = VariableAddress[b] :
# 255| r255_2(bool) = Load : &:r255_1, m254_7
# 255| v255_3(void) = ConditionalBranch : r255_2
#-----| False -> Block 2
#-----| True -> Block 1
# 249| Block 1
# 249| r249_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 249| v249_2(void) = Call : func:r249_1
# 249| m249_3(unknown) = ^CallSideEffect : ~m247_4
# 249| m249_4(unknown) = Chi : total:m247_4, partial:m249_3
# 256| Block 1
# 256| r256_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 256| v256_2(void) = Call : func:r256_1
# 256| m256_3(unknown) = ^CallSideEffect : ~m254_4
# 256| m256_4(unknown) = Chi : total:m254_4, partial:m256_3
#-----| Goto -> Block 3
# 252| Block 2
# 252| r252_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 252| v252_2(void) = Call : func:r252_1
# 252| m252_3(unknown) = ^CallSideEffect : ~m247_4
# 252| m252_4(unknown) = Chi : total:m247_4, partial:m252_3
# 259| Block 2
# 259| r259_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 259| v259_2(void) = Call : func:r259_1
# 259| m259_3(unknown) = ^CallSideEffect : ~m254_4
# 259| m259_4(unknown) = Chi : total:m254_4, partial:m259_3
#-----| Goto -> Block 3
# 255| Block 3
# 255| m255_1(unknown) = Phi : from 1:~m249_4, from 2:~m252_4
# 255| r255_2(glval<char *>) = VariableAddress[s] :
# 255| r255_3(glval<char[8]>) = StringConstant["Literal"] :
# 255| r255_4(char *) = Convert : r255_3
# 255| m255_5(char *) = Store : &:r255_2, r255_4
# 256| r256_1(glval<char>) = VariableAddress[#return] :
# 256| r256_2(glval<char *>) = VariableAddress[s] :
# 256| r256_3(char *) = Load : &:r256_2, m255_5
# 256| r256_4(int) = Constant[2] :
# 256| r256_5(glval<char>) = PointerAdd[1] : r256_3, r256_4
# 256| r256_6(char) = Load : &:r256_5, ~m247_3
# 256| m256_7(char) = Store : &:r256_1, r256_6
# 247| r247_8(glval<char>) = VariableAddress[#return] :
# 247| v247_9(void) = ReturnValue : &:r247_8, m256_7
# 247| v247_10(void) = UnmodeledUse : mu*
# 247| v247_11(void) = AliasedUse : ~m255_1
# 247| v247_12(void) = ExitFunction :
# 262| Block 3
# 262| m262_1(unknown) = Phi : from 1:~m256_4, from 2:~m259_4
# 262| r262_2(glval<char *>) = VariableAddress[s] :
# 262| r262_3(glval<char[8]>) = StringConstant["Literal"] :
# 262| r262_4(char *) = Convert : r262_3
# 262| m262_5(char *) = Store : &:r262_2, r262_4
# 263| r263_1(glval<char>) = VariableAddress[#return] :
# 263| r263_2(glval<char *>) = VariableAddress[s] :
# 263| r263_3(char *) = Load : &:r263_2, m262_5
# 263| r263_4(int) = Constant[2] :
# 263| r263_5(glval<char>) = PointerAdd[1] : r263_3, r263_4
# 263| r263_6(char) = Load : &:r263_5, ~m254_3
# 263| m263_7(char) = Store : &:r263_1, r263_6
# 254| r254_8(glval<char>) = VariableAddress[#return] :
# 254| v254_9(void) = ReturnValue : &:r254_8, m263_7
# 254| v254_10(void) = UnmodeledUse : mu*
# 254| v254_11(void) = AliasedUse : ~m262_1
# 254| v254_12(void) = ExitFunction :

View File

@@ -1131,50 +1131,105 @@ ssa.cpp:
# 239| v239_8(void) = AliasedUse : ~m244_5
# 239| v239_9(void) = ExitFunction :
# 247| char StringLiteralAliasing2(bool)
# 247| char* VoidStarIndirectParameters(char*, int)
# 247| Block 0
# 247| v247_1(void) = EnterFunction :
# 247| m247_2(unknown) = AliasedDefinition :
# 247| m247_3(unknown) = InitializeNonLocal :
# 247| m247_4(unknown) = Chi : total:m247_2, partial:m247_3
# 247| mu247_5(unknown) = UnmodeledDefinition :
# 247| r247_6(glval<bool>) = VariableAddress[b] :
# 247| m247_7(bool) = InitializeParameter[b] : &:r247_6
# 248| r248_1(glval<bool>) = VariableAddress[b] :
# 248| r248_2(bool) = Load : &:r248_1, m247_7
# 248| v248_3(void) = ConditionalBranch : r248_2
# 247| v247_1(void) = EnterFunction :
# 247| m247_2(unknown) = AliasedDefinition :
# 247| m247_3(unknown) = InitializeNonLocal :
# 247| m247_4(unknown) = Chi : total:m247_2, partial:m247_3
# 247| mu247_5(unknown) = UnmodeledDefinition :
# 247| r247_6(glval<char *>) = VariableAddress[src] :
# 247| m247_7(char *) = InitializeParameter[src] : &:r247_6
# 247| r247_8(char *) = Load : &:r247_6, m247_7
# 247| m247_9(unknown) = InitializeIndirection[src] : &:r247_8
# 247| r247_10(glval<int>) = VariableAddress[size] :
# 247| m247_11(int) = InitializeParameter[size] : &:r247_10
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
# 248| r248_3(glval<int>) = VariableAddress[size] :
# 248| r248_4(int) = Load : &:r248_3, m247_11
# 248| r248_5(unsigned long) = Convert : r248_4
# 248| r248_6(unsigned long) = Constant[1] :
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
# 248| m248_9(unknown) = ^CallSideEffect : ~m247_4
# 248| m248_10(unknown) = Chi : total:m247_4, partial:m248_9
# 248| r248_11(char *) = Convert : r248_8
# 248| m248_12(char *) = Store : &:r248_1, r248_11
# 249| r249_1(char) = Constant[97] :
# 249| r249_2(glval<char *>) = VariableAddress[src] :
# 249| r249_3(char *) = Load : &:r249_2, m247_7
# 249| r249_4(glval<char>) = CopyValue : r249_3
# 249| m249_5(char) = Store : &:r249_4, r249_1
# 249| m249_6(unknown) = Chi : total:m247_9, partial:m249_5
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
# 250| r250_3(char *) = Load : &:r250_2, m248_12
# 250| r250_4(void *) = Convert : r250_3
# 250| r250_5(glval<char *>) = VariableAddress[src] :
# 250| r250_6(char *) = Load : &:r250_5, m247_7
# 250| r250_7(void *) = Convert : r250_6
# 250| r250_8(glval<int>) = VariableAddress[size] :
# 250| r250_9(int) = Load : &:r250_8, m247_11
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~m249_6
# 250| m250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
# 250| m250_13(unknown) = Chi : total:m248_10, partial:m250_12
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
# 251| r251_3(char *) = Load : &:r251_2, m248_12
# 251| m251_4(char *) = Store : &:r251_1, r251_3
# 247| v247_12(void) = ReturnIndirection : &:r247_8, ~m249_6
# 247| r247_13(glval<char *>) = VariableAddress[#return] :
# 247| v247_14(void) = ReturnValue : &:r247_13, m251_4
# 247| v247_15(void) = UnmodeledUse : mu*
# 247| v247_16(void) = AliasedUse : ~m250_13
# 247| v247_17(void) = ExitFunction :
# 254| char StringLiteralAliasing2(bool)
# 254| Block 0
# 254| v254_1(void) = EnterFunction :
# 254| m254_2(unknown) = AliasedDefinition :
# 254| m254_3(unknown) = InitializeNonLocal :
# 254| m254_4(unknown) = Chi : total:m254_2, partial:m254_3
# 254| mu254_5(unknown) = UnmodeledDefinition :
# 254| r254_6(glval<bool>) = VariableAddress[b] :
# 254| m254_7(bool) = InitializeParameter[b] : &:r254_6
# 255| r255_1(glval<bool>) = VariableAddress[b] :
# 255| r255_2(bool) = Load : &:r255_1, m254_7
# 255| v255_3(void) = ConditionalBranch : r255_2
#-----| False -> Block 2
#-----| True -> Block 1
# 249| Block 1
# 249| r249_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 249| v249_2(void) = Call : func:r249_1
# 249| m249_3(unknown) = ^CallSideEffect : ~m247_4
# 249| m249_4(unknown) = Chi : total:m247_4, partial:m249_3
# 256| Block 1
# 256| r256_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 256| v256_2(void) = Call : func:r256_1
# 256| m256_3(unknown) = ^CallSideEffect : ~m254_4
# 256| m256_4(unknown) = Chi : total:m254_4, partial:m256_3
#-----| Goto -> Block 3
# 252| Block 2
# 252| r252_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 252| v252_2(void) = Call : func:r252_1
# 252| m252_3(unknown) = ^CallSideEffect : ~m247_4
# 252| m252_4(unknown) = Chi : total:m247_4, partial:m252_3
# 259| Block 2
# 259| r259_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 259| v259_2(void) = Call : func:r259_1
# 259| m259_3(unknown) = ^CallSideEffect : ~m254_4
# 259| m259_4(unknown) = Chi : total:m254_4, partial:m259_3
#-----| Goto -> Block 3
# 255| Block 3
# 255| m255_1(unknown) = Phi : from 1:~m249_4, from 2:~m252_4
# 255| r255_2(glval<char *>) = VariableAddress[s] :
# 255| r255_3(glval<char[8]>) = StringConstant["Literal"] :
# 255| r255_4(char *) = Convert : r255_3
# 255| m255_5(char *) = Store : &:r255_2, r255_4
# 256| r256_1(glval<char>) = VariableAddress[#return] :
# 256| r256_2(glval<char *>) = VariableAddress[s] :
# 256| r256_3(char *) = Load : &:r256_2, m255_5
# 256| r256_4(int) = Constant[2] :
# 256| r256_5(glval<char>) = PointerAdd[1] : r256_3, r256_4
# 256| r256_6(char) = Load : &:r256_5, ~m247_3
# 256| m256_7(char) = Store : &:r256_1, r256_6
# 247| r247_8(glval<char>) = VariableAddress[#return] :
# 247| v247_9(void) = ReturnValue : &:r247_8, m256_7
# 247| v247_10(void) = UnmodeledUse : mu*
# 247| v247_11(void) = AliasedUse : ~m255_1
# 247| v247_12(void) = ExitFunction :
# 262| Block 3
# 262| m262_1(unknown) = Phi : from 1:~m256_4, from 2:~m259_4
# 262| r262_2(glval<char *>) = VariableAddress[s] :
# 262| r262_3(glval<char[8]>) = StringConstant["Literal"] :
# 262| r262_4(char *) = Convert : r262_3
# 262| m262_5(char *) = Store : &:r262_2, r262_4
# 263| r263_1(glval<char>) = VariableAddress[#return] :
# 263| r263_2(glval<char *>) = VariableAddress[s] :
# 263| r263_3(char *) = Load : &:r263_2, m262_5
# 263| r263_4(int) = Constant[2] :
# 263| r263_5(glval<char>) = PointerAdd[1] : r263_3, r263_4
# 263| r263_6(char) = Load : &:r263_5, ~m254_3
# 263| m263_7(char) = Store : &:r263_1, r263_6
# 254| r254_8(glval<char>) = VariableAddress[#return] :
# 254| v254_9(void) = ReturnValue : &:r254_8, m263_7
# 254| v254_10(void) = UnmodeledUse : mu*
# 254| v254_11(void) = AliasedUse : ~m262_1
# 254| v254_12(void) = ExitFunction :

View File

@@ -244,6 +244,13 @@ void ExplicitConstructorCalls() {
c2.g();
}
char *VoidStarIndirectParameters(char *src, int size) {
char *dst = new char[size];
*src = 'a';
memcpy(dst, src, size);
return dst;
}
char StringLiteralAliasing2(bool b) {
if (b) {
ExternalFunc();

View File

@@ -1053,46 +1053,97 @@ ssa.cpp:
# 239| v239_7(void) = AliasedUse : ~mu239_4
# 239| v239_8(void) = ExitFunction :
# 247| char StringLiteralAliasing2(bool)
# 247| char* VoidStarIndirectParameters(char*, int)
# 247| Block 0
# 247| v247_1(void) = EnterFunction :
# 247| mu247_2(unknown) = AliasedDefinition :
# 247| mu247_3(unknown) = InitializeNonLocal :
# 247| mu247_4(unknown) = UnmodeledDefinition :
# 247| r247_5(glval<bool>) = VariableAddress[b] :
# 247| m247_6(bool) = InitializeParameter[b] : &:r247_5
# 248| r248_1(glval<bool>) = VariableAddress[b] :
# 248| r248_2(bool) = Load : &:r248_1, m247_6
# 248| v248_3(void) = ConditionalBranch : r248_2
# 247| v247_1(void) = EnterFunction :
# 247| mu247_2(unknown) = AliasedDefinition :
# 247| mu247_3(unknown) = InitializeNonLocal :
# 247| mu247_4(unknown) = UnmodeledDefinition :
# 247| r247_5(glval<char *>) = VariableAddress[src] :
# 247| m247_6(char *) = InitializeParameter[src] : &:r247_5
# 247| r247_7(char *) = Load : &:r247_5, m247_6
# 247| mu247_8(unknown) = InitializeIndirection[src] : &:r247_7
# 247| r247_9(glval<int>) = VariableAddress[size] :
# 247| m247_10(int) = InitializeParameter[size] : &:r247_9
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
# 248| r248_3(glval<int>) = VariableAddress[size] :
# 248| r248_4(int) = Load : &:r248_3, m247_10
# 248| r248_5(unsigned long) = Convert : r248_4
# 248| r248_6(unsigned long) = Constant[1] :
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
# 248| mu248_9(unknown) = ^CallSideEffect : ~mu247_4
# 248| r248_10(char *) = Convert : r248_8
# 248| m248_11(char *) = Store : &:r248_1, r248_10
# 249| r249_1(char) = Constant[97] :
# 249| r249_2(glval<char *>) = VariableAddress[src] :
# 249| r249_3(char *) = Load : &:r249_2, m247_6
# 249| r249_4(glval<char>) = CopyValue : r249_3
# 249| mu249_5(char) = Store : &:r249_4, r249_1
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
# 250| r250_3(char *) = Load : &:r250_2, m248_11
# 250| r250_4(void *) = Convert : r250_3
# 250| r250_5(glval<char *>) = VariableAddress[src] :
# 250| r250_6(char *) = Load : &:r250_5, m247_6
# 250| r250_7(void *) = Convert : r250_6
# 250| r250_8(glval<int>) = VariableAddress[size] :
# 250| r250_9(int) = Load : &:r250_8, m247_10
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~mu247_4
# 250| mu250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
# 251| r251_3(char *) = Load : &:r251_2, m248_11
# 251| m251_4(char *) = Store : &:r251_1, r251_3
# 247| v247_11(void) = ReturnIndirection : &:r247_7, ~mu247_4
# 247| r247_12(glval<char *>) = VariableAddress[#return] :
# 247| v247_13(void) = ReturnValue : &:r247_12, m251_4
# 247| v247_14(void) = UnmodeledUse : mu*
# 247| v247_15(void) = AliasedUse : ~mu247_4
# 247| v247_16(void) = ExitFunction :
# 254| char StringLiteralAliasing2(bool)
# 254| Block 0
# 254| v254_1(void) = EnterFunction :
# 254| mu254_2(unknown) = AliasedDefinition :
# 254| mu254_3(unknown) = InitializeNonLocal :
# 254| mu254_4(unknown) = UnmodeledDefinition :
# 254| r254_5(glval<bool>) = VariableAddress[b] :
# 254| m254_6(bool) = InitializeParameter[b] : &:r254_5
# 255| r255_1(glval<bool>) = VariableAddress[b] :
# 255| r255_2(bool) = Load : &:r255_1, m254_6
# 255| v255_3(void) = ConditionalBranch : r255_2
#-----| False -> Block 2
#-----| True -> Block 1
# 249| Block 1
# 249| r249_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 249| v249_2(void) = Call : func:r249_1
# 249| mu249_3(unknown) = ^CallSideEffect : ~mu247_4
# 256| Block 1
# 256| r256_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 256| v256_2(void) = Call : func:r256_1
# 256| mu256_3(unknown) = ^CallSideEffect : ~mu254_4
#-----| Goto -> Block 3
# 252| Block 2
# 252| r252_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 252| v252_2(void) = Call : func:r252_1
# 252| mu252_3(unknown) = ^CallSideEffect : ~mu247_4
# 259| Block 2
# 259| r259_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 259| v259_2(void) = Call : func:r259_1
# 259| mu259_3(unknown) = ^CallSideEffect : ~mu254_4
#-----| Goto -> Block 3
# 255| Block 3
# 255| r255_1(glval<char *>) = VariableAddress[s] :
# 255| r255_2(glval<char[8]>) = StringConstant["Literal"] :
# 255| r255_3(char *) = Convert : r255_2
# 255| m255_4(char *) = Store : &:r255_1, r255_3
# 256| r256_1(glval<char>) = VariableAddress[#return] :
# 256| r256_2(glval<char *>) = VariableAddress[s] :
# 256| r256_3(char *) = Load : &:r256_2, m255_4
# 256| r256_4(int) = Constant[2] :
# 256| r256_5(glval<char>) = PointerAdd[1] : r256_3, r256_4
# 256| r256_6(char) = Load : &:r256_5, ~mu247_4
# 256| m256_7(char) = Store : &:r256_1, r256_6
# 247| r247_7(glval<char>) = VariableAddress[#return] :
# 247| v247_8(void) = ReturnValue : &:r247_7, m256_7
# 247| v247_9(void) = UnmodeledUse : mu*
# 247| v247_10(void) = AliasedUse : ~mu247_4
# 247| v247_11(void) = ExitFunction :
# 262| Block 3
# 262| r262_1(glval<char *>) = VariableAddress[s] :
# 262| r262_2(glval<char[8]>) = StringConstant["Literal"] :
# 262| r262_3(char *) = Convert : r262_2
# 262| m262_4(char *) = Store : &:r262_1, r262_3
# 263| r263_1(glval<char>) = VariableAddress[#return] :
# 263| r263_2(glval<char *>) = VariableAddress[s] :
# 263| r263_3(char *) = Load : &:r263_2, m262_4
# 263| r263_4(int) = Constant[2] :
# 263| r263_5(glval<char>) = PointerAdd[1] : r263_3, r263_4
# 263| r263_6(char) = Load : &:r263_5, ~mu254_4
# 263| m263_7(char) = Store : &:r263_1, r263_6
# 254| r254_7(glval<char>) = VariableAddress[#return] :
# 254| v254_8(void) = ReturnValue : &:r254_7, m263_7
# 254| v254_9(void) = UnmodeledUse : mu*
# 254| v254_10(void) = AliasedUse : ~mu254_4
# 254| v254_11(void) = ExitFunction :

View File

@@ -1053,46 +1053,97 @@ ssa.cpp:
# 239| v239_7(void) = AliasedUse : ~mu239_4
# 239| v239_8(void) = ExitFunction :
# 247| char StringLiteralAliasing2(bool)
# 247| char* VoidStarIndirectParameters(char*, int)
# 247| Block 0
# 247| v247_1(void) = EnterFunction :
# 247| mu247_2(unknown) = AliasedDefinition :
# 247| mu247_3(unknown) = InitializeNonLocal :
# 247| mu247_4(unknown) = UnmodeledDefinition :
# 247| r247_5(glval<bool>) = VariableAddress[b] :
# 247| m247_6(bool) = InitializeParameter[b] : &:r247_5
# 248| r248_1(glval<bool>) = VariableAddress[b] :
# 248| r248_2(bool) = Load : &:r248_1, m247_6
# 248| v248_3(void) = ConditionalBranch : r248_2
# 247| v247_1(void) = EnterFunction :
# 247| mu247_2(unknown) = AliasedDefinition :
# 247| mu247_3(unknown) = InitializeNonLocal :
# 247| mu247_4(unknown) = UnmodeledDefinition :
# 247| r247_5(glval<char *>) = VariableAddress[src] :
# 247| m247_6(char *) = InitializeParameter[src] : &:r247_5
# 247| r247_7(char *) = Load : &:r247_5, m247_6
# 247| mu247_8(unknown) = InitializeIndirection[src] : &:r247_7
# 247| r247_9(glval<int>) = VariableAddress[size] :
# 247| m247_10(int) = InitializeParameter[size] : &:r247_9
# 248| r248_1(glval<char *>) = VariableAddress[dst] :
# 248| r248_2(glval<unknown>) = FunctionAddress[operator new[]] :
# 248| r248_3(glval<int>) = VariableAddress[size] :
# 248| r248_4(int) = Load : &:r248_3, m247_10
# 248| r248_5(unsigned long) = Convert : r248_4
# 248| r248_6(unsigned long) = Constant[1] :
# 248| r248_7(unsigned long) = Mul : r248_5, r248_6
# 248| r248_8(void *) = Call : func:r248_2, 0:r248_7
# 248| mu248_9(unknown) = ^CallSideEffect : ~mu247_4
# 248| r248_10(char *) = Convert : r248_8
# 248| m248_11(char *) = Store : &:r248_1, r248_10
# 249| r249_1(char) = Constant[97] :
# 249| r249_2(glval<char *>) = VariableAddress[src] :
# 249| r249_3(char *) = Load : &:r249_2, m247_6
# 249| r249_4(glval<char>) = CopyValue : r249_3
# 249| mu249_5(char) = Store : &:r249_4, r249_1
# 250| r250_1(glval<unknown>) = FunctionAddress[memcpy] :
# 250| r250_2(glval<char *>) = VariableAddress[dst] :
# 250| r250_3(char *) = Load : &:r250_2, m248_11
# 250| r250_4(void *) = Convert : r250_3
# 250| r250_5(glval<char *>) = VariableAddress[src] :
# 250| r250_6(char *) = Load : &:r250_5, m247_6
# 250| r250_7(void *) = Convert : r250_6
# 250| r250_8(glval<int>) = VariableAddress[size] :
# 250| r250_9(int) = Load : &:r250_8, m247_10
# 250| r250_10(void *) = Call : func:r250_1, 0:r250_4, 1:r250_7, 2:r250_9
# 250| v250_11(void) = ^SizedBufferReadSideEffect[1] : &:r250_7, r250_9, ~mu247_4
# 250| mu250_12(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r250_4, r250_9
# 251| r251_1(glval<char *>) = VariableAddress[#return] :
# 251| r251_2(glval<char *>) = VariableAddress[dst] :
# 251| r251_3(char *) = Load : &:r251_2, m248_11
# 251| m251_4(char *) = Store : &:r251_1, r251_3
# 247| v247_11(void) = ReturnIndirection : &:r247_7, ~mu247_4
# 247| r247_12(glval<char *>) = VariableAddress[#return] :
# 247| v247_13(void) = ReturnValue : &:r247_12, m251_4
# 247| v247_14(void) = UnmodeledUse : mu*
# 247| v247_15(void) = AliasedUse : ~mu247_4
# 247| v247_16(void) = ExitFunction :
# 254| char StringLiteralAliasing2(bool)
# 254| Block 0
# 254| v254_1(void) = EnterFunction :
# 254| mu254_2(unknown) = AliasedDefinition :
# 254| mu254_3(unknown) = InitializeNonLocal :
# 254| mu254_4(unknown) = UnmodeledDefinition :
# 254| r254_5(glval<bool>) = VariableAddress[b] :
# 254| m254_6(bool) = InitializeParameter[b] : &:r254_5
# 255| r255_1(glval<bool>) = VariableAddress[b] :
# 255| r255_2(bool) = Load : &:r255_1, m254_6
# 255| v255_3(void) = ConditionalBranch : r255_2
#-----| False -> Block 2
#-----| True -> Block 1
# 249| Block 1
# 249| r249_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 249| v249_2(void) = Call : func:r249_1
# 249| mu249_3(unknown) = ^CallSideEffect : ~mu247_4
# 256| Block 1
# 256| r256_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 256| v256_2(void) = Call : func:r256_1
# 256| mu256_3(unknown) = ^CallSideEffect : ~mu254_4
#-----| Goto -> Block 3
# 252| Block 2
# 252| r252_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 252| v252_2(void) = Call : func:r252_1
# 252| mu252_3(unknown) = ^CallSideEffect : ~mu247_4
# 259| Block 2
# 259| r259_1(glval<unknown>) = FunctionAddress[ExternalFunc] :
# 259| v259_2(void) = Call : func:r259_1
# 259| mu259_3(unknown) = ^CallSideEffect : ~mu254_4
#-----| Goto -> Block 3
# 255| Block 3
# 255| r255_1(glval<char *>) = VariableAddress[s] :
# 255| r255_2(glval<char[8]>) = StringConstant["Literal"] :
# 255| r255_3(char *) = Convert : r255_2
# 255| m255_4(char *) = Store : &:r255_1, r255_3
# 256| r256_1(glval<char>) = VariableAddress[#return] :
# 256| r256_2(glval<char *>) = VariableAddress[s] :
# 256| r256_3(char *) = Load : &:r256_2, m255_4
# 256| r256_4(int) = Constant[2] :
# 256| r256_5(glval<char>) = PointerAdd[1] : r256_3, r256_4
# 256| r256_6(char) = Load : &:r256_5, ~mu247_4
# 256| m256_7(char) = Store : &:r256_1, r256_6
# 247| r247_7(glval<char>) = VariableAddress[#return] :
# 247| v247_8(void) = ReturnValue : &:r247_7, m256_7
# 247| v247_9(void) = UnmodeledUse : mu*
# 247| v247_10(void) = AliasedUse : ~mu247_4
# 247| v247_11(void) = ExitFunction :
# 262| Block 3
# 262| r262_1(glval<char *>) = VariableAddress[s] :
# 262| r262_2(glval<char[8]>) = StringConstant["Literal"] :
# 262| r262_3(char *) = Convert : r262_2
# 262| m262_4(char *) = Store : &:r262_1, r262_3
# 263| r263_1(glval<char>) = VariableAddress[#return] :
# 263| r263_2(glval<char *>) = VariableAddress[s] :
# 263| r263_3(char *) = Load : &:r263_2, m262_4
# 263| r263_4(int) = Constant[2] :
# 263| r263_5(glval<char>) = PointerAdd[1] : r263_3, r263_4
# 263| r263_6(char) = Load : &:r263_5, ~mu254_4
# 263| m263_7(char) = Store : &:r263_1, r263_6
# 254| r254_7(glval<char>) = VariableAddress[#return] :
# 254| v254_8(void) = ReturnValue : &:r254_7, m263_7
# 254| v254_9(void) = UnmodeledUse : mu*
# 254| v254_10(void) = AliasedUse : ~mu254_4
# 254| v254_11(void) = ExitFunction :