Merge branch 'master' into rdmarsh/cpp/ir-flow-through-outparams

This commit is contained in:
Robert Marsh
2020-02-06 11:42:30 -08:00
152 changed files with 9656 additions and 4799 deletions

View File

@@ -2041,12 +2041,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result = getSuccMid()
or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
mid.getNode() = result.getNode() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
sink.getConfiguration() = unbind(mid.getConfiguration()) and
result = sink
)
}

View File

@@ -2041,12 +2041,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result = getSuccMid()
or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
mid.getNode() = result.getNode() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
sink.getConfiguration() = unbind(mid.getConfiguration()) and
result = sink
)
}

View File

@@ -2041,12 +2041,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result = getSuccMid()
or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
mid.getNode() = result.getNode() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
sink.getConfiguration() = unbind(mid.getConfiguration()) and
result = sink
)
}

View File

@@ -2041,12 +2041,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result = getSuccMid()
or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
mid.getNode() = result.getNode() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
sink.getConfiguration() = unbind(mid.getConfiguration()) and
result = sink
)
}

View File

@@ -2041,12 +2041,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result = getSuccMid()
or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
mid.getNode() = result.getNode() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
sink.getConfiguration() = unbind(mid.getConfiguration()) and
result = sink
)
}

View File

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

View File

@@ -21,6 +21,40 @@ private predicate predictableInstruction(Instruction instr) {
predictableInstruction(instr.(UnaryInstruction).getUnary())
}
/**
* Functions that we should only allow taint to flow through (to the return
* value) if all but the source argument are 'predictable'. This is done to
* emulate the old security library's implementation rather than due to any
* strong belief that this is the right approach.
*
* Note that the list itself is not very principled; it consists of all the
* functions listed in the old security library's [default] `isPureFunction`
* that have more than one argument, but are not in the old taint tracking
* library's `returnArgument` predicate. In addition, `strlen` is included
* because it's also a special case in flow to return values.
*/
predicate predictableOnlyFlow(string name) {
name = "strcasestr" or
name = "strchnul" or
name = "strchr" or
name = "strchrnul" or
name = "strcmp" or
name = "strcspn" or
name = "strlen" or // special case
name = "strncmp" or
name = "strndup" or
name = "strnlen" or
name = "strrchr" or
name = "strspn" or
name = "strstr" or
name = "strtod" or
name = "strtof" or
name = "strtol" or
name = "strtoll" or
name = "strtoq" or
name = "strtoul"
}
private DataFlow::Node getNodeForSource(Expr source) {
isUserInput(source, _) and
(
@@ -123,15 +157,16 @@ private predicate nodeIsBarrier(DataFlow::Node node) {
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// Expressions computed from tainted data are also tainted
i2 =
any(CallInstruction call |
isPureFunction(call.getStaticCallTarget().getName()) and
call.getAnArgument() = i1 and
forall(Instruction arg | arg = call.getAnArgument() | arg = i1 or predictableInstruction(arg)) and
// flow through `strlen` tends to cause dubious results, if the length is
// bounded.
not call.getStaticCallTarget().getName() = "strlen"
)
exists(CallInstruction call, int argIndex | call = i2 |
isPureFunction(call.getStaticCallTarget().getName()) and
i1 = getACallArgumentOrIndirection(call, argIndex) and
forall(Instruction arg | arg = call.getAnArgument() |
arg = getACallArgumentOrIndirection(call, argIndex) or predictableInstruction(arg)
) and
// flow through `strlen` tends to cause dubious results, if the length is
// bounded.
not call.getStaticCallTarget().getName() = "strlen"
)
or
// Flow through pointer dereference
i2.(LoadInstruction).getSourceAddress() = i1
@@ -174,7 +209,8 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
any(CallInstruction call |
exists(int indexIn |
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
i1 = getACallArgumentOrIndirection(call, indexIn)
i1 = getACallArgumentOrIndirection(call, indexIn) and
not predictableOnlyFlow(call.getStaticCallTarget().getName())
)
)
or

View File

@@ -2041,12 +2041,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result = getSuccMid()
or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
mid.getNode() = result.getNode() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
sink.getConfiguration() = unbind(mid.getConfiguration()) and
result = sink
)
}

View File

@@ -2041,12 +2041,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result = getSuccMid()
or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
mid.getNode() = result.getNode() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
sink.getConfiguration() = unbind(mid.getConfiguration()) and
result = sink
)
}

View File

@@ -2041,12 +2041,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result = getSuccMid()
or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
mid.getNode() = result.getNode() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
sink.getConfiguration() = unbind(mid.getConfiguration()) and
result = sink
)
}

View File

