mirror of
https://github.com/github/codeql.git
synced 2025-12-22 03:36:30 +01:00
Merge from master
This commit is contained in:
@@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
@@ -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 *)... | |
|
||||
@@ -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
|
||||
@@ -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 :
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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 :
|
||||
|
||||
Reference in New Issue
Block a user