mirror of
https://github.com/github/codeql.git
synced 2025-12-23 20:26:32 +01:00
Merge branch 'main' of https://github.com/microsoft/codeql
This commit is contained in:
@@ -25,6 +25,10 @@ import semmle.code.cpp.dataflow.DataFlow2
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
deprecated module TaintTracking {
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTracking
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingParameter::Public
|
||||
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
|
||||
private import semmle.code.cpp.dataflow.internal.TaintTrackingImplSpecific
|
||||
private import codeql.dataflow.TaintTracking
|
||||
import TaintFlowMake<CppOldDataFlow, CppOldTaintTracking>
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Provides C++-specific definitions for use in the taint tracking library.
|
||||
*/
|
||||
|
||||
private import codeql.dataflow.TaintTracking
|
||||
private import DataFlowImplSpecific
|
||||
|
||||
module CppOldTaintTracking implements InputSig<CppOldDataFlow> {
|
||||
import TaintTrackingUtil
|
||||
}
|
||||
@@ -39,7 +39,7 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
* of `c` at sinks and inputs to additional taint steps.
|
||||
*/
|
||||
bindingset[node]
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a sanitizer in all global taint flow configurations
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> implements
|
||||
DataFlowInternal::FullStateConfigSig
|
||||
{
|
||||
import Config
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
Config::isBarrier(node) or defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
Config::isAdditionalFlowStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
Config::allowImplicitRead(node, c)
|
||||
or
|
||||
(
|
||||
Config::isSink(node) or
|
||||
Config::isSink(node, _) or
|
||||
Config::isAdditionalFlowStep(node, _) or
|
||||
Config::isAdditionalFlowStep(node, _, _, _)
|
||||
) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a global taint tracking computation.
|
||||
*/
|
||||
module Global<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
|
||||
private module Config0 implements DataFlowInternal::FullStateConfigSig {
|
||||
import DataFlowInternal::DefaultState<Config>
|
||||
import Config
|
||||
}
|
||||
|
||||
private module C implements DataFlowInternal::FullStateConfigSig {
|
||||
import AddTaintDefaults<Config0>
|
||||
}
|
||||
|
||||
import DataFlowInternal::Impl<C>
|
||||
}
|
||||
|
||||
/** DEPRECATED: Use `Global` instead. */
|
||||
deprecated module Make<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
|
||||
import Global<Config>
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a global taint tracking computation using flow state.
|
||||
*/
|
||||
module GlobalWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
|
||||
private module Config0 implements DataFlowInternal::FullStateConfigSig {
|
||||
import Config
|
||||
}
|
||||
|
||||
private module C implements DataFlowInternal::FullStateConfigSig {
|
||||
import AddTaintDefaults<Config0>
|
||||
}
|
||||
|
||||
import DataFlowInternal::Impl<C>
|
||||
}
|
||||
|
||||
/** DEPRECATED: Use `GlobalWithState` instead. */
|
||||
deprecated module MakeWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
|
||||
import GlobalWithState<Config>
|
||||
}
|
||||
@@ -23,6 +23,10 @@ import semmle.code.cpp.dataflow.new.DataFlow2
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
module TaintTracking {
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingParameter::Public
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
||||
private import codeql.dataflow.TaintTracking
|
||||
import TaintFlowMake<CppDataFlow, CppTaintTracking>
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow2
|
||||
|
||||
module TaintTracking {
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingParameter::Public
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
||||
private import codeql.dataflow.TaintTracking
|
||||
import TaintFlowMake<CppDataFlow, CppTaintTracking>
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -1078,7 +1078,7 @@ private IRVariable getIRVariableForParameterNode(ParameterNode p) {
|
||||
|
||||
/** Holds if `v` is the source variable corresponding to the parameter represented by `p`. */
|
||||
pragma[nomagic]
|
||||
private predicate parameterNodeHasSourceVariable(ParameterNode p, Ssa::SourceIRVariable v) {
|
||||
private predicate parameterNodeHasSourceVariable(ParameterNode p, Ssa::SourceVariable v) {
|
||||
v.getIRVariable() = getIRVariableForParameterNode(p) and
|
||||
exists(Position pos | p.isParameterOf(_, pos) |
|
||||
pos instanceof DirectPosition and
|
||||
|
||||
@@ -781,26 +781,12 @@ class IndirectArgumentOutNode extends Node, TIndirectArgumentOutNode, PartialDef
|
||||
override Expr getDefinedExpr() { result = operand.getDef().getUnconvertedResultExpression() }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate indirectReturnOutNodeOperand0(CallInstruction call, Operand operand, int indirectionIndex) {
|
||||
Ssa::hasRawIndirectInstruction(call, indirectionIndex) and
|
||||
operandForFullyConvertedCall(operand, call)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate indirectReturnOutNodeInstruction0(
|
||||
CallInstruction call, Instruction instr, int indirectionIndex
|
||||
) {
|
||||
Ssa::hasRawIndirectInstruction(call, indirectionIndex) and
|
||||
instructionForFullyConvertedCall(instr, call)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is an indirect operand with columns `(operand, indirectionIndex)`, and
|
||||
* `operand` represents a use of the fully converted value of `call`.
|
||||
*/
|
||||
private predicate hasOperand(Node node, CallInstruction call, int indirectionIndex, Operand operand) {
|
||||
indirectReturnOutNodeOperand0(call, operand, indirectionIndex) and
|
||||
operandForFullyConvertedCall(operand, call) and
|
||||
hasOperandAndIndex(node, operand, indirectionIndex)
|
||||
}
|
||||
|
||||
@@ -813,7 +799,7 @@ private predicate hasOperand(Node node, CallInstruction call, int indirectionInd
|
||||
private predicate hasInstruction(
|
||||
Node node, CallInstruction call, int indirectionIndex, Instruction instr
|
||||
) {
|
||||
indirectReturnOutNodeInstruction0(call, instr, indirectionIndex) and
|
||||
instructionForFullyConvertedCall(instr, call) and
|
||||
hasInstructionAndIndex(node, instr, indirectionIndex)
|
||||
}
|
||||
|
||||
@@ -1534,6 +1520,25 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `operand.getDef() = instr`, but there exists a `StoreInstruction` that
|
||||
* writes to an address that is equivalent to the value computed by `instr` in
|
||||
* between `instr` and `operand`, and therefore there should not be flow from `*instr`
|
||||
* to `*operand`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate isStoredToBetween(Instruction instr, Operand operand) {
|
||||
simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand)) and
|
||||
exists(StoreInstruction store, IRBlock block, int storeIndex, int instrIndex, int operandIndex |
|
||||
store.getDestinationAddress() = instr and
|
||||
block.getInstruction(storeIndex) = store and
|
||||
block.getInstruction(instrIndex) = instr and
|
||||
block.getInstruction(operandIndex) = operand.getUse() and
|
||||
instrIndex < storeIndex and
|
||||
storeIndex < operandIndex
|
||||
)
|
||||
}
|
||||
|
||||
private predicate indirectionInstructionFlow(
|
||||
RawIndirectInstruction nodeFrom, IndirectOperand nodeTo
|
||||
) {
|
||||
@@ -1543,7 +1548,8 @@ private module Cached {
|
||||
simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand))
|
||||
|
|
||||
hasOperandAndIndex(nodeTo, operand, pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeFrom, instr, pragma[only_bind_into](indirectionIndex))
|
||||
hasInstructionAndIndex(nodeFrom, instr, pragma[only_bind_into](indirectionIndex)) and
|
||||
not isStoredToBetween(instr, operand)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,32 +10,35 @@ private import ssa0.SsaInternals as SsaInternals0
|
||||
import SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
int getMaxIndirectionForIRVariable(IRVariable var) {
|
||||
exists(Type type, boolean isGLValue |
|
||||
var.getLanguageType().hasType(type, isGLValue) and
|
||||
if isGLValue = true
|
||||
then result = 1 + getMaxIndirectionsForType(type)
|
||||
else result = getMaxIndirectionsForType(type)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private newtype TSourceVariable =
|
||||
TSourceIRVariable(BaseIRVariable baseVar, int ind) {
|
||||
ind = [0 .. getMaxIndirectionForIRVariable(baseVar.getIRVariable())]
|
||||
} or
|
||||
TCallVariable(AllocationInstruction call, int ind) {
|
||||
ind = [0 .. countIndirectionsForCppType(getResultLanguageType(call))]
|
||||
TMkSourceVariable(SsaInternals0::SourceVariable base, int ind) {
|
||||
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
|
||||
}
|
||||
|
||||
abstract class SourceVariable extends TSourceVariable {
|
||||
class SourceVariable extends TSourceVariable {
|
||||
SsaInternals0::SourceVariable base;
|
||||
int ind;
|
||||
|
||||
bindingset[ind]
|
||||
SourceVariable() { any() }
|
||||
SourceVariable() { this = TMkSourceVariable(base, ind) }
|
||||
|
||||
/** Gets the IR variable associated with this `SourceVariable`, if any. */
|
||||
IRVariable getIRVariable() { result = base.(BaseIRVariable).getIRVariable() }
|
||||
|
||||
/**
|
||||
* Gets the base source variable (i.e., the variable without any
|
||||
* indirections) of this source variable.
|
||||
*/
|
||||
SsaInternals0::SourceVariable getBaseVariable() { result = base }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
string toString() {
|
||||
ind = 0 and
|
||||
result = this.getBaseVariable().toString()
|
||||
or
|
||||
ind > 0 and
|
||||
result = this.getBaseVariable().toString() + " indirection"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of loads performed on the base source variable
|
||||
@@ -43,65 +46,19 @@ private module SourceVariables {
|
||||
*/
|
||||
int getIndirection() { result = ind }
|
||||
|
||||
/**
|
||||
* Gets the base source variable (i.e., the variable without any
|
||||
* indirections) of this source variable.
|
||||
*/
|
||||
abstract BaseSourceVariable getBaseVariable();
|
||||
|
||||
/** Holds if this variable is a glvalue. */
|
||||
predicate isGLValue() { none() }
|
||||
predicate isGLValue() { ind = 0 }
|
||||
|
||||
/**
|
||||
* Gets the type of this source variable. If `isGLValue()` holds, then
|
||||
* the type of this source variable should be thought of as "pointer
|
||||
* to `getType()`".
|
||||
*/
|
||||
abstract DataFlowType getType();
|
||||
}
|
||||
|
||||
class SourceIRVariable extends SourceVariable, TSourceIRVariable {
|
||||
BaseIRVariable var;
|
||||
|
||||
SourceIRVariable() { this = TSourceIRVariable(var, ind) }
|
||||
|
||||
IRVariable getIRVariable() { result = var.getIRVariable() }
|
||||
|
||||
override BaseIRVariable getBaseVariable() { result.getIRVariable() = this.getIRVariable() }
|
||||
|
||||
override string toString() {
|
||||
ind = 0 and
|
||||
result = this.getIRVariable().toString()
|
||||
or
|
||||
ind > 0 and
|
||||
result = this.getIRVariable().toString() + " indirection"
|
||||
DataFlowType getType() {
|
||||
if this.isGLValue()
|
||||
then result = base.getType()
|
||||
else result = getTypeImpl(base.getType(), ind - 1)
|
||||
}
|
||||
|
||||
override predicate isGLValue() { ind = 0 }
|
||||
|
||||
override DataFlowType getType() {
|
||||
if ind = 0 then result = var.getType() else result = getTypeImpl(var.getType(), ind - 1)
|
||||
}
|
||||
}
|
||||
|
||||
class CallVariable extends SourceVariable, TCallVariable {
|
||||
AllocationInstruction call;
|
||||
|
||||
CallVariable() { this = TCallVariable(call, ind) }
|
||||
|
||||
AllocationInstruction getCall() { result = call }
|
||||
|
||||
override BaseCallVariable getBaseVariable() { result.getCallInstruction() = call }
|
||||
|
||||
override string toString() {
|
||||
ind = 0 and
|
||||
result = "Call"
|
||||
or
|
||||
ind > 0 and
|
||||
result = "Call indirection"
|
||||
}
|
||||
|
||||
override DataFlowType getType() { result = getTypeImpl(call.getResultType(), ind) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,8 +94,9 @@ predicate hasRawIndirectInstruction(Instruction instr, int indirectionIndex) {
|
||||
|
||||
cached
|
||||
private newtype TDefOrUseImpl =
|
||||
TDefImpl(Operand address, int indirectionIndex) {
|
||||
exists(Instruction base | isDef(_, _, address, base, _, indirectionIndex) |
|
||||
TDefImpl(BaseSourceVariableInstruction base, Operand address, int indirectionIndex) {
|
||||
isDef(_, _, address, base, _, indirectionIndex) and
|
||||
(
|
||||
// We only include the definition if the SSA pruning stage
|
||||
// concluded that the definition is live after the write.
|
||||
any(SsaInternals0::Def def).getAddressOperand() = address
|
||||
@@ -148,8 +106,8 @@ private newtype TDefOrUseImpl =
|
||||
base.(VariableAddressInstruction).getAstVariable() instanceof GlobalLikeVariable
|
||||
)
|
||||
} or
|
||||
TUseImpl(Operand operand, int indirectionIndex) {
|
||||
isUse(_, operand, _, _, indirectionIndex) and
|
||||
TUseImpl(BaseSourceVariableInstruction base, Operand operand, int indirectionIndex) {
|
||||
isUse(_, operand, base, _, indirectionIndex) and
|
||||
not isDef(_, _, operand, _, _, _)
|
||||
} or
|
||||
TGlobalUse(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
|
||||
@@ -236,7 +194,7 @@ abstract private class DefOrUseImpl extends TDefOrUseImpl {
|
||||
|
||||
/**
|
||||
* Gets the instruction that computes the base of this definition or use.
|
||||
* This is always a `VariableAddressInstruction` or an `AllocationInstruction`.
|
||||
* This is always a `VariableAddressInstruction` or an `CallInstruction`.
|
||||
*/
|
||||
abstract BaseSourceVariableInstruction getBase();
|
||||
|
||||
@@ -308,15 +266,17 @@ abstract class DefImpl extends DefOrUseImpl {
|
||||
}
|
||||
|
||||
private class DirectDef extends DefImpl, TDefImpl {
|
||||
DirectDef() { this = TDefImpl(address, ind) }
|
||||
BaseSourceVariableInstruction base;
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { isDef(_, _, address, result, _, _) }
|
||||
DirectDef() { this = TDefImpl(base, address, ind) }
|
||||
|
||||
override int getIndirection() { isDef(_, _, address, _, result, ind) }
|
||||
override BaseSourceVariableInstruction getBase() { result = base }
|
||||
|
||||
override Node0Impl getValue() { isDef(_, result, address, _, _, _) }
|
||||
override int getIndirection() { isDef(_, _, address, base, result, ind) }
|
||||
|
||||
override predicate isCertain() { isDef(true, _, address, _, _, ind) }
|
||||
override Node0Impl getValue() { isDef(_, result, address, base, _, _) }
|
||||
|
||||
override predicate isCertain() { isDef(true, _, address, base, _, ind) }
|
||||
}
|
||||
|
||||
private class IteratorDef extends DefImpl, TIteratorDef {
|
||||
@@ -359,6 +319,7 @@ abstract class UseImpl extends DefOrUseImpl {
|
||||
|
||||
abstract private class OperandBasedUse extends UseImpl {
|
||||
Operand operand;
|
||||
BaseSourceVariableInstruction base;
|
||||
|
||||
bindingset[ind]
|
||||
OperandBasedUse() { any() }
|
||||
@@ -366,50 +327,44 @@ abstract private class OperandBasedUse extends UseImpl {
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
// See the comment in `ssa0`'s `OperandBasedUse` for an explanation of this
|
||||
// predicate's implementation.
|
||||
exists(BaseSourceVariableInstruction base | base = this.getBase() |
|
||||
if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
|
||||
then
|
||||
exists(Operand op, int indirectionIndex, int indirection |
|
||||
indirectionIndex = this.getIndirectionIndex() and
|
||||
indirection = this.getIndirection() and
|
||||
op =
|
||||
min(Operand cand, int i |
|
||||
isUse(_, cand, base, indirection, indirectionIndex) and
|
||||
block.getInstruction(i) = cand.getUse()
|
||||
|
|
||||
cand order by i
|
||||
) and
|
||||
block.getInstruction(index) = op.getUse()
|
||||
)
|
||||
else operand.getUse() = block.getInstruction(index)
|
||||
)
|
||||
if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
|
||||
then
|
||||
exists(Operand op, int indirectionIndex, int indirection |
|
||||
indirectionIndex = this.getIndirectionIndex() and
|
||||
indirection = this.getIndirection() and
|
||||
op =
|
||||
min(Operand cand, int i |
|
||||
isUse(_, cand, base, indirection, indirectionIndex) and
|
||||
block.getInstruction(i) = cand.getUse()
|
||||
|
|
||||
cand order by i
|
||||
) and
|
||||
block.getInstruction(index) = op.getUse()
|
||||
)
|
||||
else operand.getUse() = block.getInstruction(index)
|
||||
}
|
||||
|
||||
final override BaseSourceVariableInstruction getBase() { result = base }
|
||||
|
||||
final Operand getOperand() { result = operand }
|
||||
|
||||
final override Cpp::Location getLocation() { result = operand.getLocation() }
|
||||
}
|
||||
|
||||
private class DirectUse extends OperandBasedUse, TUseImpl {
|
||||
DirectUse() { this = TUseImpl(operand, ind) }
|
||||
DirectUse() { this = TUseImpl(base, operand, ind) }
|
||||
|
||||
override int getIndirection() { isUse(_, operand, _, result, ind) }
|
||||
override int getIndirection() { isUse(_, operand, base, result, ind) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { isUse(_, operand, result, _, ind) }
|
||||
|
||||
override predicate isCertain() { isUse(true, operand, _, _, ind) }
|
||||
override predicate isCertain() { isUse(true, operand, base, _, ind) }
|
||||
|
||||
override Node getNode() { nodeHasOperand(result, operand, ind) }
|
||||
}
|
||||
|
||||
private class IteratorUse extends OperandBasedUse, TIteratorUse {
|
||||
BaseSourceVariableInstruction container;
|
||||
IteratorUse() { this = TIteratorUse(operand, base, ind) }
|
||||
|
||||
IteratorUse() { this = TIteratorUse(operand, container, ind) }
|
||||
|
||||
override int getIndirection() { isIteratorUse(container, operand, result, ind) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { result = container }
|
||||
override int getIndirection() { isIteratorUse(base, operand, result, ind) }
|
||||
|
||||
override predicate isCertain() { none() }
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import DataFlowUtil
|
||||
private import semmle.code.cpp.models.interfaces.PointerWrapper
|
||||
private import DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
/**
|
||||
* Holds if `operand` is an operand that is not used by the dataflow library.
|
||||
@@ -146,14 +147,6 @@ int countIndirectionsForCppType(LanguageType langType) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `CallInstruction` that calls an allocation function such
|
||||
* as `malloc` or `operator new`.
|
||||
*/
|
||||
class AllocationInstruction extends CallInstruction {
|
||||
AllocationInstruction() { this.getStaticCallTarget() instanceof Cpp::AllocationFunction }
|
||||
}
|
||||
|
||||
private predicate isIndirectionType(Type t) { t instanceof Indirection }
|
||||
|
||||
private predicate hasUnspecifiedBaseType(Indirection t, Type base) {
|
||||
@@ -368,17 +361,22 @@ newtype TBaseSourceVariable =
|
||||
// Each IR variable gets its own source variable
|
||||
TBaseIRVariable(IRVariable var) or
|
||||
// Each allocation gets its own source variable
|
||||
TBaseCallVariable(AllocationInstruction call)
|
||||
TBaseCallVariable(CallInstruction call) { not call.getResultIRType() instanceof IRVoidType }
|
||||
|
||||
abstract class BaseSourceVariable extends TBaseSourceVariable {
|
||||
abstract private class AbstractBaseSourceVariable extends TBaseSourceVariable {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the type of this base source variable. */
|
||||
abstract DataFlowType getType();
|
||||
final DataFlowType getType() { this.getLanguageType().hasUnspecifiedType(result, _) }
|
||||
|
||||
/** Gets the `CppType` of this base source variable. */
|
||||
abstract CppType getLanguageType();
|
||||
}
|
||||
|
||||
class BaseIRVariable extends BaseSourceVariable, TBaseIRVariable {
|
||||
final class BaseSourceVariable = AbstractBaseSourceVariable;
|
||||
|
||||
class BaseIRVariable extends AbstractBaseSourceVariable, TBaseIRVariable {
|
||||
IRVariable var;
|
||||
|
||||
IRVariable getIRVariable() { result = var }
|
||||
@@ -387,19 +385,19 @@ class BaseIRVariable extends BaseSourceVariable, TBaseIRVariable {
|
||||
|
||||
override string toString() { result = var.toString() }
|
||||
|
||||
override DataFlowType getType() { result = var.getType() }
|
||||
override CppType getLanguageType() { result = var.getLanguageType() }
|
||||
}
|
||||
|
||||
class BaseCallVariable extends BaseSourceVariable, TBaseCallVariable {
|
||||
AllocationInstruction call;
|
||||
class BaseCallVariable extends AbstractBaseSourceVariable, TBaseCallVariable {
|
||||
CallInstruction call;
|
||||
|
||||
BaseCallVariable() { this = TBaseCallVariable(call) }
|
||||
|
||||
AllocationInstruction getCallInstruction() { result = call }
|
||||
CallInstruction getCallInstruction() { result = call }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override DataFlowType getType() { result = call.getResultType() }
|
||||
override CppType getLanguageType() { result = getResultLanguageType(call) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -499,8 +497,7 @@ private class BaseIRVariableInstruction extends BaseSourceVariableInstruction,
|
||||
override BaseIRVariable getBaseSourceVariable() { result.getIRVariable() = this.getIRVariable() }
|
||||
}
|
||||
|
||||
private class BaseAllocationInstruction extends BaseSourceVariableInstruction, AllocationInstruction
|
||||
{
|
||||
private class BaseCallInstruction extends BaseSourceVariableInstruction, CallInstruction {
|
||||
override BaseCallVariable getBaseSourceVariable() { result.getCallInstruction() = this }
|
||||
}
|
||||
|
||||
@@ -868,7 +865,7 @@ private module Cached {
|
||||
* to a specific address.
|
||||
*/
|
||||
private predicate isCertainAddress(Operand operand) {
|
||||
operand.getDef() instanceof VariableAddressInstruction
|
||||
valueNumberOfOperand(operand).getAnInstruction() instanceof VariableAddressInstruction
|
||||
or
|
||||
operand.getType() instanceof Cpp::ReferenceType
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Provides C++-specific definitions for use in the taint tracking library.
|
||||
*/
|
||||
|
||||
private import codeql.dataflow.TaintTracking
|
||||
private import DataFlowImplSpecific
|
||||
|
||||
module CppTaintTracking implements InputSig<CppDataFlow> {
|
||||
import TaintTrackingUtil
|
||||
}
|
||||
@@ -112,7 +112,7 @@ predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) {
|
||||
* of `c` at sinks and inputs to additional taint steps.
|
||||
*/
|
||||
bindingset[node]
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
|
||||
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `node` should be a sanitizer in all global taint flow configurations
|
||||
|
||||
@@ -15,15 +15,12 @@ private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
class SourceVariable instanceof BaseSourceVariable {
|
||||
string toString() { result = BaseSourceVariable.super.toString() }
|
||||
|
||||
class SourceVariable extends BaseSourceVariable {
|
||||
/**
|
||||
* Gets the base source variable of this `SourceVariable`.
|
||||
*/
|
||||
BaseSourceVariable getBaseVariable() { result = this }
|
||||
}
|
||||
|
||||
class SourceIRVariable = BaseIRVariable;
|
||||
|
||||
class CallVariable = BaseCallVariable;
|
||||
}
|
||||
|
||||
import SourceVariables
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
/**
|
||||
* Provides classes for performing local (intra-procedural) and
|
||||
* global (inter-procedural) taint-tracking analyses.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
private module AddTaintDefaults<DataFlowInternal::FullStateConfigSig Config> implements
|
||||
DataFlowInternal::FullStateConfigSig
|
||||
{
|
||||
import Config
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
Config::isBarrier(node) or defaultTaintSanitizer(node)
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
Config::isAdditionalFlowStep(node1, node2) or
|
||||
defaultAdditionalTaintStep(node1, node2)
|
||||
}
|
||||
|
||||
predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
Config::allowImplicitRead(node, c)
|
||||
or
|
||||
(
|
||||
Config::isSink(node) or
|
||||
Config::isSink(node, _) or
|
||||
Config::isAdditionalFlowStep(node, _) or
|
||||
Config::isAdditionalFlowStep(node, _, _, _)
|
||||
) and
|
||||
defaultImplicitTaintRead(node, c)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a global taint tracking computation.
|
||||
*/
|
||||
module Global<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
|
||||
private module Config0 implements DataFlowInternal::FullStateConfigSig {
|
||||
import DataFlowInternal::DefaultState<Config>
|
||||
import Config
|
||||
}
|
||||
|
||||
private module C implements DataFlowInternal::FullStateConfigSig {
|
||||
import AddTaintDefaults<Config0>
|
||||
}
|
||||
|
||||
import DataFlowInternal::Impl<C>
|
||||
}
|
||||
|
||||
/** DEPRECATED: Use `Global` instead. */
|
||||
deprecated module Make<DataFlow::ConfigSig Config> implements DataFlow::GlobalFlowSig {
|
||||
import Global<Config>
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a global taint tracking computation using flow state.
|
||||
*/
|
||||
module GlobalWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
|
||||
private module Config0 implements DataFlowInternal::FullStateConfigSig {
|
||||
import Config
|
||||
}
|
||||
|
||||
private module C implements DataFlowInternal::FullStateConfigSig {
|
||||
import AddTaintDefaults<Config0>
|
||||
}
|
||||
|
||||
import DataFlowInternal::Impl<C>
|
||||
}
|
||||
|
||||
/** DEPRECATED: Use `GlobalWithState` instead. */
|
||||
deprecated module MakeWithState<DataFlow::StateConfigSig Config> implements DataFlow::GlobalFlowSig {
|
||||
import GlobalWithState<Config>
|
||||
}
|
||||
@@ -574,16 +574,6 @@ module RangeStage<
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `e >= 1` as determined by sign analysis. */
|
||||
private predicate strictlyPositiveIntegralExpr(SemExpr e) {
|
||||
semStrictlyPositive(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/** Holds if `e <= -1` as determined by sign analysis. */
|
||||
private predicate strictlyNegativeIntegralExpr(SemExpr e) {
|
||||
semStrictlyNegative(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e1 + delta` is a valid bound for `e2`.
|
||||
* - `upper = true` : `e2 <= e1 + delta`
|
||||
@@ -597,27 +587,6 @@ module RangeStage<
|
||||
delta = D::fromInt(0) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
|
||||
not x instanceof SemConstantIntegerExpr and
|
||||
if strictlyPositiveIntegralExpr(x)
|
||||
then upper = true and delta = D::fromInt(-1)
|
||||
else
|
||||
if semPositive(x)
|
||||
then upper = true and delta = D::fromInt(0)
|
||||
else
|
||||
if strictlyNegativeIntegralExpr(x)
|
||||
then upper = false and delta = D::fromInt(1)
|
||||
else
|
||||
if semNegative(x)
|
||||
then upper = false and delta = D::fromInt(0)
|
||||
else none()
|
||||
)
|
||||
or
|
||||
e2.(SemRemExpr).getRightOperand() = e1 and
|
||||
semPositive(e1) and
|
||||
delta = D::fromInt(-1) and
|
||||
@@ -1137,6 +1106,23 @@ module RangeStage<
|
||||
b = bRight and origdelta = odRight and reason = rRight and bLeft instanceof SemZeroBound
|
||||
)
|
||||
or
|
||||
exists(D::Delta dLeft, D::Delta dRight, boolean fbeLeft, boolean fbeRight |
|
||||
boundedSubOperandLeft(e, upper, b, dLeft, fbeLeft, origdelta, reason) and
|
||||
boundedSubOperandRight(e, upper, dRight, fbeRight) and
|
||||
// when `upper` is `true` we have:
|
||||
// left <= b + dLeft
|
||||
// right >= 0 + dRight
|
||||
// left - right <= b + dLeft - (0 + dRight)
|
||||
// = b + (dLeft - dRight)
|
||||
// and when `upper` is `false` we have:
|
||||
// left >= b + dLeft
|
||||
// right <= 0 + dRight
|
||||
// left - right >= b + dLeft - (0 + dRight)
|
||||
// = b + (dLeft - dRight)
|
||||
delta = D::fromFloat(D::toFloat(dLeft) - D::toFloat(dRight)) and
|
||||
fromBackEdge = fbeLeft.booleanOr(fbeRight)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
SemRemExpr rem, D::Delta d_max, D::Delta d1, D::Delta d2, boolean fbe1, boolean fbe2,
|
||||
D::Delta od1, D::Delta od2, SemReason r1, SemReason r2
|
||||
@@ -1201,6 +1187,37 @@ module RangeStage<
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub = left - right` and `left <= b + delta` if `upper` is `true`
|
||||
* and `left >= b + delta` is `upper` is `false`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedSubOperandLeft(
|
||||
SemSubExpr sub, boolean upper, SemBound b, D::Delta delta, boolean fromBackEdge,
|
||||
D::Delta origdelta, SemReason reason
|
||||
) {
|
||||
// `semValueFlowStep` already handles the case where one of the operands is a constant.
|
||||
not semValueFlowStep(sub, _, _) and
|
||||
bounded(sub.getLeftOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub = left - right` and `right <= 0 + delta` if `upper` is `false`
|
||||
* and `right >= 0 + delta` is `upper` is `true`.
|
||||
*
|
||||
* Note that the boolean value of `upper` is flipped compared to many other predicates in
|
||||
* this file. This ensures a clean join at the call-site.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedSubOperandRight(
|
||||
SemSubExpr sub, boolean upper, D::Delta delta, boolean fromBackEdge
|
||||
) {
|
||||
// `semValueFlowStep` already handles the case where one of the operands is a constant.
|
||||
not semValueFlowStep(sub, _, _) and
|
||||
bounded(sub.getRightOperand(), any(SemZeroBound zb), delta, upper.booleanNot(), fromBackEdge, _,
|
||||
_)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate boundedRemExpr(
|
||||
SemRemExpr rem, boolean upper, D::Delta delta, boolean fromBackEdge, D::Delta origdelta,
|
||||
|
||||
@@ -53,7 +53,7 @@ private class ArgvSource extends LocalFlowSource {
|
||||
exists(Function main, Parameter argv |
|
||||
main.hasGlobalName("main") and
|
||||
main.getParameter(1) = argv and
|
||||
this.asParameter(_) = argv
|
||||
this.asParameter(2) = argv
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
|
||||
* but because there's a strict comparison that compares `n` against the size of the allocation this
|
||||
* snippet is fine.
|
||||
*/
|
||||
module SizeBarrier {
|
||||
private module SizeBarrier {
|
||||
private module SizeBarrierConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// The sources is the same as in the sources for the second
|
||||
@@ -104,35 +104,60 @@ module SizeBarrier {
|
||||
hasSize(_, source, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `small <= large + k` holds if `g` evaluates to `testIsTrue`.
|
||||
*/
|
||||
additional predicate isSink(
|
||||
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
|
||||
DataFlow::Node small, DataFlow::Node large, IRGuardCondition g, int k, boolean testIsTrue
|
||||
) {
|
||||
// The sink is any "large" side of a relational comparison. i.e., the `right` expression
|
||||
// in a guard such as `left < right + k`.
|
||||
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
|
||||
// The sink is any "large" side of a relational comparison. i.e., the `large` expression
|
||||
// in a guard such as `small <= large + k`.
|
||||
g.comparesLt(small.asOperand(), large.asOperand(), k + 1, true, testIsTrue)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
|
||||
}
|
||||
|
||||
private import DataFlow::Global<SizeBarrierConfig>
|
||||
module SizeBarrierFlow = DataFlow::Global<SizeBarrierConfig>;
|
||||
|
||||
private int getAFlowStateForNode(DataFlow::Node node) {
|
||||
private int getASizeAddend(DataFlow::Node node) {
|
||||
exists(DataFlow::Node source |
|
||||
flow(source, node) and
|
||||
SizeBarrierFlow::flow(source, node) and
|
||||
hasSize(_, source, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `small <= large + k` holds if `g` evaluates to `edge`.
|
||||
*/
|
||||
private predicate operandGuardChecks(
|
||||
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
|
||||
IRGuardCondition g, Operand small, DataFlow::Node large, int k, boolean edge
|
||||
) {
|
||||
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
|
||||
nRight.asOperand() = right and
|
||||
nLeft.asOperand() = left and
|
||||
SizeBarrierConfig::isSink(nLeft, nRight, g, k, edge) and
|
||||
state = getAFlowStateForNode(nRight) and
|
||||
k <= state
|
||||
SizeBarrierFlow::flowTo(large) and
|
||||
SizeBarrierConfig::isSink(DataFlow::operandNode(small), large, g, k, edge)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instruction `instr` that is guarded by a check such as `instr <= small + delta` where
|
||||
* `small <= _ + k` and `small` is the "small side" of of a relational comparison that checks
|
||||
* whether `small <= size` where `size` is the size of an allocation.
|
||||
*/
|
||||
Instruction getABarrierInstruction0(int delta, int k) {
|
||||
exists(
|
||||
IRGuardCondition g, ValueNumber value, Operand small, boolean edge, DataFlow::Node large
|
||||
|
|
||||
// We know:
|
||||
// 1. result <= value + delta (by `bounded`)
|
||||
// 2. value <= large + k (by `operandGuardChecks`).
|
||||
// So:
|
||||
// result <= value + delta (by 1.)
|
||||
// <= large + k + delta (by 2.)
|
||||
small = value.getAUse() and
|
||||
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](small), large,
|
||||
pragma[only_bind_into](k), pragma[only_bind_into](edge)) and
|
||||
bounded(result, value.getAnInstruction(), delta) and
|
||||
g.controls(result.getBlock(), edge) and
|
||||
k < getASizeAddend(large)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -140,13 +165,14 @@ module SizeBarrier {
|
||||
* Gets an instruction that is guarded by a guard condition which ensures that
|
||||
* the value of the instruction is upper-bounded by size of some allocation.
|
||||
*/
|
||||
bindingset[state]
|
||||
pragma[inline_late]
|
||||
Instruction getABarrierInstruction(int state) {
|
||||
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
|
||||
use = value.getAUse() and
|
||||
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _,
|
||||
pragma[only_bind_into](state), pragma[only_bind_into](edge)) and
|
||||
result = value.getAnInstruction() and
|
||||
g.controls(result.getBlock(), edge)
|
||||
exists(int delta, int k |
|
||||
state > k + delta and
|
||||
// result <= "size of allocation" + delta + k
|
||||
// < "size of allocation" + state
|
||||
result = getABarrierInstruction0(delta, k)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -155,14 +181,16 @@ module SizeBarrier {
|
||||
* the value of the node is upper-bounded by size of some allocation.
|
||||
*/
|
||||
DataFlow::Node getABarrierNode(int state) {
|
||||
result.asOperand() = getABarrierInstruction(state).getAUse()
|
||||
exists(DataFlow::Node source, int delta, int k |
|
||||
SizeBarrierFlow::flow(source, result) and
|
||||
hasSize(_, source, state) and
|
||||
result.asInstruction() = SizeBarrier::getABarrierInstruction0(delta, k) and
|
||||
state > k + delta
|
||||
// so now we have:
|
||||
// result <= "size of allocation" + delta + k
|
||||
// < "size of allocation" + state
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block of a node that is guarded (see `getABarrierInstruction` or
|
||||
* `getABarrierNode` for the definition of what it means to be guarded).
|
||||
*/
|
||||
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
|
||||
}
|
||||
|
||||
private module InterestingPointerAddInstruction {
|
||||
|
||||
@@ -66,11 +66,14 @@
|
||||
* module. Since the node we are tracking is not necessarily _equal_ to the pointer-arithmetic instruction, but rather satisfies
|
||||
* `node.asInstruction() <= pai + deltaDerefSourceAndPai`, we need to account for the delta when checking if a guard is sufficiently
|
||||
* strong to infer that a future dereference is safe. To do this, we check that the guard guarantees that a node `n` satisfies
|
||||
* `n < node + k` where `node` is a node we know is equal to the value of the dereference source (i.e., it satisfies
|
||||
* `node.asInstruction() <= pai + deltaDerefSourceAndPai`) and `k <= deltaDerefSourceAndPai`. Combining this we have
|
||||
* `n < node + k <= node + deltaDerefSourceAndPai <= pai + 2*deltaDerefSourceAndPai` (TODO: Oops. This math doesn't quite work out.
|
||||
* I think this is because we need to redefine the `BarrierConfig` to start flow at the pointer-arithmetic instruction instead of
|
||||
* at the dereference source. When combined with TODO above it's easy to show that this guard ensures that the dereference is safe).
|
||||
* `n < node + k` where `node` is a node such that `node <= pai`. Thus, we know that any node `m` such that `m <= n + delta` where
|
||||
* `delta + k <= 0` will be safe because:
|
||||
* ```
|
||||
* m <= n + delta
|
||||
* < node + k + delta
|
||||
* <= pai + k + delta
|
||||
* <= pai
|
||||
* ```
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
@@ -82,16 +85,19 @@ private import RangeAnalysisUtil
|
||||
|
||||
private module InvalidPointerToDerefBarrier {
|
||||
private module BarrierConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// The sources is the same as in the sources for `InvalidPointerToDerefConfig`.
|
||||
invalidPointerToDerefSource(_, _, source, _)
|
||||
additional predicate isSource(DataFlow::Node source, PointerArithmeticInstruction pai) {
|
||||
invalidPointerToDerefSource(_, pai, _, _) and
|
||||
// source <= pai
|
||||
bounded2(source.asInstruction(), pai, any(int d | d <= 0))
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
additional predicate isSink(
|
||||
DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int k, boolean testIsTrue
|
||||
DataFlow::Node small, DataFlow::Node large, IRGuardCondition g, int k, boolean testIsTrue
|
||||
) {
|
||||
// The sink is any "large" side of a relational comparison.
|
||||
g.comparesLt(left.asOperand(), right.asOperand(), k, true, testIsTrue)
|
||||
g.comparesLt(small.asOperand(), large.asOperand(), k, true, testIsTrue)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) }
|
||||
@@ -99,59 +105,82 @@ private module InvalidPointerToDerefBarrier {
|
||||
|
||||
private module BarrierFlow = DataFlow::Global<BarrierConfig>;
|
||||
|
||||
private int getInvalidPointerToDerefSourceDelta(DataFlow::Node node) {
|
||||
exists(DataFlow::Node source |
|
||||
BarrierFlow::flow(source, node) and
|
||||
invalidPointerToDerefSource(_, _, source, result)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `g` ensures that `small < large + k` if `g` evaluates to `edge`.
|
||||
*
|
||||
* Additionally, it also holds that `large <= pai`. Thus, when `g` evaluates to `edge`
|
||||
* it holds that `small < pai + k`.
|
||||
*/
|
||||
private predicate operandGuardChecks(
|
||||
IRGuardCondition g, Operand left, Operand right, int state, boolean edge
|
||||
PointerArithmeticInstruction pai, IRGuardCondition g, Operand small, int k, boolean edge
|
||||
) {
|
||||
exists(DataFlow::Node nLeft, DataFlow::Node nRight, int k |
|
||||
nRight.asOperand() = right and
|
||||
nLeft.asOperand() = left and
|
||||
BarrierConfig::isSink(nLeft, nRight, g, k, edge) and
|
||||
state = getInvalidPointerToDerefSourceDelta(nRight) and
|
||||
k <= state
|
||||
exists(DataFlow::Node source, DataFlow::Node nSmall, DataFlow::Node nLarge |
|
||||
nSmall.asOperand() = small and
|
||||
BarrierConfig::isSource(source, pai) and
|
||||
BarrierFlow::flow(source, nLarge) and
|
||||
BarrierConfig::isSink(nSmall, nLarge, g, k, edge)
|
||||
)
|
||||
}
|
||||
|
||||
Instruction getABarrierInstruction(int state) {
|
||||
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge |
|
||||
/**
|
||||
* Gets an instruction `instr` such that `instr < pai`.
|
||||
*/
|
||||
Instruction getABarrierInstruction(PointerArithmeticInstruction pai) {
|
||||
exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge, int delta, int k |
|
||||
use = value.getAUse() and
|
||||
operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _, state,
|
||||
pragma[only_bind_into](edge)) and
|
||||
result = value.getAnInstruction() and
|
||||
g.controls(result.getBlock(), edge)
|
||||
// value < pai + k
|
||||
operandGuardChecks(pai, pragma[only_bind_into](g), pragma[only_bind_into](use),
|
||||
pragma[only_bind_into](k), pragma[only_bind_into](edge)) and
|
||||
// result <= value + delta
|
||||
bounded(result, value.getAnInstruction(), delta) and
|
||||
g.controls(result.getBlock(), edge) and
|
||||
delta + k <= 0
|
||||
// combining the above we have: result < pai + k + delta <= pai
|
||||
)
|
||||
}
|
||||
|
||||
DataFlow::Node getABarrierNode() { result.asOperand() = getABarrierInstruction(_).getAUse() }
|
||||
DataFlow::Node getABarrierNode(PointerArithmeticInstruction pai) {
|
||||
result.asOperand() = getABarrierInstruction(pai).getAUse()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) }
|
||||
/**
|
||||
* Gets an address operand whose definition `instr` satisfies `instr < pai`.
|
||||
*/
|
||||
AddressOperand getABarrierAddressOperand(PointerArithmeticInstruction pai) {
|
||||
result.getDef() = getABarrierInstruction(pai)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration to track flow from a pointer-arithmetic operation found
|
||||
* by `AllocToInvalidPointerConfig` to a dereference of the pointer.
|
||||
*/
|
||||
private module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { invalidPointerToDerefSource(_, _, source, _) }
|
||||
private module InvalidPointerToDerefConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState extends PointerArithmeticInstruction {
|
||||
FlowState() { invalidPointerToDerefSource(_, this, _, _) }
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState pai) {
|
||||
invalidPointerToDerefSource(_, pai, source, _)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _, _) }
|
||||
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _, _, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, FlowState pai) { none() }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node = any(DataFlow::SsaPhiNode phi | not phi.isPhiRead()).getAnInput(true)
|
||||
or
|
||||
node = InvalidPointerToDerefBarrier::getABarrierNode()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node, FlowState pai) {
|
||||
// `node = getABarrierNode(pai)` ensures that node < pai, so this node is safe to dereference.
|
||||
// Note that this is the only place where the `FlowState` is used in this configuration.
|
||||
node = InvalidPointerToDerefBarrier::getABarrierNode(pai)
|
||||
}
|
||||
}
|
||||
|
||||
private import DataFlow::Global<InvalidPointerToDerefConfig>
|
||||
private import DataFlow::GlobalWithState<InvalidPointerToDerefConfig>
|
||||
|
||||
/**
|
||||
* Holds if `allocSource` is dataflow node that represents an allocation that flows to the
|
||||
@@ -165,19 +194,14 @@ private predicate invalidPointerToDerefSource(
|
||||
DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource,
|
||||
int deltaDerefSourceAndPai
|
||||
) {
|
||||
exists(int rhsSizeDelta |
|
||||
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
|
||||
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
|
||||
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
|
||||
// and the instruction computing the address for which we will search for a dereference.
|
||||
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, rhsSizeDelta) and
|
||||
bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and
|
||||
deltaDerefSourceAndPai >= 0 and
|
||||
// TODO: This condition will go away once #13725 is merged, and then we can make `SizeBarrier`
|
||||
// private to `AllocationToInvalidPointer.qll`.
|
||||
not derefSource.getBasicBlock() =
|
||||
AllocToInvalidPointer::SizeBarrier::getABarrierBlock(rhsSizeDelta)
|
||||
)
|
||||
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
|
||||
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
|
||||
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
|
||||
// and the instruction computing the address for which we will search for a dereference.
|
||||
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, _) and
|
||||
// derefSource <= pai + deltaDerefSourceAndPai
|
||||
bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and
|
||||
deltaDerefSourceAndPai >= 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,15 +211,14 @@ private predicate invalidPointerToDerefSource(
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate isInvalidPointerDerefSink(
|
||||
DataFlow::Node sink, Instruction i, string operation, int deltaDerefSinkAndDerefAddress
|
||||
DataFlow::Node sink, AddressOperand addr, Instruction i, string operation,
|
||||
int deltaDerefSinkAndDerefAddress
|
||||
) {
|
||||
exists(AddressOperand addr, Instruction s, IRBlock b |
|
||||
exists(Instruction s |
|
||||
s = sink.asInstruction() and
|
||||
bounded(addr.getDef(), s, deltaDerefSinkAndDerefAddress) and
|
||||
deltaDerefSinkAndDerefAddress >= 0 and
|
||||
i.getAnOperand() = addr and
|
||||
b = i.getBlock() and
|
||||
not b = InvalidPointerToDerefBarrier::getABarrierBlock(deltaDerefSinkAndDerefAddress)
|
||||
i.getAnOperand() = addr
|
||||
|
|
||||
i instanceof StoreInstruction and
|
||||
operation = "write"
|
||||
@@ -221,9 +244,11 @@ private Instruction getASuccessor(Instruction instr) {
|
||||
instr.getBlock().getASuccessor+() = result.getBlock()
|
||||
}
|
||||
|
||||
private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFlow::Node derefSink) {
|
||||
private predicate paiForDereferenceSink(
|
||||
PointerArithmeticInstruction pai, DataFlow::Node derefSink, int deltaDerefSourceAndPai
|
||||
) {
|
||||
exists(DataFlow::Node derefSource |
|
||||
invalidPointerToDerefSource(_, pai, derefSource, _) and
|
||||
invalidPointerToDerefSource(_, pai, derefSource, deltaDerefSourceAndPai) and
|
||||
flow(derefSource, derefSink)
|
||||
)
|
||||
}
|
||||
@@ -235,13 +260,15 @@ private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFl
|
||||
*/
|
||||
private predicate derefSinkToOperation(
|
||||
DataFlow::Node derefSink, PointerArithmeticInstruction pai, DataFlow::Node operation,
|
||||
string description, int deltaDerefSinkAndDerefAddress
|
||||
string description, int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress
|
||||
) {
|
||||
exists(Instruction operationInstr |
|
||||
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink)) and
|
||||
isInvalidPointerDerefSink(derefSink, operationInstr, description, deltaDerefSinkAndDerefAddress) and
|
||||
exists(Instruction operationInstr, AddressOperand addr |
|
||||
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink), deltaDerefSourceAndPai) and
|
||||
isInvalidPointerDerefSink(derefSink, addr, operationInstr, description,
|
||||
deltaDerefSinkAndDerefAddress) and
|
||||
operationInstr = getASuccessor(derefSink.asInstruction()) and
|
||||
operation.asInstruction() = operationInstr
|
||||
operation.asInstruction() = operationInstr and
|
||||
not addr = InvalidPointerToDerefBarrier::getABarrierAddressOperand(pai)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -260,7 +287,8 @@ predicate operationIsOffBy(
|
||||
exists(int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress |
|
||||
invalidPointerToDerefSource(allocation, pai, derefSource, deltaDerefSourceAndPai) and
|
||||
flow(derefSource, derefSink) and
|
||||
derefSinkToOperation(derefSink, pai, operation, description, deltaDerefSinkAndDerefAddress) and
|
||||
derefSinkToOperation(derefSink, pai, operation, description, deltaDerefSourceAndPai,
|
||||
deltaDerefSinkAndDerefAddress) and
|
||||
delta = deltaDerefSourceAndPai + deltaDerefSinkAndDerefAddress
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ private Instruction getABoundIn(SemBound b, IRFunction func) {
|
||||
* Holds if `i <= b + delta`.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate boundedImpl(Instruction i, Instruction b, int delta) {
|
||||
private predicate boundedImplCand(Instruction i, Instruction b, int delta) {
|
||||
exists(SemBound bound, IRFunction func |
|
||||
semBounded(getSemanticExpr(i), bound, delta, true, _) and
|
||||
b = getABoundIn(bound, func) and
|
||||
@@ -26,6 +26,15 @@ private predicate boundedImpl(Instruction i, Instruction b, int delta) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i <= b + delta` and `delta` is the smallest integer that satisfies
|
||||
* this condition.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate boundedImpl(Instruction i, Instruction b, int delta) {
|
||||
delta = min(int cand | boundedImplCand(i, b, cand))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i <= b + delta`.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user