@@ -2041,12 +2041,12 @@ private class PathNodeMid extends PathNode, TPathNodeMid {
result = getSuccMid()
or
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
exists(PathNodeMid mid |
exists(PathNodeMid mid, PathNodeSink sink |
mid = getSuccMid() and
mid.getNode() = result.getNode() and
mid.getNode() = sink.getNode() and
mid.getAp() instanceof AccessPathNil and
result instanceof PathNodeSink and
result.getConfiguration() = unbind(mid.getConfiguration())
sink.getConfiguration() = unbind(mid.getConfiguration()) and
result = sink
)
}

View File

@@ -6,6 +6,7 @@ private import cpp
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.models.interfaces.DataFlow
/**
* A newtype wrapper to prevent accidental casts between `Node` and
@@ -289,6 +290,51 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// Flow through the partial operand belongs in the taint-tracking libraries
// for now.
iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
or
// Flow through modeled functions
modelFlow(iFrom, iTo)
}
private predicate modelFlow(Instruction iFrom, Instruction iTo) {
exists(
CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut
|
call.getStaticCallTarget() = func and
func.hasDataFlow(modelIn, modelOut)
|
(
modelOut.isReturnValue() and
iTo = call
or
// TODO: Add write side effects for return values
modelOut.isReturnValueDeref() and
iTo = call
or
exists(WriteSideEffectInstruction outNode |
modelOut.isParameterDeref(outNode.getIndex()) and
iTo = outNode and
outNode.getPrimaryInstruction() = call
)
// TODO: add write side effects for qualifiers
) and
(
exists(int index |
modelIn.isParameter(index) and
iFrom = call.getPositionalArgument(index)
)
or
exists(int index, ReadSideEffectInstruction read |
modelIn.isParameterDeref(index) and
read.getIndex() = index and
read.getPrimaryInstruction() = call and
iFrom = read.getSideEffectOperand().getAnyDef()
)
or
modelIn.isQualifierAddress() and
iFrom = call.getThisArgument()
// TODO: add read side effects for qualifiers
)
)
}
/**

View File

@@ -63,6 +63,7 @@ private newtype TOpcode =
TUnmodeledDefinition() or
TUnmodeledUse() or
TAliasedDefinition() or
TInitializeNonLocal() or
TAliasedUse() or
TPhi() or
TBuiltIn() or
@@ -596,6 +597,14 @@ module Opcode {
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess }
}
class InitializeNonLocal extends Opcode, TInitializeNonLocal {
final override string toString() { result = "InitializeNonLocal" }
final override MemoryAccessKind getWriteMemoryAccess() {
result instanceof NonLocalMemoryAccess
}
}
class AliasedUse extends Opcode, TAliasedUse {
final override string toString() { result = "AliasedUse" }

View File

@@ -52,6 +52,11 @@ newtype TValueNumber =
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
TLoadTotalOverlapValueNumber(
IRFunction irFunc, IRType type, ValueNumber memOperand, ValueNumber operand
) {
loadTotalOverlapValueNumber(_, irFunc, type, memOperand, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
/**
@@ -101,12 +106,18 @@ class ValueNumber extends TValueNumber {
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
*/
private class CongruentCopyInstruction extends CopyInstruction {
class CongruentCopyInstruction extends CopyInstruction {
CongruentCopyInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
}
}
class LoadTotalOverlapInstruction extends LoadInstruction {
LoadTotalOverlapInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustTotallyOverlap
}
}
/**
* Holds if this library knows how to assign a value number to the specified instruction, other than
* a `unique` value number that is never shared by multiple instructions.
@@ -131,6 +142,8 @@ private predicate numberableInstruction(Instruction instr) {
instr instanceof PointerArithmeticInstruction
or
instr instanceof CongruentCopyInstruction
or
instr instanceof LoadTotalOverlapInstruction
}
private predicate variableAddressValueNumber(
@@ -205,6 +218,7 @@ private predicate unaryValueNumber(
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
valueNumber(instr.getUnary()) = operand
@@ -221,6 +235,16 @@ private predicate inheritanceConversionValueNumber(
valueNumber(instr.getUnary()) = operand
}
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, ValueNumber memOperand,
ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
valueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
valueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand
}
/**
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
@@ -313,6 +337,11 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) {
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, ValueNumber memOperand, ValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
)

View File

@@ -26,7 +26,7 @@ private predicate hasResultMemoryAccess(
type = languageType.getIRType() and
isIndirectOrBufferMemoryAccess(instr.getResultMemoryAccess()) and
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
if exists(type.getByteSize())
if type.getByteSize() > 0
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
else endBitOffset = Ints::unknown()
)
@@ -43,7 +43,7 @@ private predicate hasOperandMemoryAccess(
type = languageType.getIRType() and
isIndirectOrBufferMemoryAccess(operand.getMemoryAccess()) and
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
if exists(type.getByteSize())
if type.getByteSize() > 0
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
else endBitOffset = Ints::unknown()
)
@@ -298,7 +298,7 @@ class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
final override string toStringInternal() { result = "{AllNonLocal}" }
final override VirtualVariable getVirtualVariable() { result = TAllAliasedMemory(irFunc, false) }
final override AliasedVirtualVariable getVirtualVariable() { result.getIRFunction() = irFunc }
final override Language::LanguageType getType() {
result = any(IRUnknownType type).getCanonicalLanguageType()
@@ -311,6 +311,14 @@ class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
final override string getUniqueId() { result = "{AllNonLocal}" }
final override predicate isMayAccess() { isMayAccess = true }
override predicate canDefineReadOnly() {
// A "must" access that defines all non-local memory appears only on the `InitializeNonLocal`
// instruction, which provides the initial definition for all memory outside of the current
// function's stack frame. This memory includes string literals and other read-only globals, so
// we allow such an access to be the definition for a use of a read-only location.
not isMayAccess()
}
}
/**
@@ -341,16 +349,6 @@ class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation {
class AliasedVirtualVariable extends AllAliasedMemory, VirtualVariable {
AliasedVirtualVariable() { not isMayAccess() }
override predicate canDefineReadOnly() {
// A must-def of all aliased memory is only used in two places:
// 1. In the prologue of the function, to provide a definition for all memory defined before the
// function was called. In this case, it needs to provide a definition even for read-only
// non-local variables.
// 2. As the result of a `Chi` instruction. These don't participate in overlap analysis, so it's
// OK if we let this predicate hold in that case.
any()
}
}
/**
@@ -405,10 +403,16 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
use instanceof AllNonLocalMemory and
result instanceof MustExactlyOverlap
or
// AllNonLocalMemory may partially overlap any location within the same virtual variable,
// except a local variable.
result instanceof MayPartiallyOverlap and
not use.isAlwaysAllocatedOnStack()
not use instanceof AllNonLocalMemory and
not use.isAlwaysAllocatedOnStack() and
if use instanceof VariableMemoryLocation
then
// AllNonLocalMemory totally overlaps any non-local variable.
result instanceof MustTotallyOverlap
else
// AllNonLocalMemory may partially overlap any other location within the same virtual
// variable, except a stack variable.
result instanceof MayPartiallyOverlap
)
or
def.getVirtualVariable() = use.getVirtualVariable() and

View File

@@ -759,7 +759,21 @@ module DefUse {
then defLocation = useLocation
else (
definitionHasPhiNode(defLocation, block) and
defLocation = useLocation.getVirtualVariable()
defLocation = useLocation.getVirtualVariable() and
// Handle the unusual case where a virtual variable does not overlap one of its member
// locations. For example, a definition of the virtual variable representing all aliased
// memory does not overlap a use of a string literal, because the contents of a string
// literal can never be redefined. The string literal's location could still be a member of
// the `AliasedVirtualVariable` due to something like:
// ```
// char s[10];
// strcpy(s, p);
// const char* p = b ? "SomeLiteral" : s;
// return p[3];
// ```
// In the above example, `p[3]` may access either the string literal or the local variable
// `s`, so both of those locations must be members of the `AliasedVirtualVariable`.
exists(Alias::getOverlap(defLocation, useLocation))
)
)
or

View File

@@ -52,6 +52,11 @@ newtype TValueNumber =
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
TLoadTotalOverlapValueNumber(
IRFunction irFunc, IRType type, ValueNumber memOperand, ValueNumber operand
) {
loadTotalOverlapValueNumber(_, irFunc, type, memOperand, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
/**
@@ -101,12 +106,18 @@ class ValueNumber extends TValueNumber {
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
*/
private class CongruentCopyInstruction extends CopyInstruction {
class CongruentCopyInstruction extends CopyInstruction {
CongruentCopyInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
}
}
class LoadTotalOverlapInstruction extends LoadInstruction {
LoadTotalOverlapInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustTotallyOverlap
}
}
/**
* Holds if this library knows how to assign a value number to the specified instruction, other than
* a `unique` value number that is never shared by multiple instructions.
@@ -131,6 +142,8 @@ private predicate numberableInstruction(Instruction instr) {
instr instanceof PointerArithmeticInstruction
or
instr instanceof CongruentCopyInstruction
or
instr instanceof LoadTotalOverlapInstruction
}
private predicate variableAddressValueNumber(
@@ -205,6 +218,7 @@ private predicate unaryValueNumber(
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
valueNumber(instr.getUnary()) = operand
@@ -221,6 +235,16 @@ private predicate inheritanceConversionValueNumber(
valueNumber(instr.getUnary()) = operand
}
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, ValueNumber memOperand,
ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
valueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
valueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand
}
/**
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
@@ -313,6 +337,11 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) {
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, ValueNumber memOperand, ValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
)

View File

@@ -12,12 +12,10 @@ newtype TInstructionTag =
ZeroPadStringElementIndexTag() or
ZeroPadStringElementAddressTag() or
ZeroPadStringStoreTag() or
AssignOperationLoadTag() or
AssignOperationConvertLeftTag() or
AssignOperationOpTag() or
AssignOperationConvertResultTag() or
AssignmentStoreTag() or
CrementLoadTag() or
CrementConstantTag() or
CrementOpTag() or
CrementStoreTag() or
@@ -28,6 +26,7 @@ newtype TInstructionTag =
UnmodeledDefinitionTag() or
UnmodeledUseTag() or
AliasedDefinitionTag() or
InitializeNonLocalTag() or
AliasedUseTag() or
SwitchBranchTag() or
CallTargetTag() or
@@ -94,8 +93,6 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = ZeroPadStringStoreTag() and result = "ZeroPadStore"
or
tag = AssignOperationLoadTag() and result = "AssignOpLoad"
or
tag = AssignOperationConvertLeftTag() and result = "AssignOpConvLeft"
or
tag = AssignOperationOpTag() and result = "AssignOpOp"
@@ -104,8 +101,6 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = AssignmentStoreTag() and result = "AssignStore"
or
tag = CrementLoadTag() and result = "CrementLoad"
or
tag = CrementConstantTag() and result = "CrementConst"
or
tag = CrementOpTag() and result = "CrementOp"
@@ -126,6 +121,8 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = AliasedDefinitionTag() and result = "AliasedDef"
or
tag = InitializeNonLocalTag() and result = "InitNonLocal"
or
tag = AliasedUseTag() and result = "AliasedUse"
or
tag = SwitchBranchTag() and result = "SwitchBranch"

View File

@@ -208,7 +208,7 @@ private predicate usedAsCondition(Expr expr) {
* AST as an lvalue-to-rvalue conversion, but the IR represents both a function
* lvalue and a function pointer prvalue the same.
*/
predicate ignoreLoad(Expr expr) {
private predicate ignoreLoad(Expr expr) {
expr.hasLValueToRValueConversion() and
(
expr instanceof ThisExpr or
@@ -220,6 +220,34 @@ predicate ignoreLoad(Expr expr) {
)
}
/**
* Holds if `expr` should have a load on it because it will be loaded as part
* of the translation of its parent. We want to associate this load with `expr`
* itself rather than its parent since in practical applications like data flow
* we maintain that the value of the `x` in `x++` should be what's loaded from
* `x`.
*/
private predicate needsLoadForParentExpr(Expr expr) {
exists(CrementOperation crement | expr = crement.getOperand().getFullyConverted())
or
exists(AssignOperation ao | expr = ao.getLValue().getFullyConverted())
}
/**
* Holds if `expr` should have a `TranslatedLoad` on it.
*/
predicate hasTranslatedLoad(Expr expr) {
(
expr.hasLValueToRValueConversion()
or
needsLoadForParentExpr(expr)
) and
not ignoreExpr(expr) and
not isNativeCondition(expr) and
not isFlexibleCondition(expr) and
not ignoreLoad(expr)
}
newtype TTranslatedElement =
// An expression that is not being consumed as a condition
TTranslatedValueExpr(Expr expr) {
@@ -229,21 +257,12 @@ newtype TTranslatedElement =
} or
// A separate element to handle the lvalue-to-rvalue conversion step of an
// expression.
TTranslatedLoad(Expr expr) {
not ignoreExpr(expr) and
not isNativeCondition(expr) and
not isFlexibleCondition(expr) and
expr.hasLValueToRValueConversion() and
not ignoreLoad(expr)
} or
TTranslatedLoad(Expr expr) { hasTranslatedLoad(expr) } or
// For expressions that would not otherwise generate an instruction.
TTranslatedResultCopy(Expr expr) {
not ignoreExpr(expr) and
exprNeedsCopyIfNotLoaded(expr) and
// Doesn't have a TTranslatedLoad
not (
expr.hasLValueToRValueConversion() and
not ignoreLoad(expr)
)
not hasTranslatedLoad(expr)
} or
// An expression most naturally translated as control flow.
TTranslatedNativeCondition(Expr expr) {

View File

@@ -63,9 +63,12 @@ abstract class TranslatedExpr extends TranslatedElement {
* Holds if the result of this `TranslatedExpr` is a glvalue.
*/
predicate isResultGLValue() {
// This implementation is overridden in `TranslatedCoreExpr` to mark them
// as glvalues if they have loads on them. It's not overridden in
// `TranslatedResultCopy` since result copies never have loads.
// This implementation is overridden in `TranslatedCoreExpr` to mark them as
// glvalues if they have loads on them. It's also overridden in
// `TranslatedLoad` to always mark loads as glvalues since a
// `TranslatedLoad` may have been created as a result of
// `needsLoadForParentExpr`. It's not overridden in `TranslatedResultCopy`
// since result copies never have loads.
expr.isGLValueCategory()
}
@@ -103,18 +106,13 @@ abstract class TranslatedCoreExpr extends TranslatedExpr {
or
// If this TranslatedExpr doesn't produce the result, then it must represent
// a glvalue that is then loaded by a TranslatedLoad.
hasLoad()
}
final predicate hasLoad() {
expr.hasLValueToRValueConversion() and
not ignoreLoad(expr)
hasTranslatedLoad(expr)
}
final override predicate producesExprResult() {
// If there's no load, then this is the only TranslatedExpr for this
// expression.
not hasLoad() and
not hasTranslatedLoad(expr) and
// If there's a result copy, then this expression's result is the copy.
not exprNeedsCopyIfNotLoaded(expr)
}
@@ -270,6 +268,8 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
resultType = getResultType()
}
override predicate isResultGLValue() { none() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = LoadTag() and
result = getParent().getChildSuccessor(this) and
@@ -298,7 +298,7 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
any()
}
private TranslatedCoreExpr getOperand() { result.getExpr() = expr }
TranslatedCoreExpr getOperand() { result.getExpr() = expr }
}
/**
@@ -387,7 +387,7 @@ private int getElementSize(Type type) {
abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
override CrementOperation expr;
final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
final override TranslatedElement getChild(int id) { id = 0 and result = getLoadedOperand() }
final override string getInstructionConstantValue(InstructionTag tag) {
tag = CrementConstantTag() and
@@ -416,10 +416,6 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
}
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = CrementLoadTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(expr.getType())
or
tag = CrementConstantTag() and
opcode instanceof Opcode::Constant and
resultType = getConstantType()
@@ -434,19 +430,10 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = CrementLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getOperand().getResult()
or
operandTag instanceof LoadOperandTag and
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
tag = CrementOpTag() and
(
operandTag instanceof LeftOperandTag and
result = getInstruction(CrementLoadTag())
result = getLoadedOperand().getResult()
or
operandTag instanceof RightOperandTag and
result = getInstruction(CrementConstantTag())
@@ -455,21 +442,20 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
tag = CrementStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getOperand().getResult()
result = getUnloadedOperand().getResult()
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(CrementOpTag())
)
}
final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
final override Instruction getFirstInstruction() {
result = getLoadedOperand().getFirstInstruction()
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
kind instanceof GotoEdge and
(
tag = CrementLoadTag() and
result = getInstruction(CrementConstantTag())
or
tag = CrementConstantTag() and
result = getInstruction(CrementOpTag())
or
@@ -482,7 +468,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getOperand() and result = getInstruction(CrementLoadTag())
child = getLoadedOperand() and result = getInstruction(CrementConstantTag())
}
final override int getInstructionElementSize(InstructionTag tag) {
@@ -494,10 +480,20 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
result = getElementSize(expr.getType())
}
final TranslatedExpr getOperand() {
/**
* Gets the `TranslatedLoad` on the `e` in this `e++`, which is the element
* that holds the value to be cremented. It's guaranteed that there's a load
* on `e` because of the `needsLoadForParentExpr` predicate.
*/
final TranslatedLoad getLoadedOperand() {
result = getTranslatedExpr(expr.getOperand().getFullyConverted())
}
/**
* Gets the address to which the result of this crement will be stored.
*/
final TranslatedExpr getUnloadedOperand() { result = getLoadedOperand().getOperand() }
final Opcode getOpcode() {
exists(Type resultType |
resultType = expr.getUnspecifiedType() and
@@ -534,17 +530,14 @@ class TranslatedPrefixCrementOperation extends TranslatedCrementOperation {
else
// This is C++, where the result is an lvalue for the operand, and that
// lvalue is not being loaded as part of this expression.
result = getOperand().getResult()
result = getUnloadedOperand().getResult()
}
}
class TranslatedPostfixCrementOperation extends TranslatedCrementOperation {
override PostfixCrementOperation expr;
override Instruction getResult() {
// The result is a prvalue copy of the original value
result = getInstruction(CrementLoadTag())
}
override Instruction getResult() { result = getLoadedOperand().getResult() }
}
/**
@@ -1231,8 +1224,8 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
}
}
abstract class TranslatedAssignment extends TranslatedNonConstantExpr {
override Assignment expr;
class TranslatedAssignExpr extends TranslatedNonConstantExpr {
override AssignExpr expr;
final override TranslatedElement getChild(int id) {
id = 0 and result = getLeftOperand()
@@ -1252,7 +1245,7 @@ abstract class TranslatedAssignment extends TranslatedNonConstantExpr {
// value assigned to the left operand. If this is C++, then the result is
// an lvalue, but that lvalue is being loaded as part of this expression.
// EDG doesn't mark this as a load.
result = getStoredValue()
result = getRightOperand().getResult()
else
// This is C++, where the result is an lvalue for the left operand,
// and that lvalue is not being loaded as part of this expression.
@@ -1268,10 +1261,6 @@ abstract class TranslatedAssignment extends TranslatedNonConstantExpr {
final TranslatedExpr getRightOperand() {
result = getTranslatedExpr(expr.getRValue().getFullyConverted())
}
}
class TranslatedAssignExpr extends TranslatedAssignment {
TranslatedAssignExpr() { expr instanceof AssignExpr }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = AssignmentStoreTag() and
@@ -1304,23 +1293,57 @@ class TranslatedAssignExpr extends TranslatedAssignment {
result = getRightOperand().getResult()
)
}
override Instruction getStoredValue() { result = getRightOperand().getResult() }
}
class TranslatedAssignOperation extends TranslatedAssignment {
class TranslatedAssignOperation extends TranslatedNonConstantExpr {
override AssignOperation expr;
final override TranslatedElement getChild(int id) {
id = 0 and result = getLoadedLeftOperand()
or
id = 1 and result = getRightOperand()
}
final override Instruction getFirstInstruction() {
// Evaluation is right-to-left
result = getRightOperand().getFirstInstruction()
}
final override Instruction getResult() {
if expr.isPRValueCategory()
then
// If this is C, then the result of an assignment is a prvalue for the new
// value assigned to the left operand. If this is C++, then the result is
// an lvalue, but that lvalue is being loaded as part of this expression.
// EDG doesn't mark this as a load.
result = getStoredValue()
else
// This is C++, where the result is an lvalue for the left operand,
// and that lvalue is not being loaded as part of this expression.
result = getUnloadedLeftOperand().getResult()
}
final TranslatedExpr getUnloadedLeftOperand() { result = getLoadedLeftOperand().getOperand() }
/**
* Gets the `TranslatedLoad` on the `e` in this `e += ...` which is the
* element that holds the value to be cremented. It's guaranteed that there's
* a load on `e` because of the `needsLoadForParentExpr` predicate.
*/
final TranslatedLoad getLoadedLeftOperand() {
result = getTranslatedExpr(expr.getLValue().getFullyConverted())
}
/**
* Gets the address to which the result of this operation will be stored.
*/
final TranslatedExpr getRightOperand() {
result = getTranslatedExpr(expr.getRValue().getFullyConverted())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
kind instanceof GotoEdge and
(
(
tag = AssignOperationLoadTag() and
if leftOperandNeedsConversion()
then result = getInstruction(AssignOperationConvertLeftTag())
else result = getInstruction(AssignOperationOpTag())
)
or
tag = AssignOperationConvertLeftTag() and
result = getInstruction(AssignOperationOpTag())
or
@@ -1342,13 +1365,15 @@ class TranslatedAssignOperation extends TranslatedAssignment {
override Instruction getChildSuccessor(TranslatedElement child) {
// Operands are evaluated right-to-left.
child = getRightOperand() and
result = getLeftOperand().getFirstInstruction()
result = getLoadedLeftOperand().getFirstInstruction()
or
child = getLeftOperand() and
result = getInstruction(AssignOperationLoadTag())
child = getLoadedLeftOperand() and
if leftOperandNeedsConversion()
then result = getInstruction(AssignOperationConvertLeftTag())
else result = getInstruction(AssignOperationOpTag())
}
override Instruction getStoredValue() {
private Instruction getStoredValue() {
if leftOperandNeedsConversion()
then result = getInstruction(AssignOperationConvertResultTag())
else result = getInstruction(AssignOperationOpTag())
@@ -1368,7 +1393,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
// anyway. If we really want to model this case perfectly, we'll need the
// extractor to tell us what the promoted type of the left operand would
// be.
result = getLeftOperand().getExpr().getType()
result = getLoadedLeftOperand().getExpr().getType()
else
// The right operand has already been converted to the type of the op.
result = getRightOperand().getExpr().getType()
@@ -1376,7 +1401,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
private predicate leftOperandNeedsConversion() {
getConvertedLeftOperandType().getUnspecifiedType() !=
getLeftOperand().getExpr().getUnspecifiedType()
getLoadedLeftOperand().getExpr().getUnspecifiedType()
}
private Opcode getOpcode() {
@@ -1406,10 +1431,6 @@ class TranslatedAssignOperation extends TranslatedAssignment {
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = AssignOperationLoadTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(getLeftOperand().getExpr().getType())
or
tag = AssignOperationOpTag() and
opcode = getOpcode() and
resultType = getTypeForPRValue(getConvertedLeftOperandType())
@@ -1425,7 +1446,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
resultType = getTypeForPRValue(getConvertedLeftOperandType())
or
tag = AssignOperationConvertResultTag() and
resultType = getTypeForPRValue(getLeftOperand().getExpr().getType())
resultType = getTypeForPRValue(getLoadedLeftOperand().getExpr().getType())
)
}
@@ -1439,19 +1460,10 @@ class TranslatedAssignOperation extends TranslatedAssignment {
}
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = AssignOperationLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getLeftOperand().getResult()
or
operandTag instanceof LoadOperandTag and
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
leftOperandNeedsConversion() and
tag = AssignOperationConvertLeftTag() and
operandTag instanceof UnaryOperandTag and
result = getInstruction(AssignOperationLoadTag())
result = getLoadedLeftOperand().getResult()
or
tag = AssignOperationOpTag() and
(
@@ -1459,7 +1471,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
operandTag instanceof LeftOperandTag and
if leftOperandNeedsConversion()
then result = getInstruction(AssignOperationConvertLeftTag())
else result = getInstruction(AssignOperationLoadTag())
else result = getLoadedLeftOperand().getResult()
)
or
operandTag instanceof RightOperandTag and
@@ -1474,7 +1486,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
tag = AssignmentStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getLeftOperand().getResult()
result = getUnloadedLeftOperand().getResult()
or
operandTag instanceof StoreValueOperandTag and
result = getStoredValue()
@@ -2457,6 +2469,9 @@ predicate exprNeedsCopyIfNotLoaded(Expr expr) {
expr instanceof PrefixCrementOperation and
not expr.isPRValueCategory() // is C++
or
// Because the load is on the `e` in `e++`.
expr instanceof PostfixCrementOperation
or
expr instanceof PointerDereferenceExpr
or
expr instanceof AddressOfExpr

View File

@@ -71,6 +71,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
result = getInstruction(AliasedDefinitionTag())
or
tag = AliasedDefinitionTag() and
result = getInstruction(InitializeNonLocalTag())
or
tag = InitializeNonLocalTag() and
result = getInstruction(UnmodeledDefinitionTag())
or
(
@@ -144,6 +147,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
opcode instanceof Opcode::AliasedDefinition and
resultType = getUnknownType()
or
tag = InitializeNonLocalTag() and
opcode instanceof Opcode::InitializeNonLocal and
resultType = getUnknownType()
or
tag = InitializeThisTag() and
opcode instanceof Opcode::InitializeThis and
resultType = getTypeForGLValue(getThisType())

View File

@@ -52,6 +52,11 @@ newtype TValueNumber =
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
TLoadTotalOverlapValueNumber(
IRFunction irFunc, IRType type, ValueNumber memOperand, ValueNumber operand
) {
loadTotalOverlapValueNumber(_, irFunc, type, memOperand, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
/**
@@ -101,12 +106,18 @@ class ValueNumber extends TValueNumber {
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
*/
private class CongruentCopyInstruction extends CopyInstruction {
class CongruentCopyInstruction extends CopyInstruction {
CongruentCopyInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
}
}
class LoadTotalOverlapInstruction extends LoadInstruction {
LoadTotalOverlapInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustTotallyOverlap
}
}
/**
* Holds if this library knows how to assign a value number to the specified instruction, other than
* a `unique` value number that is never shared by multiple instructions.
@@ -131,6 +142,8 @@ private predicate numberableInstruction(Instruction instr) {
instr instanceof PointerArithmeticInstruction
or
instr instanceof CongruentCopyInstruction
or
instr instanceof LoadTotalOverlapInstruction
}
private predicate variableAddressValueNumber(
@@ -205,6 +218,7 @@ private predicate unaryValueNumber(
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
valueNumber(instr.getUnary()) = operand
@@ -221,6 +235,16 @@ private predicate inheritanceConversionValueNumber(
valueNumber(instr.getUnary()) = operand
}
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, ValueNumber memOperand,
ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
valueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
valueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand
}
/**
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
@@ -313,6 +337,11 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) {
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, ValueNumber memOperand, ValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
)

View File

@@ -759,7 +759,21 @@ module DefUse {
then defLocation = useLocation
else (
definitionHasPhiNode(defLocation, block) and
defLocation = useLocation.getVirtualVariable()
defLocation = useLocation.getVirtualVariable() and
// Handle the unusual case where a virtual variable does not overlap one of its member
// locations. For example, a definition of the virtual variable representing all aliased
// memory does not overlap a use of a string literal, because the contents of a string
// literal can never be redefined. The string literal's location could still be a member of
// the `AliasedVirtualVariable` due to something like:
// ```
// char s[10];
// strcpy(s, p);
// const char* p = b ? "SomeLiteral" : s;
// return p[3];
// ```
// In the above example, `p[3]` may access either the string literal or the local variable
// `s`, so both of those locations must be members of the `AliasedVirtualVariable`.
exists(Alias::getOverlap(defLocation, useLocation))
)
)
or

View File

@@ -187,6 +187,13 @@ private predicate boundFlowStepSsa(
guard.controls(op2.getUse().getBlock(), testIsTrue) and
reason = TCondReason(guard)
)
or
exists(IRGuardCondition guard, boolean testIsTrue, SafeCastInstruction cast |
valueNumberOfOperand(op2) = valueNumber(cast.getUnary()) and
guard = boundFlowCond(valueNumber(cast), op1, delta, upper, testIsTrue) and
guard.controls(op2.getUse().getBlock(), testIsTrue) and
reason = TCondReason(guard)
)
}
/**
@@ -259,7 +266,7 @@ private predicate safeCast(IntegralType fromtyp, IntegralType totyp) {
private class SafeCastInstruction extends ConvertInstruction {
SafeCastInstruction() {
safeCast(getResultType(), getUnary().getResultType())
safeCast(getUnary().getResultType(), getResultType())
or
getResultType() instanceof PointerType and
getUnary().getResultType() instanceof PointerType

View File

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

View File

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

View File

@@ -79,6 +79,15 @@ void test_dynamic_cast() {
dynamic_cast<D3*>(b2)->f(getenv("VAR")); // tainted [FALSE POSITIVE]
}
namespace std {
template< class T >
T&& move( T&& t ) noexcept;
}
void test_std_move() {
sink(std::move(getenv("VAR")));
}
void flow_to_outparam(char ** ret, char *arg) {
*ret = arg;
}

View File

@@ -93,13 +93,21 @@
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:79:30:79:35 | call to getenv |
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:79:30:79:42 | (const char *)... |
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:82:42:82:44 | arg |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:83:12:83:14 | arg |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:88:27:88:32 | call to getenv |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:89:10:89:11 | (const char *)... |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:89:10:89:11 | p2 |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:84:17:84:17 | t |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:16 | call to move |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (const char *)... |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:23 | call to getenv |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:91:42:91:44 | arg |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:12:92:14 | arg |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:97:27:97:32 | call to getenv |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | (const char *)... |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
| test_diff.cpp:92:10:92:13 | argv | defaulttainttracking.cpp:9:11:9:20 | p#0 |
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:1:11:1:20 | p#0 |
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:13 | argv |

View File

@@ -9,13 +9,19 @@
| 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 |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 | IR only |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:82:31:82:33 | ret | AST only |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:83:5:83:8 | * ... | AST only |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:83:6:83:8 | ret | AST only |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:89:10:89:11 | (const char *)... | IR only |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | defaulttainttracking.cpp:89:10:89:11 | p2 | IR only |
| defaulttainttracking.cpp:88:27:88:32 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:16 | call to move | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (const char *)... | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 | IR only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 | IR only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:91:31:91:33 | ret | AST only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:5:92:8 | * ... | AST only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:92:6:92:8 | ret | AST only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | (const char *)... | IR only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 | IR only |
| defaulttainttracking.cpp:97:27:97:32 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 | IR only |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |
| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:36:24:36:24 | p | AST only |
| test_diff.cpp:111:10:111:13 | argv | defaulttainttracking.cpp:9:11:9:20 | p#0 | AST only |

View File

@@ -0,0 +1,16 @@
void test_crement() {
int x1 = 0;
++x1;
int x2 = 0;
x2++;
int x3 = 0;
x3 -= 1; // flow
int x4 = 0;
x4 |= 1; // flow
int x5 = 0;
x5 = x5 - 1; // flow (to RHS)
}

View File

@@ -0,0 +1,5 @@
| crements.cpp:3:5:3:6 | x1 |
| crements.cpp:6:3:6:4 | x2 |
| crements.cpp:9:3:9:4 | x3 |
| crements.cpp:12:3:12:4 | x4 |
| crements.cpp:15:8:15:9 | x5 |

View File

@@ -0,0 +1,14 @@
import cpp
import semmle.code.cpp.ir.dataflow.DataFlow
class Cfg extends DataFlow::Configuration {
Cfg() { this = "from0::Cfg" }
override predicate isSource(DataFlow::Node source) { source.asExpr().getValue() = "0" }
override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof VariableAccess }
}
from Cfg cfg, Expr sink
where cfg.hasFlowToExpr(sink)
select sink

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
| 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:68:28:68:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:67:7:67:13 | copying | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:69:10:69:13 | copy | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:12:70:15 | copy | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:71:12:71:15 | copy | AST only |

View File

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

View File

@@ -0,0 +1,42 @@
| 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: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:25:64:32 | userName | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:17:68:24 | userName | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:33 | call to getenv | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:46 | (const char *)... | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:5:70:10 | call to strcpy | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:18:70:25 | userName | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:15:22:15:25 | nptr | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:15:75:18 | call to atoi | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:25 | call to getenv | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:45 | (const char *)... | |

View File

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

View File

@@ -34,7 +34,6 @@
| taint.cpp:352:7:352:7 | taint.cpp:330:6:330:11 | AST only |
| taint.cpp:372:7:372:7 | taint.cpp:365:24:365:29 | AST only |
| taint.cpp:374:7:374:7 | taint.cpp:365:24:365:29 | AST only |
| taint.cpp:382:7:382:7 | taint.cpp:377:23:377:28 | AST only |
| taint.cpp:391:7:391:7 | taint.cpp:385:27:385:32 | AST only |
| taint.cpp:423:7:423:7 | taint.cpp:422:14:422:19 | AST only |
| taint.cpp:424:9:424:17 | taint.cpp:422:14:422:19 | AST only |

View File

@@ -17,5 +17,6 @@
| taint.cpp:291:7:291:7 | y | taint.cpp:275:6:275:11 | call to source |
| taint.cpp:337:7:337:7 | t | taint.cpp:330:6:330:11 | call to source |
| taint.cpp:350:7:350:7 | t | taint.cpp:330:6:330:11 | call to source |
| taint.cpp:382:7:382:7 | a | taint.cpp:377:23:377:28 | source |
| taint.cpp:429:7:429:7 | b | taint.cpp:428:13:428:18 | call to source |
| taint.cpp:430:9:430:14 | member | taint.cpp:428:13:428:18 | call to source |

View File

@@ -5753,9 +5753,9 @@ ir.cpp:
# 851| 0: [VariableDeclarationEntry] definition of d
# 851| Type = [Struct] PolymorphicDerived
# 851| init: [Initializer] initializer for d
# 851| expr: [ConstructorCall] call to PolymorphicDerived
# 851| Type = [VoidType] void
# 851| ValueCategory = prvalue
#-----| expr: [ConstructorCall] call to PolymorphicDerived
#-----| Type = [VoidType] void
#-----| ValueCategory = prvalue
# 853| 2: [DeclStmt] declaration
# 853| 0: [VariableDeclarationEntry] definition of pb
# 853| Type = [PointerType] PolymorphicBase *

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -243,3 +243,22 @@ void ExplicitConstructorCalls() {
Constructible c2 = Constructible(2);
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();
}
else {
ExternalFunc();
}
const char* s = "Literal";
return s[2];
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
| test.cpp:9:12:9:35 | call to MyInt |
| test.cpp:9:5:9:36 | call to MyInt |
| test.cpp:26:18:26:23 | call to MyInt |
| test.cpp:42:15:42:15 | call to operator+ |
| test.cpp:43:5:43:5 | call to operator+= |

View File

@@ -1,2 +1 @@
| non_permissive.cpp:6:3:6:3 | call to f | non_permissive.cpp:2:13:2:13 | f |
| permissive.cpp:6:3:6:3 | call to f | permissive.cpp:2:13:2:13 | f |

View File

@@ -59,3 +59,8 @@
| test.cpp:183:10:183:10 | Load: i | test.cpp:175:23:175:23 | InitializeParameter: x | -1 | true | CompareLT: ... < ... | test.cpp:182:9:182:13 | test.cpp:182:9:182:13 |
| test.cpp:185:10:185:10 | Load: i | test.cpp:175:23:175:23 | InitializeParameter: x | 0 | true | CompareLT: ... < ... | test.cpp:176:7:176:11 | test.cpp:176:7:176:11 |
| test.cpp:187:10:187:10 | Store: i | test.cpp:175:23:175:23 | InitializeParameter: x | 0 | false | CompareLT: ... < ... | test.cpp:182:9:182:13 | test.cpp:182:9:182:13 |
| test.cpp:194:8:194:8 | Load: l | test.cpp:191:16:191:16 | InitializeParameter: i | 0 | false | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
| test.cpp:194:8:194:8 | Load: l | test.cpp:191:16:191:16 | InitializeParameter: i | 0 | true | NoReason | file://:0:0:0:0 | file://:0:0:0:0 |
| test.cpp:200:10:200:10 | Load: i | test.cpp:198:25:198:25 | InitializeParameter: l | -1 | true | CompareLT: ... < ... | test.cpp:199:7:199:11 | test.cpp:199:7:199:11 |
| test.cpp:203:11:203:11 | Load: i | test.cpp:198:25:198:25 | InitializeParameter: l | -3 | true | CompareLT: ... < ... | test.cpp:202:7:202:15 | test.cpp:202:7:202:15 |
| test.cpp:209:10:209:10 | Load: x | test.cpp:207:24:207:24 | InitializeParameter: y | -3 | true | CompareLT: ... < ... | test.cpp:208:7:208:15 | test.cpp:208:7:208:15 |

View File

@@ -186,3 +186,26 @@ int test15(int i, int x) {
}
return i;
}
// safe integer type conversion
int test16(int i) {
long l;
l = i;
sink(l);
}
// implicit integer casts
void test17(int i, long l) {
if (i < l) {
sink(i);
}
if (i < l - 2) {
sink (i);
}
}
void test18(int x, int y) {
if (x < y - 2) {
sink(x);
}
}

View File

@@ -52,9 +52,9 @@
| test.c:18:10:18:14 | Load: count | positive |
| test.c:18:10:18:14 | Store: count | positive |
| test.c:23:10:23:10 | Phi: p | positive |
| test.c:24:5:24:9 | Load: count | positive |
| test.c:24:5:24:11 | Add: ... ++ | positive strictlyPositive |
| test.c:24:5:24:11 | Constant: ... ++ | positive strictlyPositive |
| test.c:24:5:24:11 | Load: ... ++ | positive |
| test.c:24:5:24:11 | Store: ... ++ | positive strictlyPositive |
| test.c:25:5:25:22 | Store: ... = ... | positive |
| test.c:25:13:25:17 | Load: count | positive strictlyPositive |
@@ -70,8 +70,8 @@
| test.c:33:26:33:26 | Load: i | positive |
| test.c:33:26:33:28 | Add: ... + ... | positive strictlyPositive |
| test.c:33:28:33:28 | Constant: 1 | positive strictlyPositive |
| test.c:34:5:34:9 | Load: total | positive |
| test.c:34:5:34:14 | Add: ... += ... | positive |
| test.c:34:5:34:14 | Load: ... += ... | positive |
| test.c:34:5:34:14 | Store: ... += ... | positive |
| test.c:34:14:34:14 | Load: i | positive |
| test.c:36:10:36:14 | Load: total | positive |
@@ -82,12 +82,12 @@
| test.c:42:15:42:15 | Phi: i | positive |
| test.c:42:15:42:15 | Phi: i | positive |
| test.c:42:19:42:19 | Constant: 2 | positive strictlyPositive |
| test.c:42:22:42:22 | Load: i | positive |
| test.c:42:22:42:24 | Add: ... ++ | positive strictlyPositive |
| test.c:42:22:42:24 | Constant: ... ++ | positive strictlyPositive |
| test.c:42:22:42:24 | Load: ... ++ | positive |
| test.c:42:22:42:24 | Store: ... ++ | positive strictlyPositive |
| test.c:43:5:43:9 | Load: total | positive |
| test.c:43:5:43:14 | Add: ... += ... | positive |
| test.c:43:5:43:14 | Load: ... += ... | positive |
| test.c:43:5:43:14 | Store: ... += ... | positive |
| test.c:43:14:43:14 | Load: i | positive |
| test.c:45:10:45:14 | Load: total | positive |
@@ -104,8 +104,8 @@
| test.c:51:28:51:28 | Load: i | positive |
| test.c:51:28:51:30 | Add: ... + ... | positive strictlyPositive |
| test.c:51:30:51:30 | Constant: 1 | positive strictlyPositive |
| test.c:52:5:52:9 | Load: total | positive |
| test.c:52:5:52:14 | Add: ... += ... | positive |
| test.c:52:5:52:14 | Load: ... += ... | positive |
| test.c:52:5:52:14 | Store: ... += ... | positive |
| test.c:52:14:52:14 | Load: i | positive |
| test.c:54:10:54:14 | Load: total | positive |
@@ -147,8 +147,8 @@
| test.c:124:36:124:36 | Constant: (unsigned long long)... | positive strictlyPositive |
| test.c:126:31:126:43 | Call: call to test12_helper | positive |
| test.c:126:31:126:43 | Store: call to test12_helper | positive |
| test.c:127:6:127:10 | Load: Start | positive |
| test.c:127:6:127:24 | Add: ... += ... | positive strictlyPositive |
| test.c:127:6:127:24 | Load: ... += ... | positive |
| test.c:127:6:127:24 | Store: ... += ... | positive strictlyPositive |
| test.c:127:15:127:20 | Load: Length | positive |
| test.c:127:15:127:24 | Add: ... + ... | positive strictlyPositive |
@@ -252,8 +252,8 @@
| test.c:205:13:205:15 | Mul: ... * ... | positive |
| test.c:205:13:205:15 | Store: ... * ... | positive |
| test.c:205:15:205:15 | Load: b | positive |
| test.c:206:5:206:9 | Load: total | positive |
| test.c:206:5:206:14 | Add: ... += ... | positive |
| test.c:206:5:206:14 | Load: ... += ... | positive |
| test.c:206:5:206:14 | Store: ... += ... | positive |
| test.c:206:14:206:14 | Load: r | positive |
| test.c:208:7:208:7 | Constant: 3 | positive strictlyPositive |
@@ -263,7 +263,7 @@
| test.c:208:28:208:30 | Constant: - ... | negative strictlyNegative |
| test.c:208:45:208:46 | Constant: 23 | positive strictlyPositive |
| test.c:209:13:209:13 | Load: a | positive strictlyPositive |
| test.c:210:5:210:14 | Load: ... += ... | positive |
| test.c:210:5:210:9 | Load: total | positive |
| test.c:212:7:212:7 | Constant: 3 | positive strictlyPositive |
| test.c:212:17:212:17 | Load: a | positive strictlyPositive |
| test.c:212:22:212:23 | Constant: 11 | positive strictlyPositive |
@@ -304,8 +304,8 @@
| test.c:233:13:233:15 | Mul: ... * ... | positive |
| test.c:233:13:233:15 | Store: ... * ... | positive |
| test.c:233:15:233:15 | Load: b | positive |
| test.c:234:5:234:9 | Load: total | positive |
| test.c:234:5:234:14 | Add: ... += ... | positive |
| test.c:234:5:234:14 | Load: ... += ... | positive |
| test.c:234:5:234:14 | Store: ... += ... | positive |
| test.c:234:14:234:14 | Load: r | positive |
| test.c:236:7:236:7 | Phi: 0 | positive |
@@ -314,7 +314,7 @@
| test.c:236:28:236:30 | Constant: - ... | negative strictlyNegative |
| test.c:236:45:236:46 | Constant: 23 | positive strictlyPositive |
| test.c:237:13:237:13 | Load: a | positive |
| test.c:238:5:238:14 | Load: ... += ... | positive |
| test.c:238:5:238:9 | Load: total | positive |
| test.c:240:17:240:17 | Load: a | positive |
| test.c:240:22:240:23 | Constant: 11 | positive strictlyPositive |
| test.c:240:28:240:30 | Constant: - ... | negative strictlyNegative |
@@ -375,8 +375,8 @@
| test.c:289:13:289:15 | Mul: ... * ... | negative |
| test.c:289:13:289:15 | Store: ... * ... | negative |
| test.c:289:15:289:15 | Load: b | positive |
| test.c:290:5:290:9 | Load: total | negative |
| test.c:290:5:290:14 | Add: ... += ... | negative |
| test.c:290:5:290:14 | Load: ... += ... | negative |
| test.c:290:5:290:14 | Store: ... += ... | negative |
| test.c:290:14:290:14 | Load: r | negative |
| test.c:292:7:292:9 | Constant: - ... | negative strictlyNegative |
@@ -384,7 +384,7 @@
| test.c:292:29:292:31 | Constant: - ... | negative strictlyNegative |
| test.c:292:46:292:47 | Constant: 23 | positive strictlyPositive |
| test.c:293:13:293:13 | Load: a | negative |
| test.c:294:5:294:14 | Load: ... += ... | negative |
| test.c:294:5:294:9 | Load: total | negative |
| test.c:296:7:296:9 | Constant: - ... | negative strictlyNegative |
| test.c:296:29:296:31 | Constant: - ... | negative strictlyNegative |
| test.c:297:13:297:13 | Load: a | negative |
@@ -421,8 +421,8 @@
| test.c:317:13:317:15 | Mul: ... * ... | negative |
| test.c:317:13:317:15 | Store: ... * ... | negative |
| test.c:317:15:317:15 | Load: b | positive |
| test.c:318:5:318:9 | Load: total | negative |
| test.c:318:5:318:14 | Add: ... += ... | negative |
| test.c:318:5:318:14 | Load: ... += ... | negative |
| test.c:318:5:318:14 | Store: ... += ... | negative |
| test.c:318:14:318:14 | Load: r | negative |
| test.c:320:7:320:9 | Constant: - ... | negative strictlyNegative |
@@ -431,7 +431,7 @@
| test.c:320:30:320:32 | Constant: - ... | negative strictlyNegative |
| test.c:320:47:320:48 | Constant: 23 | positive strictlyPositive |
| test.c:321:13:321:13 | Load: a | negative strictlyNegative |
| test.c:322:5:322:14 | Load: ... += ... | negative |
| test.c:322:5:322:9 | Load: total | negative |
| test.c:324:7:324:9 | Constant: - ... | negative strictlyNegative |
| test.c:324:24:324:25 | Constant: - ... | negative strictlyNegative |
| test.c:324:30:324:32 | Constant: - ... | negative strictlyNegative |
@@ -454,9 +454,9 @@
| test.c:342:10:342:10 | Load: i | positive |
| test.c:342:10:342:10 | Phi: i | positive |
| test.c:342:14:342:14 | Constant: 3 | positive strictlyPositive |
| test.c:343:5:343:5 | Load: i | positive |
| test.c:343:5:343:7 | Add: ... ++ | positive strictlyPositive |
| test.c:343:5:343:7 | Constant: ... ++ | positive strictlyPositive |
| test.c:343:5:343:7 | Load: ... ++ | positive |
| test.c:343:5:343:7 | Store: ... ++ | positive strictlyPositive |
| test.c:345:3:345:7 | Store: ... = ... | positive strictlyPositive |
| test.c:345:7:345:7 | Load: i | positive strictlyPositive |
@@ -646,18 +646,18 @@
| test.c:397:3:397:15 | Store: ... = ... | positive strictlyPositive |
| test.c:397:9:397:11 | Add: ++ ... | positive strictlyPositive |
| test.c:397:9:397:11 | Constant: ++ ... | positive strictlyPositive |
| test.c:397:9:397:11 | Load: ++ ... | positive |
| test.c:397:9:397:11 | Store: ++ ... | positive strictlyPositive |
| test.c:397:9:397:14 | CopyValue: ... , ... | positive strictlyPositive |
| test.c:397:11:397:11 | Load: y | positive |
| test.c:397:14:397:14 | Load: y | positive strictlyPositive |
| test.c:398:3:398:23 | Store: ... = ... | positive strictlyPositive |
| test.c:398:9:398:9 | Load: y | positive strictlyPositive |
| test.c:398:9:398:11 | Add: ... ++ | positive strictlyPositive |
| test.c:398:9:398:11 | Constant: ... ++ | positive strictlyPositive |
| test.c:398:9:398:11 | Load: ... ++ | positive strictlyPositive |
| test.c:398:9:398:11 | Store: ... ++ | positive strictlyPositive |
| test.c:398:9:398:22 | CopyValue: ... , ... | positive strictlyPositive |
| test.c:398:14:398:14 | Load: y | positive strictlyPositive |
| test.c:398:14:398:19 | Add: ... += ... | positive strictlyPositive |
| test.c:398:14:398:19 | Load: ... += ... | positive strictlyPositive |
| test.c:398:14:398:19 | Store: ... += ... | positive strictlyPositive |
| test.c:398:19:398:19 | Constant: (unsigned int)... | positive strictlyPositive |
| test.c:398:22:398:22 | Load: y | positive strictlyPositive |

View File

@@ -30,3 +30,11 @@
| test.cpp:105:11:105:12 | (Base *)... | 105:c11-c12 106:c14-c35 107:c11-c12 |
| test.cpp:105:11:105:12 | pd | 105:c11-c12 106:c33-c34 |
| test.cpp:105:15:105:15 | b | 105:c15-c15 107:c15-c15 109:c10-c10 |
| test.cpp:125:11:125:12 | pa | 125:c11-c12 126:c11-c12 128:c3-c4 129:c11-c12 |
| test.cpp:125:15:125:15 | x | 125:c15-c15 126:c15-c15 128:c7-c7 |
| test.cpp:136:11:136:18 | global_a | 136:c11-c18 137:c11-c18 139:c3-c10 |
| test.cpp:136:21:136:21 | x | 136:c21-c21 137:c21-c21 139:c13-c13 |
| test.cpp:144:11:144:12 | pa | 144:c11-c12 145:c11-c12 147:c3-c4 149:c11-c12 |
| test.cpp:145:15:145:15 | y | 145:c15-c15 147:c7-c7 |
| test.cpp:153:11:153:18 | global_a | 153:c11-c18 154:c11-c18 156:c3-c10 |
| test.cpp:153:21:153:21 | x | 153:c21-c21 154:c21-c21 |

View File

@@ -42,7 +42,7 @@ int test03(int p0, int p1, int* p2) {
x = p0 + p1 + global03;
*p2 = 0; // Might change global03
x = p0 + p1 + global03; // Not the same value
x = p0 + p1 + global03; // BUG: Not the same value, but given the same value number (this is likely due to #2696)
y = x;
}
@@ -115,3 +115,45 @@ void test06() {
"a";
"c";
}
struct A {
int x;
int y;
};
void test_read_arg_same(A *pa, int n) {
int b = pa->x;
int c = pa->x;
pa->x = n;
int d = pa->x;
}
A* global_a;
int global_n;
void test_read_global_same() {
int b = global_a->x;
int c = global_a->x;
global_a->x = global_n;
int d = global_a->x;
}
void test_read_arg_different(A *pa) {
int b = pa->x;
int c = pa->y;
pa->y = global_n;
int d = pa->x;
}
void test_read_global_different(int n) {
int b = global_a->x;
int c = global_a->x;
global_a->y = n;
int d = global_a->x;
}

View File

@@ -17,7 +17,7 @@ void test_not_same_basic_block(int *p) {
void test_indirect(int **p) {
int x;
x = **p;
if (*p == nullptr) { // BAD [NOT DETECTED]
if (*p == nullptr) { // BAD
return;
}
}

View File

@@ -1,5 +1,6 @@
| RedundantNullCheckSimple.cpp:4:7:4:7 | Load: p | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:3:7:3:8 | Load: * ... | dereferenced here |
| RedundantNullCheckSimple.cpp:13:8:13:8 | Load: p | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:10:11:10:12 | Load: * ... | dereferenced here |
| RedundantNullCheckSimple.cpp:20:7:20:8 | Load: * ... | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:19:7:19:9 | Load: * ... | dereferenced here |
| RedundantNullCheckSimple.cpp:48:12:48:12 | Load: p | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:51:10:51:11 | Load: * ... | dereferenced here |
| RedundantNullCheckSimple.cpp:79:7:79:9 | Load: * ... | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:78:7:78:10 | Load: * ... | dereferenced here |
| RedundantNullCheckSimple.cpp:93:13:93:13 | Load: p | This null check is redundant because the value is $@ in any case | RedundantNullCheckSimple.cpp:92:13:92:18 | Load: * ... | dereferenced here |

View File

@@ -35,7 +35,7 @@ void processRequest()
adminPrivileges = 0; // OK, since it's a 0 and not a 1
}
// BAD, but it requires pointer analysis to catch
// BAD (requires pointer analysis to catch)
const char** userp = &currentUser;
*userp = userName;
if (!strcmp(currentUser, "admin")) {

View File

@@ -42,7 +42,7 @@
| class.cpp:91:27:91:29 | num | class.cpp:87:17:87:19 | num | V |
| class.cpp:100:24:100:34 | type mention | class.cpp:94:18:94:28 | string_type | T |
| class.cpp:105:1:105:15 | type mention | class.cpp:97:7:97:21 | StringContainer | T |
| class.cpp:106:9:106:23 | type mention | class.cpp:100:2:100:16 | StringContainer | M |
| class.cpp:106:9:106:23 | type mention | class.cpp:97:7:97:21 | StringContainer | T |
| class.cpp:106:25:106:27 | STR(x) | class.cpp:95:1:95:18 | #define STR(x) L ## x | X |
| class.cpp:117:2:117:29 | type mention | class.cpp:109:7:109:34 | myClassWithConstructorParams | T |
| class.cpp:117:37:117:37 | a | class.cpp:115:27:115:27 | a | V |