mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
C++: Remove DefaultTaintTracking library
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The deprecated `DefaultTaintTracking` library has been removed.
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: Use `semmle.code.cpp.ir.dataflow.TaintTracking` as a replacement.
|
||||
*
|
||||
* An IR taint tracking library that uses an IR DataFlow configuration to track
|
||||
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl as DefaultTaintTrackingImpl
|
||||
|
||||
deprecated predicate predictableOnlyFlow = DefaultTaintTrackingImpl::predictableOnlyFlow/1;
|
||||
|
||||
deprecated predicate tainted = DefaultTaintTrackingImpl::tainted/2;
|
||||
|
||||
deprecated predicate taintedIncludingGlobalVars =
|
||||
DefaultTaintTrackingImpl::taintedIncludingGlobalVars/3;
|
||||
|
||||
deprecated predicate globalVarFromId = DefaultTaintTrackingImpl::globalVarFromId/1;
|
||||
|
||||
deprecated module TaintedWithPath = DefaultTaintTrackingImpl::TaintedWithPath;
|
||||
@@ -1,668 +0,0 @@
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* An IR taint tracking library that uses an IR DataFlow configuration to track
|
||||
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.ResolveCall
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
private import semmle.code.cpp.models.interfaces.Taint
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking2
|
||||
private import semmle.code.cpp.ir.dataflow.TaintTracking3
|
||||
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
|
||||
/**
|
||||
* A predictable instruction is one where an external user can predict
|
||||
* the value. For example, a literal in the source code is considered
|
||||
* predictable.
|
||||
*/
|
||||
private predicate predictableInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction
|
||||
or
|
||||
instr instanceof StringConstantInstruction
|
||||
or
|
||||
// This could be a conversion on a string literal
|
||||
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.
|
||||
*/
|
||||
predicate predictableOnlyFlow(string name) {
|
||||
name =
|
||||
[
|
||||
"strcasestr", "strchnul", "strchr", "strchrnul", "strcmp", "strcspn", "strncmp", "strndup",
|
||||
"strnlen", "strrchr", "strspn", "strstr", "strtod", "strtof", "strtol", "strtoll", "strtoq",
|
||||
"strtoul"
|
||||
]
|
||||
}
|
||||
|
||||
private DataFlow::Node getNodeForSource(Expr source) {
|
||||
isUserInput(source, _) and
|
||||
result = getNodeForExpr(source)
|
||||
}
|
||||
|
||||
private DataFlow::Node getNodeForExpr(Expr node) {
|
||||
node = DataFlow::ExprFlowCached::asExprInternal(result)
|
||||
or
|
||||
// Some of the sources in `isUserInput` are intended to match the value of
|
||||
// an expression, while others (those modeled below) are intended to match
|
||||
// the taint that propagates out of an argument, like the `char *` argument
|
||||
// to `gets`. It's impossible here to tell which is which, but the "access
|
||||
// to argv" source is definitely not intended to match an output argument,
|
||||
// and it causes false positives if we let it.
|
||||
//
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `nodeIsBarrierIn`.
|
||||
result = DataFlow::definitionByReferenceNodeFromArgument(node) and
|
||||
not argv(node.(VariableAccess).getTarget())
|
||||
}
|
||||
|
||||
private predicate conflatePointerAndPointee(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
// Flow from `op` to `*op`.
|
||||
exists(Operand operand, int indirectionIndex |
|
||||
nodeHasOperand(nodeFrom, operand, indirectionIndex) and
|
||||
nodeHasOperand(nodeTo, operand, indirectionIndex - 1)
|
||||
)
|
||||
or
|
||||
// Flow from `instr` to `*instr`.
|
||||
exists(Instruction instr, int indirectionIndex |
|
||||
nodeHasInstruction(nodeFrom, instr, indirectionIndex) and
|
||||
nodeHasInstruction(nodeTo, instr, indirectionIndex - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private module DefaultTaintTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
conflatePointerAndPointee(nodeFrom, nodeTo)
|
||||
}
|
||||
}
|
||||
|
||||
private module DefaultTaintTrackingFlow = TaintTracking::Global<DefaultTaintTrackingConfig>;
|
||||
|
||||
private module ToGlobalVarTaintTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink.asVariable() instanceof GlobalOrNamespaceVariable }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private module ToGlobalVarTaintTrackingFlow = TaintTracking::Global<ToGlobalVarTaintTrackingConfig>;
|
||||
|
||||
private module FromGlobalVarTaintTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// This set of sources should be reasonably small, which is good for
|
||||
// performance since the set of sinks is very large.
|
||||
ToGlobalVarTaintTrackingFlow::flowTo(source)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
// Additional step for flow out of variables. There is no flow _into_
|
||||
// variables in this configuration, so this step only serves to take flow
|
||||
// out of a variable that's a source.
|
||||
readsVariable(n2.asInstruction(), n1.asVariable())
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private module FromGlobalVarTaintTrackingFlow =
|
||||
TaintTracking::Global<FromGlobalVarTaintTrackingConfig>;
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
private predicate writesVariable(StoreInstruction store, Variable var) {
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that has any kind of upper-bound check anywhere in the program. This is
|
||||
* biased towards being inclusive because there are a lot of valid ways of doing an
|
||||
* upper bounds checks if we don't consider where it occurs, for example:
|
||||
* ```
|
||||
* if (x < 10) { sink(x); }
|
||||
*
|
||||
* if (10 > y) { sink(y); }
|
||||
*
|
||||
* if (z > 10) { z = 10; }
|
||||
* sink(z);
|
||||
* ```
|
||||
*/
|
||||
// TODO: This coarse overapproximation, ported from the old taint tracking
|
||||
// library, could be replaced with an actual semantic check that a particular
|
||||
// variable _access_ is guarded by an upper-bound check. We probably don't want
|
||||
// to do this right away since it could expose a lot of FPs that were
|
||||
// previously suppressed by this predicate by coincidence.
|
||||
private predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
private predicate nodeIsBarrierEqualityCandidate(
|
||||
DataFlow::Node node, Operand access, Variable checkedVar
|
||||
) {
|
||||
exists(Instruction instr | instr = node.asOperand().getDef() |
|
||||
readsVariable(instr, checkedVar) and
|
||||
any(IRGuardCondition guard).ensuresEq(access, _, _, instr.getBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
predicate nodeIsBarrier(DataFlow::Node node) {
|
||||
exists(Variable checkedVar, Instruction instr | instr = node.asOperand().getDef() |
|
||||
readsVariable(instr, checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
exists(Variable checkedVar, Operand access |
|
||||
/*
|
||||
* This node is guarded by a condition that forces the accessed variable
|
||||
* to equal something else. For example:
|
||||
* ```
|
||||
* x = taintsource()
|
||||
* if (x == 10) {
|
||||
* taintsink(x); // not considered tainted
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
|
||||
readsVariable(access.getDef(), checkedVar)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||
// don't use dataflow into taint sources, as this leads to duplicate results.
|
||||
exists(Expr source | isUserInput(source, _) |
|
||||
source = DataFlow::ExprFlowCached::asExprInternal(node)
|
||||
or
|
||||
// This case goes together with the similar (but not identical) rule in
|
||||
// `getNodeForSource`.
|
||||
node = DataFlow::definitionByReferenceNodeFromArgument(source)
|
||||
)
|
||||
or
|
||||
// don't use dataflow into binary instructions if both operands are unpredictable
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not predictableInstruction(iTo.getLeft()) and
|
||||
not predictableInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of predictability
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
or
|
||||
// don't use dataflow through calls to pure functions if two or more operands
|
||||
// are unpredictable
|
||||
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
isPureFunction(iTo.getStaticCallTarget().getName()) and
|
||||
iFrom1 = iTo.getAnArgument() and
|
||||
iFrom2 = iTo.getAnArgument() and
|
||||
not predictableInstruction(iFrom1) and
|
||||
not predictableInstruction(iFrom2) and
|
||||
iFrom1 != iFrom2
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Element adjustedSink(DataFlow::Node sink) {
|
||||
// TODO: is it more appropriate to use asConvertedExpr here and avoid
|
||||
// `getConversion*`? Or will that cause us to miss some cases where there's
|
||||
// flow to a conversion (like a `ReferenceDereferenceExpr`) and we want to
|
||||
// pretend there was flow to the converted `Expr` for the sake of
|
||||
// compatibility.
|
||||
sink.asExpr().getConversion*() = result
|
||||
or
|
||||
// For compatibility, send flow from arguments to parameters, even for
|
||||
// functions with no body.
|
||||
exists(FunctionCall call, int i |
|
||||
sink.asExpr() = call.getArgument(pragma[only_bind_into](i)) and
|
||||
result = resolveCall(call).getParameter(pragma[only_bind_into](i))
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `Variable` if there is flow to any
|
||||
// Load or Store of that variable.
|
||||
exists(CopyInstruction copy |
|
||||
copy.getSourceValue() = sink.asInstruction() and
|
||||
(
|
||||
readsVariable(copy, result) or
|
||||
writesVariable(copy, result)
|
||||
) and
|
||||
not hasUpperBoundsCheck(result)
|
||||
)
|
||||
or
|
||||
// For compatibility, send flow into a `NotExpr` even if it's part of a
|
||||
// short-circuiting condition and thus might get skipped.
|
||||
result.(NotExpr).getOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint postfix and prefix crement operations when their operand is tainted.
|
||||
result.(CrementOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
|
||||
result.(AssignOperation).getAnOperand() = sink.asExpr()
|
||||
or
|
||||
result =
|
||||
sink.asOperand()
|
||||
.(SideEffectOperand)
|
||||
.getUse()
|
||||
.(ReadSideEffectInstruction)
|
||||
.getArgumentDef()
|
||||
.getUnconvertedResultExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Step to return value of a modeled function when an input taints the
|
||||
* dereference of the return value.
|
||||
*/
|
||||
cached
|
||||
predicate additionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(CallInstruction call, Function func, FunctionInput modelIn, FunctionOutput modelOut |
|
||||
n1 = callInput(call, modelIn) and
|
||||
(
|
||||
func.(TaintFunction).hasTaintFlow(modelIn, modelOut)
|
||||
or
|
||||
func.(DataFlowFunction).hasDataFlow(modelIn, modelOut)
|
||||
) and
|
||||
call.getStaticCallTarget() = func and
|
||||
modelOut.isReturnValueDeref() and
|
||||
call = n2.asInstruction()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call `taintedIncludingGlobalVars`.
|
||||
*/
|
||||
cached
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
exists(DataFlow::Node sink |
|
||||
DefaultTaintTrackingFlow::flow(getNodeForSource(source), sink) and
|
||||
tainted = adjustedSink(sink)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where the taint passed
|
||||
* through a global variable named `globalVar`.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This version gives the same results as tainted but also includes
|
||||
* data flow through global variables.
|
||||
*
|
||||
* The parameter `globalVar` is the qualified name of the last global variable
|
||||
* used to move the value from source to tainted. If the taint did not pass
|
||||
* through a global variable, then `globalVar = ""`.
|
||||
*/
|
||||
cached
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
tainted(source, tainted) and
|
||||
globalVar = ""
|
||||
or
|
||||
exists(
|
||||
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
||||
|
|
||||
global = variableNode.getVariable() and
|
||||
ToGlobalVarTaintTrackingFlow::flow(getNodeForSource(source), variableNode) and
|
||||
FromGlobalVarTaintTrackingFlow::flow(variableNode, sink) and
|
||||
tainted = adjustedSink(sink) and
|
||||
global = globalVarFromId(globalVar)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the global variable whose qualified name is `id`. Use this predicate
|
||||
* together with `taintedIncludingGlobalVars`. Example:
|
||||
*
|
||||
* ```
|
||||
* exists(string varName |
|
||||
* taintedIncludingGlobalVars(source, tainted, varName) and
|
||||
* var = globalVarFromId(varName)
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
GlobalOrNamespaceVariable globalVarFromId(string id) { id = result.getQualifiedName() }
|
||||
|
||||
/**
|
||||
* Provides definitions for augmenting source/sink pairs with data-flow paths
|
||||
* between them. From a `@kind path-problem` query, import this module in the
|
||||
* global scope, extend `TaintTrackingConfiguration`, and use `taintedWithPath`
|
||||
* in place of `tainted`.
|
||||
*
|
||||
* Importing this module will also import the query predicates that contain the
|
||||
* taint paths.
|
||||
*/
|
||||
module TaintedWithPath {
|
||||
private newtype TSingleton = MkSingleton()
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that matches sources and sinks in the same
|
||||
* way as the `tainted` predicate.
|
||||
*
|
||||
* Override `isSink` and `taintThroughGlobals` as needed, but do not provide
|
||||
* a characteristic predicate.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TSingleton {
|
||||
/** Override this to specify which elements are sources in this configuration. */
|
||||
predicate isSource(Expr source) { exists(getNodeForSource(source)) }
|
||||
|
||||
/** Override this to specify which elements are sinks in this configuration. */
|
||||
abstract predicate isSink(Element e);
|
||||
|
||||
/** Override this to specify which expressions are barriers in this configuration. */
|
||||
predicate isBarrier(Expr e) { nodeIsBarrier(getNodeForExpr(e)) }
|
||||
|
||||
/**
|
||||
* Override this predicate to `any()` to allow taint to flow through global
|
||||
* variables.
|
||||
*/
|
||||
predicate taintThroughGlobals() { none() }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = "TaintTrackingConfiguration" }
|
||||
}
|
||||
|
||||
private module AdjustedConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e |
|
||||
cfg.isSource(e) and source = getNodeForExpr(e)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(TaintTrackingConfiguration cfg | cfg.isSink(adjustedSink(sink)))
|
||||
}
|
||||
|
||||
predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
conflatePointerAndPointee(n1, n2)
|
||||
or
|
||||
// Steps into and out of global variables
|
||||
exists(TaintTrackingConfiguration cfg | cfg.taintThroughGlobals() |
|
||||
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
|
||||
or
|
||||
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
|
||||
)
|
||||
or
|
||||
additionalTaintStep(n1, n2)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
exists(TaintTrackingConfiguration cfg, Expr e | cfg.isBarrier(e) and node = getNodeForExpr(e))
|
||||
}
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
|
||||
predicate neverSkip(Node node) { none() }
|
||||
}
|
||||
|
||||
private module AdjustedFlow = TaintTracking::Global<AdjustedConfig>;
|
||||
|
||||
/*
|
||||
* A sink `Element` may map to multiple `DataFlowX::PathNode`s via (the
|
||||
* inverse of) `adjustedSink`. For example, an `Expr` maps to all its
|
||||
* conversions, and a `Variable` maps to all loads and stores from it. Because
|
||||
* the path node is part of the tuple that constitutes the alert, this leads
|
||||
* to duplicate alerts.
|
||||
*
|
||||
* To avoid showing duplicates, we edit the graph to replace the final node
|
||||
* coming from the data-flow library with a node that matches exactly the
|
||||
* `Element` sink that's requested.
|
||||
*
|
||||
* The same is done for sources.
|
||||
*/
|
||||
|
||||
private newtype TPathNode =
|
||||
TWrapPathNode(AdjustedFlow::PathNode n) or
|
||||
// There's a single newtype constructor for both sources and sinks since
|
||||
// that makes it easiest to deal with the case where source = sink.
|
||||
TEndpointPathNode(Element e) {
|
||||
exists(DataFlow::Node sourceNode, DataFlow::Node sinkNode |
|
||||
AdjustedFlow::flow(sourceNode, sinkNode)
|
||||
|
|
||||
sourceNode = getNodeForExpr(e) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSource(e))
|
||||
or
|
||||
e = adjustedSink(sinkNode) and
|
||||
exists(TaintTrackingConfiguration ttCfg | ttCfg.isSink(e))
|
||||
)
|
||||
}
|
||||
|
||||
/** An opaque type used for the nodes of a data-flow path. */
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
module Private {
|
||||
/** Gets a predecessor `PathNode` of `pathNode`, if any. */
|
||||
PathNode getAPredecessor(PathNode pathNode) { edges(result, pathNode) }
|
||||
|
||||
/** Gets the element that `pathNode` wraps, if any. */
|
||||
Element getElementFromPathNode(PathNode pathNode) {
|
||||
exists(DataFlow::Node node | node = pathNode.(WrapPathNode).inner().getNode() |
|
||||
result = node.asInstruction().getAst()
|
||||
or
|
||||
result = node.asOperand().getDef().getAst()
|
||||
)
|
||||
or
|
||||
result = pathNode.(EndpointPathNode).inner()
|
||||
}
|
||||
}
|
||||
|
||||
private class WrapPathNode extends PathNode, TWrapPathNode {
|
||||
AdjustedFlow::PathNode inner() { this = TWrapPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
private class EndpointPathNode extends PathNode, TEndpointPathNode {
|
||||
Expr inner() { this = TEndpointPathNode(result) }
|
||||
|
||||
override string toString() { result = this.inner().toString() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.inner()
|
||||
.getLocation()
|
||||
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a source. It may also be a sink. */
|
||||
private class InitialPathNode extends EndpointPathNode {
|
||||
InitialPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSource(this.inner())) }
|
||||
}
|
||||
|
||||
/** A PathNode whose `Element` is a sink. It may also be a source. */
|
||||
private class FinalPathNode extends EndpointPathNode {
|
||||
FinalPathNode() { exists(TaintTrackingConfiguration cfg | cfg.isSink(this.inner())) }
|
||||
}
|
||||
|
||||
/** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */
|
||||
query predicate edges(PathNode a, PathNode b) {
|
||||
AdjustedFlow::PathGraph::edges(a.(WrapPathNode).inner(), b.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
AdjustedFlow::PathGraph::edges(a.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
AdjustedFlow::PathGraph::edges(sourceNode.inner(), b.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
AdjustedFlow::PathGraph::edges(sourceNode.inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(a.(InitialPathNode).inner()) and
|
||||
b.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is flow from `arg` to `out` across a call that can by summarized by the flow
|
||||
* from `par` to `ret` within it, in the graph of data flow path explanations.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
AdjustedFlow::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner())
|
||||
or
|
||||
// To avoid showing trivial-looking steps, we _replace_ the last node instead
|
||||
// of adding an edge out of it.
|
||||
exists(WrapPathNode sinkNode |
|
||||
AdjustedFlow::PathGraph::subpaths(arg.(WrapPathNode).inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
or
|
||||
// Same for the first node
|
||||
exists(WrapPathNode sourceNode |
|
||||
AdjustedFlow::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), out.(WrapPathNode).inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner())
|
||||
)
|
||||
or
|
||||
// Finally, handle the case where the path goes directly from a source to a
|
||||
// sink, meaning that they both need to be translated.
|
||||
exists(WrapPathNode sinkNode, WrapPathNode sourceNode |
|
||||
AdjustedFlow::PathGraph::subpaths(sourceNode.inner(), par.(WrapPathNode).inner(),
|
||||
ret.(WrapPathNode).inner(), sinkNode.inner()) and
|
||||
sourceNode.inner().getNode() = getNodeForExpr(arg.(InitialPathNode).inner()) and
|
||||
out.(FinalPathNode).inner() = adjustedSink(sinkNode.inner().getNode())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` may contain taint from `source`, where `sourceNode` and
|
||||
* `sinkNode` are the corresponding `PathNode`s that can be used in a query
|
||||
* to provide path explanations. Extend `TaintTrackingConfiguration` to use
|
||||
* this predicate.
|
||||
*
|
||||
* A tainted expression is either directly user input, or is computed from
|
||||
* user input in a way that users can probably control the exact output of
|
||||
* the computation.
|
||||
*/
|
||||
predicate taintedWithPath(Expr source, Element tainted, PathNode sourceNode, PathNode sinkNode) {
|
||||
exists(DataFlow::Node flowSource, DataFlow::Node flowSink |
|
||||
source = sourceNode.(InitialPathNode).inner() and
|
||||
flowSource = getNodeForExpr(source) and
|
||||
AdjustedFlow::flow(flowSource, flowSink) and
|
||||
tainted = adjustedSink(flowSink) and
|
||||
tainted = sinkNode.(FinalPathNode).inner()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isGlobalVariablePathNode(WrapPathNode n) {
|
||||
n.inner().getNode().asVariable() instanceof GlobalOrNamespaceVariable
|
||||
or
|
||||
n.inner().getNode().asIndirectVariable() instanceof GlobalOrNamespaceVariable
|
||||
}
|
||||
|
||||
private predicate edgesWithoutGlobals(PathNode a, PathNode b) {
|
||||
edges(a, b) and
|
||||
not isGlobalVariablePathNode(a) and
|
||||
not isGlobalVariablePathNode(b)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `tainted` can be reached from a taint source without passing
|
||||
* through a global variable.
|
||||
*/
|
||||
predicate taintedWithoutGlobals(Element tainted) {
|
||||
exists(PathNode sourceNode, FinalPathNode sinkNode |
|
||||
AdjustedConfig::isSource(sourceNode.(WrapPathNode).inner().getNode()) and
|
||||
edgesWithoutGlobals+(sourceNode, sinkNode) and
|
||||
tainted = sinkNode.inner()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/**
|
||||
* Support for tracking tainted data through the program. This is an alias for
|
||||
* `semmle.code.cpp.ir.dataflow.DefaultTaintTracking` provided for backwards
|
||||
* compatibility.
|
||||
*
|
||||
* Prefer to use `semmle.code.cpp.dataflow.TaintTracking` or
|
||||
* `semmle.code.cpp.ir.dataflow.TaintTracking` when designing new queries.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
|
||||
@@ -1,654 +0,0 @@
|
||||
/**
|
||||
* DEPRECATED: we now use `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`,
|
||||
* which is based on the IR but designed to behave similarly to this old
|
||||
* library.
|
||||
*
|
||||
* Provides the implementation of `semmle.code.cpp.security.TaintTracking`. Do
|
||||
* not import this file directly.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import Security
|
||||
|
||||
/** Expressions that change the value of a variable */
|
||||
private predicate valueSource(Expr expr) {
|
||||
exists(AssignExpr ae | expr = ae.getLValue())
|
||||
or
|
||||
exists(FunctionCall fc, int i |
|
||||
userInputArgument(fc, i) and
|
||||
expr = fc.getArgument(i)
|
||||
)
|
||||
or
|
||||
exists(FunctionCall c, int arg |
|
||||
copyValueBetweenArguments(c.getTarget(), _, arg) and
|
||||
expr = c.getArgument(arg)
|
||||
)
|
||||
or
|
||||
exists(FunctionCall c, int arg |
|
||||
c.getTarget().getParameter(arg).getType() instanceof ReferenceType and
|
||||
expr = c.getArgument(arg)
|
||||
)
|
||||
}
|
||||
|
||||
/** Expressions that are inside an expression that changes the value of a variable */
|
||||
private predicate insideValueSource(Expr expr) {
|
||||
valueSource(expr)
|
||||
or
|
||||
insideValueSource(expr.getParent()) and
|
||||
// A modification of array[offset] does not modify offset
|
||||
not expr.getParent().(ArrayExpr).getArrayOffset() = expr
|
||||
}
|
||||
|
||||
private predicate isPointer(Type type) {
|
||||
type instanceof PointerType or
|
||||
isPointer(type.(ReferenceType).getBaseType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks data flow from src to dest.
|
||||
* If this is used in the left side of an assignment src and dest should be swapped
|
||||
*/
|
||||
private predicate moveToDependingOnSide(Expr src, Expr dest) {
|
||||
exists(ParenthesisExpr e |
|
||||
src = e.getAChild() and
|
||||
dest = e
|
||||
)
|
||||
or
|
||||
exists(ArrayExpr e |
|
||||
src = e.getArrayBase() and
|
||||
dest = e
|
||||
)
|
||||
or
|
||||
exists(PointerDereferenceExpr e |
|
||||
src = e.getOperand() and
|
||||
dest = e
|
||||
)
|
||||
or
|
||||
exists(AddressOfExpr e |
|
||||
src = e.getOperand() and
|
||||
dest = e
|
||||
)
|
||||
or
|
||||
// if var+offset is tainted, then so is var
|
||||
exists(VariableAccess base, BinaryOperation binop |
|
||||
dest = binop and
|
||||
(base = binop.getLeftOperand() or base = binop.getRightOperand()) and
|
||||
isPointer(base.getType()) and
|
||||
base.getTarget() instanceof LocalScopeVariable and
|
||||
src = base and
|
||||
// flow through pointer-pointer subtraction is dubious, the result should be
|
||||
// a number bounded by the size of the pointed-to thing.
|
||||
not binop instanceof PointerDiffExpr
|
||||
)
|
||||
or
|
||||
exists(UnaryOperation unop |
|
||||
dest = unop and
|
||||
unop.getAnOperand() = src
|
||||
)
|
||||
or
|
||||
exists(BinaryOperation binop |
|
||||
dest = binop and
|
||||
binop.getLeftOperand() = src and
|
||||
predictable(binop.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(BinaryOperation binop |
|
||||
dest = binop and
|
||||
binop.getRightOperand() = src and
|
||||
predictable(binop.getLeftOperand())
|
||||
)
|
||||
or
|
||||
exists(Cast cast |
|
||||
dest = cast and
|
||||
src = cast.getExpr()
|
||||
)
|
||||
or
|
||||
exists(ConditionalExpr cond |
|
||||
cond = dest and
|
||||
(
|
||||
cond.getThen() = src or
|
||||
cond.getElse() = src
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Track value flow between functions.
|
||||
* Handles the following cases:
|
||||
* - If an argument to a function is tainted, all the usages of the parameter inside the function are tainted
|
||||
* - If a function obtains input from the user internally and returns it, all calls to the function are tainted
|
||||
* - If an argument to a function is tainted and that parameter is returned, all calls to the function are not tainted
|
||||
* (this is done to avoid false positives). Because of this we need to track if the tainted element came from an argument
|
||||
* or not, and for that we use destFromArg
|
||||
*/
|
||||
deprecated private predicate betweenFunctionsValueMoveTo(
|
||||
Element src, Element dest, boolean destFromArg
|
||||
) {
|
||||
not unreachable(src) and
|
||||
not unreachable(dest) and
|
||||
(
|
||||
exists(Call call, int i |
|
||||
src = call.getArgument(i) and
|
||||
resolveCallWithParam(call, _, i, dest) and
|
||||
destFromArg = true
|
||||
)
|
||||
or
|
||||
// Only move the return of the function to the function itself if the value didn't came from an
|
||||
// argument, or else we would taint all the calls to one function if one argument is tainted
|
||||
// somewhere
|
||||
exists(Function f, ReturnStmt ret |
|
||||
ret.getEnclosingFunction() = f and
|
||||
src = ret.getExpr() and
|
||||
destFromArg = false and
|
||||
dest = f
|
||||
)
|
||||
or
|
||||
exists(Call call, Function f |
|
||||
f = resolveCall(call) and
|
||||
src = f and
|
||||
dest = call and
|
||||
destFromArg = false
|
||||
)
|
||||
or
|
||||
// If a parameter of type reference is tainted inside a function, taint the argument too
|
||||
exists(Call call, int pi, Parameter p |
|
||||
resolveCallWithParam(call, _, pi, p) and
|
||||
p.getType() instanceof ReferenceType and
|
||||
src = p and
|
||||
dest = call.getArgument(pi) and
|
||||
destFromArg = false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// predicate folding for proper join-order
|
||||
// bad magic: pushes down predicate that ruins join-order
|
||||
pragma[nomagic]
|
||||
deprecated private predicate resolveCallWithParam(Call call, Function called, int i, Parameter p) {
|
||||
called = resolveCall(call) and
|
||||
p = called.getParameter(i)
|
||||
}
|
||||
|
||||
/** A variable for which flow through is allowed. */
|
||||
deprecated library class FlowVariable extends Variable {
|
||||
FlowVariable() {
|
||||
(
|
||||
this instanceof LocalScopeVariable or
|
||||
this instanceof GlobalOrNamespaceVariable
|
||||
) and
|
||||
not argv(this)
|
||||
}
|
||||
}
|
||||
|
||||
/** A local scope variable for which flow through is allowed. */
|
||||
deprecated library class FlowLocalScopeVariable extends Variable {
|
||||
FlowLocalScopeVariable() { this instanceof LocalScopeVariable }
|
||||
}
|
||||
|
||||
deprecated private predicate insideFunctionValueMoveTo(Element src, Element dest) {
|
||||
not unreachable(src) and
|
||||
not unreachable(dest) and
|
||||
(
|
||||
// Taint all variable usages when one is tainted
|
||||
// This function taints global variables but doesn't taint from a global variable (see globalVariableValueMoveTo)
|
||||
exists(FlowLocalScopeVariable v |
|
||||
src = v and
|
||||
dest = v.getAnAccess() and
|
||||
not insideValueSource(dest)
|
||||
)
|
||||
or
|
||||
exists(FlowVariable v |
|
||||
src = v.getAnAccess() and
|
||||
dest = v and
|
||||
insideValueSource(src)
|
||||
)
|
||||
or
|
||||
// Taint all union usages when one is tainted
|
||||
// This function taints global variables but doesn't taint from a global variable (see globalVariableValueMoveTo)
|
||||
exists(FlowLocalScopeVariable v, FieldAccess a |
|
||||
unionAccess(v, _, a) and
|
||||
src = v and
|
||||
dest = a and
|
||||
not insideValueSource(dest)
|
||||
)
|
||||
or
|
||||
exists(FlowVariable v, FieldAccess a |
|
||||
unionAccess(v, _, a) and
|
||||
src = a and
|
||||
dest = v and
|
||||
insideValueSource(src)
|
||||
)
|
||||
or
|
||||
// If a pointer is tainted, taint the original variable
|
||||
exists(FlowVariable p, FlowVariable v, AddressOfExpr e |
|
||||
p.getAnAssignedValue() = e and
|
||||
e.getOperand() = v.getAnAccess() and
|
||||
src = p and
|
||||
dest = v
|
||||
)
|
||||
or
|
||||
// If a reference is tainted, taint the original variable
|
||||
exists(FlowVariable r, FlowVariable v |
|
||||
r.getType() instanceof ReferenceType and
|
||||
r.getInitializer().getExpr() = v.getAnAccess() and
|
||||
src = r and
|
||||
dest = v
|
||||
)
|
||||
or
|
||||
exists(Variable var |
|
||||
var = dest and
|
||||
var.getInitializer().getExpr() = src
|
||||
)
|
||||
or
|
||||
exists(AssignExpr ae |
|
||||
src = ae.getRValue() and
|
||||
dest = ae.getLValue()
|
||||
)
|
||||
or
|
||||
exists(CommaExpr comma |
|
||||
comma = dest and
|
||||
comma.getRightOperand() = src
|
||||
)
|
||||
or
|
||||
exists(FunctionCall c, int sourceArg, int destArg |
|
||||
copyValueBetweenArguments(c.getTarget(), sourceArg, destArg) and
|
||||
// Only consider copies from `printf`-like functions if the format is a string
|
||||
(
|
||||
exists(FormattingFunctionCall ffc, FormatLiteral format |
|
||||
ffc = c and
|
||||
format = ffc.getFormat() and
|
||||
format.getConversionChar(sourceArg - ffc.getTarget().getNumberOfParameters()) = ["s", "S"]
|
||||
)
|
||||
or
|
||||
not c.(FormattingFunctionCall).getFormat() instanceof FormatLiteral
|
||||
or
|
||||
not c instanceof FormattingFunctionCall
|
||||
) and
|
||||
src = c.getArgument(sourceArg) and
|
||||
dest = c.getArgument(destArg)
|
||||
)
|
||||
or
|
||||
exists(FunctionCall c, int sourceArg |
|
||||
returnArgument(c.getTarget(), sourceArg) and
|
||||
src = c.getArgument(sourceArg) and
|
||||
dest = c
|
||||
)
|
||||
or
|
||||
exists(FormattingFunctionCall formattingSend, int arg, FormatLiteral format |
|
||||
dest = formattingSend and
|
||||
formattingSend.getArgument(arg) = src and
|
||||
format = formattingSend.getFormat() and
|
||||
format.getConversionChar(arg - formattingSend.getTarget().getNumberOfParameters()) =
|
||||
["s", "S", "@"]
|
||||
)
|
||||
or
|
||||
// Expressions computed from tainted data are also tainted
|
||||
exists(FunctionCall call | dest = call and isPureFunction(call.getTarget().getName()) |
|
||||
call.getAnArgument() = src and
|
||||
forall(Expr arg | arg = call.getAnArgument() | arg = src or predictable(arg)) and
|
||||
// flow through `strlen` tends to cause dubious results, if the length is
|
||||
// bounded.
|
||||
not call.getTarget().getName() = "strlen"
|
||||
)
|
||||
or
|
||||
exists(Element a, Element b |
|
||||
moveToDependingOnSide(a, b) and
|
||||
if insideValueSource(a) then (src = b and dest = a) else (src = a and dest = b)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles data flow from global variables to its usages.
|
||||
* The tainting for the global variable itself is done at insideFunctionValueMoveTo.
|
||||
*/
|
||||
private predicate globalVariableValueMoveTo(GlobalOrNamespaceVariable src, Expr dest) {
|
||||
not unreachable(dest) and
|
||||
(
|
||||
exists(GlobalOrNamespaceVariable v |
|
||||
src = v and
|
||||
dest = v.getAnAccess() and
|
||||
not insideValueSource(dest)
|
||||
)
|
||||
or
|
||||
exists(GlobalOrNamespaceVariable v, FieldAccess a |
|
||||
unionAccess(v, _, a) and
|
||||
src = v and
|
||||
dest = a and
|
||||
not insideValueSource(dest)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate unionAccess(Variable v, Field f, FieldAccess a) {
|
||||
f.getDeclaringType() instanceof Union and
|
||||
a.getTarget() = f and
|
||||
a.getQualifier() = v.getAnAccess()
|
||||
}
|
||||
|
||||
deprecated GlobalOrNamespaceVariable globalVarFromId(string id) {
|
||||
if result instanceof NamespaceVariable
|
||||
then id = result.getNamespace() + "::" + result.getName()
|
||||
else id = result.getName()
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable that has any kind of upper-bound check anywhere in the program. This is
|
||||
* biased towards being inclusive because there are a lot of valid ways of doing an
|
||||
* upper bounds checks if we don't consider where it occurs, for example:
|
||||
* ```
|
||||
* if (x < 10) { sink(x); }
|
||||
*
|
||||
* if (10 > y) { sink(y); }
|
||||
*
|
||||
* if (z > 10) { z = 10; }
|
||||
* sink(z);
|
||||
* ```
|
||||
*/
|
||||
private predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
deprecated private predicate taintedWithArgsAndGlobalVars(
|
||||
Element src, Element dest, boolean destFromArg, string globalVar
|
||||
) {
|
||||
isUserInput(src, _) and
|
||||
not unreachable(src) and
|
||||
dest = src and
|
||||
destFromArg = false and
|
||||
globalVar = ""
|
||||
or
|
||||
exists(Element other, boolean otherFromArg, string otherGlobalVar |
|
||||
taintedWithArgsAndGlobalVars(src, other, otherFromArg, otherGlobalVar)
|
||||
|
|
||||
not unreachable(dest) and
|
||||
not hasUpperBoundsCheck(dest) and
|
||||
(
|
||||
// Direct flow from one expression to another.
|
||||
betweenFunctionsValueMoveTo(other, dest, destFromArg) and
|
||||
(destFromArg = true or otherFromArg = false) and
|
||||
globalVar = otherGlobalVar
|
||||
or
|
||||
insideFunctionValueMoveTo(other, dest) and
|
||||
destFromArg = otherFromArg and
|
||||
globalVar = otherGlobalVar
|
||||
or
|
||||
exists(GlobalOrNamespaceVariable v |
|
||||
v = other and
|
||||
globalVariableValueMoveTo(v, dest) and
|
||||
destFromArg = false and
|
||||
v = globalVarFromId(globalVar)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This doesn't include data flow through global variables.
|
||||
* If you need that you must call taintedIncludingGlobalVars.
|
||||
*/
|
||||
deprecated predicate tainted(Expr source, Element tainted) {
|
||||
taintedWithArgsAndGlobalVars(source, tainted, _, "")
|
||||
}
|
||||
|
||||
/**
|
||||
* A tainted expression is either directly user input, or is
|
||||
* computed from user input in a way that users can probably
|
||||
* control the exact output of the computation.
|
||||
*
|
||||
* This version gives the same results as tainted but also includes
|
||||
* data flow through global variables.
|
||||
*
|
||||
* The parameter `globalVar` is the name of the last global variable used to move the
|
||||
* value from source to tainted.
|
||||
*/
|
||||
deprecated predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
taintedWithArgsAndGlobalVars(source, tainted, _, globalVar)
|
||||
}
|
||||
|
||||
/**
|
||||
* A predictable expression is one where an external user can predict
|
||||
* the value. For example, a literal in the source code is considered
|
||||
* predictable.
|
||||
*/
|
||||
private predicate predictable(Expr expr) {
|
||||
expr instanceof Literal
|
||||
or
|
||||
exists(BinaryOperation binop | binop = expr |
|
||||
predictable(binop.getLeftOperand()) and predictable(binop.getRightOperand())
|
||||
)
|
||||
or
|
||||
exists(UnaryOperation unop | unop = expr | predictable(unop.getOperand()))
|
||||
}
|
||||
|
||||
private int maxArgIndex(Function f) {
|
||||
result =
|
||||
max(FunctionCall fc, int toMax |
|
||||
fc.getTarget() = f and toMax = fc.getNumberOfArguments() - 1
|
||||
|
|
||||
toMax
|
||||
)
|
||||
}
|
||||
|
||||
/** Functions that copy the value of one argument to another */
|
||||
private predicate copyValueBetweenArguments(Function f, int sourceArg, int destArg) {
|
||||
f.hasGlobalOrStdName("memcpy") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalName("__builtin___memcpy_chk") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("memmove") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("strcat") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalName("_mbscat") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("wcscat") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("strncat") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalName("_mbsncat") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalName("wcsncat") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("strcpy") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalName("_mbscpy") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("wcscpy") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("strncpy") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalName("_mbsncpy") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("wcsncpy") and sourceArg = 1 and destArg = 0
|
||||
or
|
||||
f.hasGlobalName("inet_aton") and sourceArg = 0 and destArg = 1
|
||||
or
|
||||
f.hasGlobalName("inet_pton") and sourceArg = 1 and destArg = 2
|
||||
or
|
||||
f.hasGlobalOrStdName("strftime") and sourceArg in [2 .. maxArgIndex(f)] and destArg = 0
|
||||
or
|
||||
exists(FormattingFunction ff | ff = f |
|
||||
sourceArg in [ff.getFormatParameterIndex() .. maxArgIndex(f)] and
|
||||
destArg = ff.getOutputParameterIndex(false)
|
||||
)
|
||||
}
|
||||
|
||||
/** Functions where if one of the arguments is tainted, the result should be tainted */
|
||||
private predicate returnArgument(Function f, int sourceArg) {
|
||||
f.hasGlobalName("memcpy") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("__builtin___memcpy_chk") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("memmove") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("strcat") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("_mbscat") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("wcsncat") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("strncat") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("_mbsncat") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("wcsncat") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("strcpy") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("_mbscpy") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("wcscpy") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("strncpy") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("_mbsncpy") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("wcsncpy") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("inet_ntoa") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("inet_addr") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("inet_network") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("inet_ntoa") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("inet_makeaddr") and
|
||||
(sourceArg = 0 or sourceArg = 1)
|
||||
or
|
||||
f.hasGlobalName("inet_lnaof") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("inet_netof") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("gethostbyname") and sourceArg = 0
|
||||
or
|
||||
f.hasGlobalName("gethostbyaddr") and sourceArg = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve potential target function(s) for `call`.
|
||||
*
|
||||
* If `call` is a call through a function pointer (`ExprCall`) or
|
||||
* targets a virtual method, simple data flow analysis is performed
|
||||
* in order to identify target(s).
|
||||
*/
|
||||
deprecated Function resolveCall(Call call) {
|
||||
result = call.getTarget()
|
||||
or
|
||||
result = call.(DataSensitiveCallExpr).resolve()
|
||||
}
|
||||
|
||||
/** A data sensitive call expression. */
|
||||
abstract deprecated library class DataSensitiveCallExpr extends Expr {
|
||||
DataSensitiveCallExpr() { not unreachable(this) }
|
||||
|
||||
abstract Expr getSrc();
|
||||
|
||||
cached
|
||||
abstract Function resolve();
|
||||
|
||||
/**
|
||||
* Whether `src` can flow to this call expression.
|
||||
*
|
||||
* Searches backwards from `getSrc()` to `src`.
|
||||
*/
|
||||
predicate flowsFrom(Element src, boolean allowFromArg) {
|
||||
src = this.getSrc() and allowFromArg = true
|
||||
or
|
||||
exists(Element other, boolean allowOtherFromArg | this.flowsFrom(other, allowOtherFromArg) |
|
||||
exists(boolean otherFromArg | betweenFunctionsValueMoveToStatic(src, other, otherFromArg) |
|
||||
otherFromArg = true and allowOtherFromArg = true and allowFromArg = true
|
||||
or
|
||||
otherFromArg = false and allowFromArg = false
|
||||
)
|
||||
or
|
||||
insideFunctionValueMoveTo(src, other) and allowFromArg = allowOtherFromArg
|
||||
or
|
||||
globalVariableValueMoveTo(src, other) and allowFromArg = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Call through a function pointer. */
|
||||
deprecated library class DataSensitiveExprCall extends DataSensitiveCallExpr, ExprCall {
|
||||
override Expr getSrc() { result = this.getExpr() }
|
||||
|
||||
override Function resolve() {
|
||||
exists(FunctionAccess fa | this.flowsFrom(fa, true) | result = fa.getTarget())
|
||||
}
|
||||
}
|
||||
|
||||
/** Call to a virtual function. */
|
||||
deprecated library class DataSensitiveOverriddenFunctionCall extends DataSensitiveCallExpr,
|
||||
FunctionCall
|
||||
{
|
||||
DataSensitiveOverriddenFunctionCall() {
|
||||
exists(this.getTarget().(VirtualFunction).getAnOverridingFunction())
|
||||
}
|
||||
|
||||
override Expr getSrc() { result = this.getQualifier() }
|
||||
|
||||
override MemberFunction resolve() {
|
||||
exists(NewExpr new |
|
||||
this.flowsFrom(new, true) and
|
||||
memberFunctionFromNewExpr(new, result) and
|
||||
result.overrides*(this.getTarget().(VirtualFunction))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate memberFunctionFromNewExpr(NewExpr new, MemberFunction f) {
|
||||
f = new.getAllocatedType().(Class).getAMemberFunction()
|
||||
}
|
||||
|
||||
/** Same as `betweenFunctionsValueMoveTo`, but calls are resolved to their static target. */
|
||||
private predicate betweenFunctionsValueMoveToStatic(Element src, Element dest, boolean destFromArg) {
|
||||
not unreachable(src) and
|
||||
not unreachable(dest) and
|
||||
(
|
||||
exists(FunctionCall call, Function called, int i |
|
||||
src = call.getArgument(i) and
|
||||
called = call.getTarget() and
|
||||
dest = called.getParameter(i) and
|
||||
destFromArg = true
|
||||
)
|
||||
or
|
||||
// Only move the return of the function to the function itself if the value didn't came from an
|
||||
// argument, or else we would taint all the calls to one function if one argument is tainted
|
||||
// somewhere
|
||||
exists(Function f, ReturnStmt ret |
|
||||
ret.getEnclosingFunction() = f and
|
||||
src = ret.getExpr() and
|
||||
destFromArg = false and
|
||||
dest = f
|
||||
)
|
||||
or
|
||||
exists(FunctionCall call, Function f |
|
||||
call.getTarget() = f and
|
||||
src = f and
|
||||
dest = call and
|
||||
destFromArg = false
|
||||
)
|
||||
or
|
||||
// If a parameter of type reference is tainted inside a function, taint the argument too
|
||||
exists(FunctionCall call, Function f, int pi, Parameter p |
|
||||
call.getTarget() = f and
|
||||
f.getParameter(pi) = p and
|
||||
p.getType() instanceof ReferenceType and
|
||||
src = p and
|
||||
dest = call.getArgument(pi) and
|
||||
destFromArg = false
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
#include "../shared.h"
|
||||
|
||||
using SinkFunction = void (*)(int);
|
||||
|
||||
void notSink(int notSinkParam);
|
||||
|
||||
void callsSink(int sinkParam) { // $ ir-path=31:23 ir-path=32:26 ir-path=34:17
|
||||
sink(sinkParam); // $ ast=31:28 ast=32:31 ast=34:22 ir-sink
|
||||
}
|
||||
|
||||
struct {
|
||||
SinkFunction sinkPtr, notSinkPtr;
|
||||
} globalStruct;
|
||||
|
||||
union {
|
||||
SinkFunction sinkPtr, notSinkPtr;
|
||||
} globalUnion;
|
||||
|
||||
SinkFunction globalSinkPtr;
|
||||
|
||||
void assignGlobals() {
|
||||
globalStruct.sinkPtr = callsSink;
|
||||
globalUnion.sinkPtr = callsSink;
|
||||
globalSinkPtr = callsSink;
|
||||
};
|
||||
|
||||
void testStruct() {
|
||||
globalStruct.sinkPtr(atoi(getenv("TAINTED"))); // $ MISSING: ir-path,ast
|
||||
globalStruct.notSinkPtr(atoi(getenv("TAINTED"))); // clean
|
||||
|
||||
globalUnion.sinkPtr(atoi(getenv("TAINTED"))); // $ ast ir-path
|
||||
globalUnion.notSinkPtr(atoi(getenv("TAINTED"))); // $ ast ir-path
|
||||
|
||||
globalSinkPtr(atoi(getenv("TAINTED"))); // $ ast ir-path
|
||||
}
|
||||
|
||||
class B {
|
||||
public:
|
||||
virtual void f(const char*) = 0;
|
||||
};
|
||||
|
||||
class D1 : public B {};
|
||||
|
||||
class D2 : public D1 {
|
||||
public:
|
||||
void f(const char* p) override {}
|
||||
};
|
||||
|
||||
class D3 : public D2 {
|
||||
public:
|
||||
void f(const char* p) override { // $ ir-path=58:10 ir-path=60:17 ir-path=61:28 ir-path=62:29 ir-path=63:33 SPURIOUS: ir-path=73:30
|
||||
sink(p); // $ ast=58:10 ast=60:17 ast=61:28 ast=62:29 ast=63:33 ir-sink SPURIOUS: ast=73:30
|
||||
}
|
||||
};
|
||||
|
||||
void test_dynamic_cast() {
|
||||
B* b = new D3();
|
||||
b->f(getenv("VAR")); // $ ast ir-path
|
||||
|
||||
((D2*)b)->f(getenv("VAR")); // $ ast ir-path
|
||||
static_cast<D2*>(b)->f(getenv("VAR")); // $ ast ir-path
|
||||
dynamic_cast<D2*>(b)->f(getenv("VAR")); // $ ast ir-path
|
||||
reinterpret_cast<D2*>(b)->f(getenv("VAR")); // $ ast ir-path
|
||||
|
||||
B* b2 = new D2();
|
||||
b2->f(getenv("VAR"));
|
||||
|
||||
((D2*)b2)->f(getenv("VAR"));
|
||||
static_cast<D2*>(b2)->f(getenv("VAR"));
|
||||
dynamic_cast<D2*>(b2)->f(getenv("VAR"));
|
||||
reinterpret_cast<D2*>(b2)->f(getenv("VAR"));
|
||||
|
||||
dynamic_cast<D3*>(b2)->f(getenv("VAR")); // $ SPURIOUS: ast ir-path
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted.ql:9,8-47)
|
||||
WARNING: Predicate tainted has been deprecated and may be removed in future (tainted.ql:20,49-74)
|
||||
testFailures
|
||||
failures
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* This test provides the possibility to annotate elements when they are on a path of a taint flow to a sink.
|
||||
* This is different when compared to the tests in `../annotate_sink`, where only sink invocations are annotated.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.TaintTrackingImpl as AstTaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IRDefaultTaintTracking
|
||||
import IRDefaultTaintTracking::TaintedWithPath as TaintedWithPath
|
||||
import TaintedWithPath::Private
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
predicate isSinkArgument(Element sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
predicate astTaint(Expr source, Element sink) { AstTaintTracking::tainted(source, sink) }
|
||||
|
||||
class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
|
||||
override predicate isSink(Element e) { isSinkArgument(e) }
|
||||
}
|
||||
|
||||
predicate irTaint(Element source, TaintedWithPath::PathNode predNode, string tag) {
|
||||
exists(TaintedWithPath::PathNode sinkNode |
|
||||
TaintedWithPath::taintedWithPath(source, _, _, sinkNode) and
|
||||
predNode = getAPredecessor*(sinkNode) and
|
||||
// Make sure the path is actually reachable from this predecessor.
|
||||
// Otherwise, we could pick `predNode` to be b when `source` is
|
||||
// `source1` in this dataflow graph:
|
||||
// source1 ---> a ---> c ---> sinkNode
|
||||
// ^
|
||||
// source2 ---> b --/
|
||||
source = getElementFromPathNode(getAPredecessor*(predNode)) and
|
||||
if predNode = sinkNode then tag = "ir-sink" else tag = "ir-path"
|
||||
)
|
||||
}
|
||||
|
||||
module IRDefaultTaintTrackingTest implements TestSig {
|
||||
string getARelevantTag() { result = ["ir-path", "ir-sink"] }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Element elem, TaintedWithPath::PathNode node, int n |
|
||||
irTaint(_, node, tag) and
|
||||
elem = getElementFromPathNode(node) and
|
||||
n = count(int startline | getAPredecessor(node).hasLocationInfo(_, startline, _, _, _)) and
|
||||
location = elem.getLocation() and
|
||||
element = elem.toString()
|
||||
|
|
||||
// Zero predecessors means it's a source, and 1 predecessor means it has a unique predecessor.
|
||||
// In either of these cases we leave out the location.
|
||||
n = [0, 1] and value = ""
|
||||
or
|
||||
// If there is more than one predecessor for this node
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
exists(TaintedWithPath::PathNode pred | pred = getAPredecessor(node) |
|
||||
value =
|
||||
getElementFromPathNode(pred).getLocation().getStartLine().toString() + ":" +
|
||||
getElementFromPathNode(pred).getLocation().getStartColumn()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module AstTaintTrackingTest implements TestSig {
|
||||
string getARelevantTag() { result = "ast" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Expr source, Element tainted, int n |
|
||||
tag = "ast" and
|
||||
astTaint(source, tainted) and
|
||||
(
|
||||
isSinkArgument(tainted)
|
||||
or
|
||||
exists(Element sink |
|
||||
isSinkArgument(sink) and
|
||||
astTaint(tainted, sink)
|
||||
)
|
||||
) and
|
||||
n = strictcount(Expr otherSource | astTaint(otherSource, tainted)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = tainted.getLocation() and
|
||||
element = tainted.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<MergeTests<IRDefaultTaintTrackingTest, AstTaintTrackingTest>>
|
||||
@@ -1,129 +0,0 @@
|
||||
#include "../shared.h"
|
||||
|
||||
|
||||
struct S {
|
||||
void(*f)(const char*);
|
||||
|
||||
void apply(char* p) {
|
||||
f(p);
|
||||
}
|
||||
|
||||
void (*get())(const char*) {
|
||||
return f;
|
||||
}
|
||||
};
|
||||
|
||||
void calls_sink_with_argv(const char* a) { // $ ir-path=96:26 ir-path=102:26
|
||||
sink(a); // $ ast=96:26 ast=98:18 ir-sink
|
||||
}
|
||||
|
||||
extern int i;
|
||||
|
||||
class BaseWithPureVirtual {
|
||||
public:
|
||||
virtual void f(const char*) = 0;
|
||||
};
|
||||
|
||||
class DerivedCallsSink : public BaseWithPureVirtual {
|
||||
public:
|
||||
void f(const char* p) override { // $ ir-path
|
||||
sink(p); // $ ast=108:10 ir-sink SPURIOUS: ast=111:10
|
||||
}
|
||||
};
|
||||
|
||||
class DerivedDoesNotCallSink : public BaseWithPureVirtual {
|
||||
public:
|
||||
void f(const char* p) override {}
|
||||
};
|
||||
|
||||
class DerivedCallsSinkDiamond1 : virtual public BaseWithPureVirtual {
|
||||
public:
|
||||
void f(const char* p) override { // $ ir-path
|
||||
sink(p); // $ ast ir-sink
|
||||
}
|
||||
};
|
||||
|
||||
class DerivedDoesNotCallSinkDiamond2 : virtual public BaseWithPureVirtual {
|
||||
public:
|
||||
void f(const char* p) override {}
|
||||
};
|
||||
|
||||
class DerivesMultiple : public DerivedCallsSinkDiamond1, public DerivedDoesNotCallSinkDiamond2 {
|
||||
void f(const char* p) override { // $ ir-path=53:37 ir-path=115:11
|
||||
DerivedCallsSinkDiamond1::f(p); // $ ir-path
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class CRTP {
|
||||
public:
|
||||
void f(const char* p) { // $ ir-path
|
||||
static_cast<T*>(this)->g(p); // $ ir-path
|
||||
}
|
||||
};
|
||||
|
||||
class CRTPCallsSink : public CRTP<CRTPCallsSink> {
|
||||
public:
|
||||
void g(const char* p) { // $ ir-path
|
||||
sink(p); // $ ast ir-sink
|
||||
}
|
||||
};
|
||||
|
||||
class Derived1 : public BaseWithPureVirtual {};
|
||||
|
||||
class Derived2 : public Derived1 {
|
||||
public:
|
||||
void f(const char* p) override {}
|
||||
};
|
||||
|
||||
class Derived3 : public Derived2 {
|
||||
public:
|
||||
void f(const char* p) override { // $ ir-path=124:19 ir-path=126:43 ir-path=128:44
|
||||
sink(p); // $ ast=124:19 ast=126:43 ast=128:44 ir-sink
|
||||
}
|
||||
};
|
||||
|
||||
class CRTPDoesNotCallSink : public CRTP<CRTPDoesNotCallSink> {
|
||||
public:
|
||||
void g(const char* p) {}
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
sink(argv[0]); // $ ast,ir-path,ir-sink
|
||||
|
||||
sink(reinterpret_cast<int>(argv)); // $ ast,ir-sink
|
||||
|
||||
calls_sink_with_argv(argv[1]); // $ ast,ir-path
|
||||
|
||||
char*** p = &argv; // $ ast,ir-path
|
||||
|
||||
sink(*p[0]); // $ ast ir-sink=96:26 ir-sink=98:18 ir-sink=98:17
|
||||
|
||||
calls_sink_with_argv(*p[i]); // $ ir-path=96:26 ir-path=98:18 ir-path=98:17 MISSING:ast
|
||||
|
||||
sink(*(argv + 1)); // $ ast ir-path ir-sink
|
||||
|
||||
BaseWithPureVirtual* b = new DerivedCallsSink;
|
||||
|
||||
b->f(argv[1]); // $ ast,ir-path
|
||||
|
||||
b = new DerivedDoesNotCallSink;
|
||||
b->f(argv[0]); // $ SPURIOUS: ast
|
||||
|
||||
BaseWithPureVirtual* b2 = new DerivesMultiple;
|
||||
|
||||
b2->f(argv[i]); // $ ast,ir-path
|
||||
|
||||
CRTP<CRTPDoesNotCallSink> crtp_not_call_sink;
|
||||
crtp_not_call_sink.f(argv[0]); // clean
|
||||
|
||||
CRTP<CRTPCallsSink> crtp_calls_sink;
|
||||
crtp_calls_sink.f(argv[0]); // $ ast,ir-path
|
||||
|
||||
Derived1* calls_sink = new Derived3;
|
||||
calls_sink->f(argv[1]); // $ ast,ir-path
|
||||
|
||||
static_cast<Derived2*>(calls_sink)->f(argv[1]); // $ ast,ir-path
|
||||
|
||||
dynamic_cast<Derived2*>(calls_sink)->f(argv[1]); // $ ast,ir-path
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
#include "../shared.h"
|
||||
|
||||
int main() {
|
||||
sink(_strdup(getenv("VAR"))); // $ ir MISSING: ast
|
||||
sink(strdup(getenv("VAR"))); // $ ast,ir
|
||||
sink(unmodeled_function(getenv("VAR"))); // clean by assumption
|
||||
|
||||
char untainted_buf[100] = "";
|
||||
char buf[100] = "VAR = ";
|
||||
sink(strcat(buf, getenv("VAR"))); // $ ast,ir
|
||||
|
||||
sink(buf); // $ ast,ir
|
||||
sink(untainted_buf); // the two buffers would be conflated if we added flow through all partial chi inputs
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef unsigned int inet_addr_retval;
|
||||
inet_addr_retval inet_addr(const char *dotted_address);
|
||||
void sink(inet_addr_retval);
|
||||
|
||||
void test_indirect_arg_to_model() {
|
||||
// This test is non-sensical but carefully arranged so we get data flow into
|
||||
// inet_addr not through the function argument but through its associated
|
||||
// read side effect.
|
||||
void *env_pointer = getenv("VAR"); // env_pointer is tainted, not its data.
|
||||
inet_addr_retval a = inet_addr((const char *)&env_pointer);
|
||||
sink(a); // $ ast,ir
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template< class T >
|
||||
T&& move( T&& t ) noexcept;
|
||||
}
|
||||
|
||||
void test_std_move() {
|
||||
sink(std::move(getenv("VAR"))); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void flow_to_outparam(char ** ret, char *arg) {
|
||||
*ret = arg;
|
||||
}
|
||||
|
||||
void test_outparams() {
|
||||
char *p2 = nullptr;
|
||||
flow_to_outparam(&p2, getenv("VAR"));
|
||||
sink(p2); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
struct XY {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
void taint_y(XY *xyp) {
|
||||
int tainted = getenv("VAR")[0];
|
||||
xyp->y = tainted;
|
||||
}
|
||||
|
||||
void test_conflated_fields3() {
|
||||
XY xy;
|
||||
xy.x = 0;
|
||||
taint_y(&xy);
|
||||
sink(xy.x); // not tainted
|
||||
}
|
||||
|
||||
struct Point {
|
||||
int x;
|
||||
int y;
|
||||
|
||||
void callSink() {
|
||||
sink(this->x); // $ ir MISSING: ast
|
||||
sink(this->y); // not tainted
|
||||
}
|
||||
};
|
||||
|
||||
void test_conflated_fields1() {
|
||||
Point p;
|
||||
p.x = getenv("VAR")[0];
|
||||
sink(p.x); // $ ir MISSING: ast
|
||||
sink(p.y); // not tainted
|
||||
p.callSink();
|
||||
}
|
||||
|
||||
void taint_x(Point *pp) {
|
||||
pp->x = getenv("VAR")[0];
|
||||
}
|
||||
|
||||
void y_to_sink(Point *pp) {
|
||||
sink(pp->y); // not tainted
|
||||
}
|
||||
|
||||
void test_conflated_fields2() {
|
||||
Point p;
|
||||
taint_x(&p);
|
||||
y_to_sink(&p);
|
||||
}
|
||||
|
||||
void sink(Point*);
|
||||
void sink(Point);
|
||||
|
||||
void test_field_to_obj_taint_object(Point p) {
|
||||
p.x = getenv("VAR")[0];
|
||||
sink(p); // not tainted
|
||||
sink(p.x); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test_field_to_obj_taint_object_addrof(Point p) {
|
||||
taint_x(&p);
|
||||
sink(p); // not tainted
|
||||
sink(&p); // not tainted
|
||||
sink(p.x); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test_field_to_obj_taint_pointer(Point* pp) {
|
||||
pp->x = getenv("VAR")[0];
|
||||
sink(pp);// not tainted
|
||||
sink(*pp); // not tainted
|
||||
}
|
||||
|
||||
void call_sink_on_object(Point* pp) {
|
||||
sink(pp);// not tainted
|
||||
sink(*pp);// not tainted
|
||||
}
|
||||
|
||||
void test_field_to_obj_taint_call_sink(Point* pp) {
|
||||
pp->x = getenv("VAR")[0];
|
||||
call_sink_on_object(pp);
|
||||
}
|
||||
|
||||
void test_field_to_obj_taint_through_setter(Point* pp) {
|
||||
taint_x(pp);
|
||||
sink(pp);// not tainted
|
||||
sink(*pp); // not tainted
|
||||
}
|
||||
|
||||
Point* getPoint();
|
||||
|
||||
void test_field_to_obj_local_variable() {
|
||||
Point* pp = getPoint();
|
||||
pp->x = getenv("VAR")[0];
|
||||
sink(pp); // not tainted
|
||||
sink(*pp); // not tainted
|
||||
}
|
||||
|
||||
void test_field_to_obj_taint_array(Point* pp, int i) {
|
||||
pp[0].x = getenv("VAR")[0];
|
||||
sink(pp[i]); // not tainted
|
||||
sink(pp);// not tainted
|
||||
sink(*pp); // not tainted
|
||||
}
|
||||
|
||||
void test_field_to_obj_test_pointer_arith(Point* pp) {
|
||||
(pp + sizeof(*pp))->x = getenv("VAR")[0];
|
||||
sink(pp);// not tainted
|
||||
sink(pp + sizeof(*pp));// not tainted
|
||||
}
|
||||
|
||||
void sink(char **);
|
||||
|
||||
void test_pointers1()
|
||||
{
|
||||
char buffer[1024];
|
||||
char *s = getenv("VAR");
|
||||
char *ptr1, **ptr2;
|
||||
char *ptr3, **ptr4;
|
||||
|
||||
ptr1 = buffer;
|
||||
ptr2 = &ptr1;
|
||||
memcpy(buffer, s, 1024);
|
||||
ptr3 = buffer;
|
||||
ptr4 = &ptr3;
|
||||
|
||||
sink(buffer); // $ ast,ir
|
||||
sink(ptr1); // $ ast MISSING: ir
|
||||
sink(ptr2); // $ SPURIOUS: ast
|
||||
sink(*ptr2); // $ ast MISSING: ir
|
||||
sink(ptr3); // $ ast,ir
|
||||
sink(ptr4); // $ SPURIOUS: ast,ir
|
||||
sink(*ptr4); // $ ast,ir
|
||||
}
|
||||
|
||||
void test_pointers2()
|
||||
{
|
||||
char buffer[1024];
|
||||
char *s = getenv("VAR");
|
||||
char *ptr1, **ptr2;
|
||||
char *ptr3, **ptr4;
|
||||
|
||||
ptr1 = buffer;
|
||||
ptr2 = &ptr1;
|
||||
memcpy(*ptr2, s, 1024);
|
||||
ptr3 = buffer;
|
||||
ptr4 = &ptr3;
|
||||
|
||||
sink(buffer); // $ MISSING: ast,ir
|
||||
sink(ptr1); // $ ast MISSING: ir
|
||||
sink(ptr2); // $ SPURIOUS: ast,ir
|
||||
sink(*ptr2); // $ ast,ir
|
||||
sink(ptr3); // $ MISSING: ast,ir
|
||||
sink(ptr4); // clean
|
||||
sink(*ptr4); // $ MISSING: ast,ir
|
||||
}
|
||||
|
||||
// --- recv ---
|
||||
|
||||
int recv(int s, char* buf, int len, int flags);
|
||||
|
||||
void test_recv() {
|
||||
char buffer[1024];
|
||||
recv(0, buffer, sizeof(buffer), 0);
|
||||
sink(buffer); // $ ast,ir
|
||||
sink(*buffer); // $ ast,ir
|
||||
}
|
||||
|
||||
// --- send and related functions ---
|
||||
|
||||
struct iovec {
|
||||
void *iov_base;
|
||||
unsigned iov_len;
|
||||
};
|
||||
|
||||
int readv(int, const struct iovec*, int);
|
||||
|
||||
void sink(const iovec* iovs);
|
||||
void sink(iovec);
|
||||
|
||||
void test_readv_and_writev(iovec* iovs) {
|
||||
readv(0, iovs, 16);
|
||||
sink(iovs); // $ast,ir
|
||||
sink(iovs[0]); // $ast,ir
|
||||
sink(*iovs); // $ast,ir
|
||||
|
||||
char* p = (char*)iovs[1].iov_base;
|
||||
sink(p); // $ MISSING: ast,ir
|
||||
sink(*p); // $ MISSING: ast,ir
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
|
||||
#include "../shared.h"
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class charT> struct char_traits;
|
||||
|
||||
typedef size_t streamsize;
|
||||
|
||||
template <class T> class allocator {
|
||||
public:
|
||||
allocator() throw();
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
|
||||
class basic_string {
|
||||
public:
|
||||
explicit basic_string(const Allocator& a = Allocator());
|
||||
basic_string(const charT* s, const Allocator& a = Allocator());
|
||||
|
||||
const charT* c_str() const;
|
||||
};
|
||||
|
||||
typedef basic_string<char> string;
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
basic_istream<charT,traits>& operator>>(int& n);
|
||||
};
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
typedef charT char_type;
|
||||
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
|
||||
|
||||
basic_ostream<charT, traits>& operator<<(int n);
|
||||
};
|
||||
|
||||
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
|
||||
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
|
||||
|
||||
template<class charT, class traits = char_traits<charT>>
|
||||
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
|
||||
public:
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
|
||||
class basic_stringstream : public basic_iostream<charT, traits> {
|
||||
public:
|
||||
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
|
||||
|
||||
basic_string<charT, traits, Allocator> str() const;
|
||||
};
|
||||
|
||||
using stringstream = basic_stringstream<char>;
|
||||
}
|
||||
|
||||
char *source() { return getenv("USERDATA"); }
|
||||
void sink(const std::string &s) {};
|
||||
void sink(const std::stringstream &s) {};
|
||||
|
||||
void test_string()
|
||||
{
|
||||
char *a = source();
|
||||
std::string b("123");
|
||||
std::string c(source());
|
||||
|
||||
sink(a); // $ ast,ir
|
||||
sink(b); // clean
|
||||
sink(c); // $ ir MISSING: ast
|
||||
sink(b.c_str()); // clean
|
||||
sink(c.c_str()); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test_stringstream()
|
||||
{
|
||||
std::stringstream ss1, ss2, ss3, ss4, ss5;
|
||||
std::string t(source());
|
||||
|
||||
ss1 << "1234";
|
||||
ss2 << source();
|
||||
ss3 << "123" << source();
|
||||
ss4 << source() << "456";
|
||||
ss5 << t;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // $ ir MISSING: ast
|
||||
sink(ss3); // $ ir MISSING: ast
|
||||
sink(ss4); // $ ir MISSING: ast
|
||||
sink(ss5); // $ ir MISSING: ast
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // $ ir MISSING: ast
|
||||
sink(ss3.str()); // $ ir MISSING: ast
|
||||
sink(ss4.str()); // $ ir MISSING: ast
|
||||
sink(ss5.str()); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test_stringstream_int(int source)
|
||||
{
|
||||
std::stringstream ss1, ss2;
|
||||
|
||||
ss1 << 1234;
|
||||
ss2 << source;
|
||||
|
||||
sink(ss1); // clean
|
||||
sink(ss2); // $ MISSING: ast,ir
|
||||
sink(ss1.str()); // clean
|
||||
sink(ss2.str()); // $ MISSING: ast,ir
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
char *user_input() {
|
||||
return source();
|
||||
}
|
||||
|
||||
void sink(const char *filename, const char *mode);
|
||||
|
||||
void test_strings2()
|
||||
{
|
||||
string path1 = user_input();
|
||||
sink(path1.c_str(), "r"); // $ ir MISSING: ast
|
||||
|
||||
string path2;
|
||||
path2 = user_input();
|
||||
sink(path2.c_str(), "r"); // $ ir MISSING: ast
|
||||
|
||||
string path3(user_input());
|
||||
sink(path3.c_str(), "r"); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test_string3()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
sink(cs); // $ ast,ir
|
||||
sink(ss); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test_string4()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
// convert back std::string -> char *
|
||||
cs = ss.c_str();
|
||||
|
||||
sink(cs); // $ ast,ir
|
||||
sink(ss); // $ ir MISSING: ast
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted.ql:10,8-47)
|
||||
WARNING: Predicate tainted has been deprecated and may be removed in future (tainted.ql:21,3-28)
|
||||
testFailures
|
||||
failures
|
||||
@@ -1,80 +0,0 @@
|
||||
/**
|
||||
* This test provides the usual facilities to annotate taint flow when reaching a sink.
|
||||
* This is different when compared to the tests in `../annotate_path_to_sink`, where all elements on a taint path to a sink
|
||||
* are annotated.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.TaintTrackingImpl as AstTaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IRDefaultTaintTracking
|
||||
import IRDefaultTaintTracking::TaintedWithPath as TaintedWithPath
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
predicate argToSinkCall(Element sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
predicate astTaint(Expr source, Element sink) {
|
||||
AstTaintTracking::tainted(source, sink) and argToSinkCall(sink)
|
||||
}
|
||||
|
||||
class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
|
||||
override predicate isSink(Element e) { argToSinkCall(e) }
|
||||
}
|
||||
|
||||
predicate irTaint(Expr source, Element sink) {
|
||||
TaintedWithPath::taintedWithPath(source, sink, _, _)
|
||||
}
|
||||
|
||||
module IRDefaultTaintTrackingTest implements TestSig {
|
||||
string getARelevantTag() { result = "ir" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Expr source, Element tainted, int n |
|
||||
tag = "ir" and
|
||||
irTaint(source, tainted) and
|
||||
n = strictcount(Expr otherSource | irTaint(otherSource, tainted)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = tainted.getLocation() and
|
||||
element = tainted.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module AstTaintTrackingTest implements TestSig {
|
||||
string getARelevantTag() { result = "ast" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Expr source, Element tainted, int n |
|
||||
tag = "ast" and
|
||||
astTaint(source, tainted) and
|
||||
n = strictcount(Expr otherSource | astTaint(otherSource, tainted)) and
|
||||
(
|
||||
n = 1 and value = ""
|
||||
or
|
||||
// If there is more than one source for this sink
|
||||
// we specify the source location explicitly.
|
||||
n > 1 and
|
||||
value =
|
||||
source.getLocation().getStartLine().toString() + ":" +
|
||||
source.getLocation().getStartColumn()
|
||||
) and
|
||||
location = tainted.getLocation() and
|
||||
element = tainted.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<MergeTests<IRDefaultTaintTrackingTest, AstTaintTrackingTest>>
|
||||
@@ -1,4 +0,0 @@
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (global.ql:8,3-47)
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (global.ql:12,3-53)
|
||||
failures
|
||||
testFailures
|
||||
@@ -1,41 +0,0 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTrackingImpl as AstTaintTracking
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IRDefaultTaintTracking
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
predicate astTaint(Expr source, Element sink, string globalVar) {
|
||||
AstTaintTracking::taintedIncludingGlobalVars(source, sink, globalVar) and globalVar != ""
|
||||
}
|
||||
|
||||
predicate irTaint(Expr source, Element sink, string globalVar) {
|
||||
IRDefaultTaintTracking::taintedIncludingGlobalVars(source, sink, globalVar) and globalVar != ""
|
||||
}
|
||||
|
||||
module IRGlobalDefaultTaintTrackingTest implements TestSig {
|
||||
string getARelevantTag() { result = "ir" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Element tainted |
|
||||
tag = "ir" and
|
||||
irTaint(_, tainted, value) and
|
||||
location = tainted.getLocation() and
|
||||
element = tainted.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module AstGlobalDefaultTaintTrackingTest implements TestSig {
|
||||
string getARelevantTag() { result = "ast" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Element tainted |
|
||||
tag = "ast" and
|
||||
astTaint(_, tainted, value) and
|
||||
location = tainted.getLocation() and
|
||||
element = tainted.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<MergeTests<IRGlobalDefaultTaintTrackingTest, AstGlobalDefaultTaintTrackingTest>>
|
||||
@@ -1,24 +0,0 @@
|
||||
char *getenv(const char *name);
|
||||
void sink(const char *sinkparam); // $ ast,ir=global1 ast,ir=global2
|
||||
|
||||
void throughLocal() {
|
||||
char * local = getenv("VAR");
|
||||
sink(local);
|
||||
}
|
||||
|
||||
char * global1 = 0;
|
||||
|
||||
void readWriteGlobal1() {
|
||||
sink(global1); // $ ast,ir=global1
|
||||
global1 = getenv("VAR");
|
||||
}
|
||||
|
||||
static char * global2 = 0;
|
||||
|
||||
void readGlobal2() {
|
||||
sink(global2); // $ ast,ir=global2
|
||||
}
|
||||
|
||||
void writeGlobal2() {
|
||||
global2 = getenv("VAR");
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// Common declarations in this test dir should go in this file. Otherwise, some
|
||||
// declarations will have multiple locations, which leads to confusing test
|
||||
// output.
|
||||
|
||||
void sink(const char *sinkparam);
|
||||
void sink(int sinkparam);
|
||||
|
||||
int atoi(const char *nptr);
|
||||
char *getenv(const char *name);
|
||||
char *strcat(char * s1, const char * s2);
|
||||
|
||||
char *strdup(const char *string);
|
||||
char *_strdup(const char *string);
|
||||
char *unmodeled_function(const char *const_string);
|
||||
|
||||
typedef unsigned long size_t;
|
||||
void *memcpy(void *s1, const void *s2, size_t n);
|
||||
@@ -1,91 +0,0 @@
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted.ql:5,3-29)
|
||||
| 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 | envStrGlobal |
|
||||
| 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:45:13:45:24 | envStrGlobal | 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:50:15:50:24 | envStr_ptr | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:15:50:24 | envStr_ptr | envStrGlobal |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:28:50:40 | & ... | envStrGlobal |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:29:50:40 | envStrGlobal | envStrGlobal |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:2:52:12 | * ... | |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:3:52:12 | envStr_ptr | |
|
||||
| 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 | ! ... | envStrGlobal |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp | envStrGlobal |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... | envStrGlobal |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal | 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:20:11:21 | s1 | |
|
||||
| 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:67:7:67:13 | copying | |
|
||||
| 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:69:10:69:13 | copy | |
|
||||
| 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:12:70:15 | copy | |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:18:70:25 | userName | |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:71:12:71:15 | copy | |
|
||||
| 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 *)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:8:24:8:25 | s1 | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:20:11:21 | s1 | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:17:83:24 | userName | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:33 | call to getenv | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:46 | (const char *)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:85:8:85:11 | copy | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:2:86:7 | call to strcpy | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:9:86:12 | copy | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:15:86:22 | userName | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:6:88:27 | ! ... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:12 | call to strcmp | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
|
||||
| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | |
|
||||
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:2:100:8 | pointer | |
|
||||
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets | |
|
||||
| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | |
|
||||
| test.cpp:100:17:100:22 | buffer | test.cpp:97:7:97:12 | buffer | |
|
||||
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | buffer | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:8:24:8:25 | s1 | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:11:20:11:21 | s1 | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:17:106:24 | userName | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:28:106:33 | call to getenv | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:28:106:46 | (const char *)... | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:108:8:108:11 | copy | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:2:109:7 | call to strcpy | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:9:109:12 | copy | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:15:109:22 | userName | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:6:111:27 | ! ... | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:7:111:12 | call to strcmp | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:7:111:27 | (bool)... | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | (const char *)... | |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | copy | |
|
||||
@@ -1,7 +0,0 @@
|
||||
import semmle.code.cpp.security.TaintTrackingImpl
|
||||
|
||||
from Expr source, Element tainted, string globalVar
|
||||
where
|
||||
taintedIncludingGlobalVars(source, tainted, globalVar) and
|
||||
not tainted.getLocation().getFile().getExtension() = "h"
|
||||
select source, tainted, globalVar
|
||||
@@ -1,51 +0,0 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:5,35-54)
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:12,7-26)
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_diff.ql:16,3-22)
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted_diff.ql:11,3-34)
|
||||
WARNING: Predicate taintedIncludingGlobalVars has been deprecated and may be removed in future (tainted_diff.ql:17,7-38)
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
|
||||
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | AST only |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
|
||||
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:14:38:19 | envStr | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:45:13:45:24 | envStrGlobal | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:14:49:19 | envStr | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:15:50:24 | envStr_ptr | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:28:50:40 | & ... | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:29:50:40 | envStrGlobal | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:2:52:12 | * ... | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:3:52:12 | envStr_ptr | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:6:54:35 | ! ... | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... | AST only |
|
||||
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal | AST only |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:10:27:10:27 | s | AST only |
|
||||
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:18:60:25 | userName | 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:11:36:11:37 | s2 | 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:68:17:68:24 | userName | 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:5:70:10 | call to strcpy | 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 | array to pointer conversion | IR only |
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:15:22:15:25 | nptr | AST only |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:36:11:37 | s2 | AST only |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:17:83:24 | userName | AST only |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:85:8:85:11 | copy | AST only |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:2:86:7 | call to strcpy | AST only |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:9:86:12 | copy | AST only |
|
||||
| test.cpp:100:12:100:15 | call to gets | test.cpp:98:8:98:14 | pointer | AST only |
|
||||
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:2:100:8 | pointer | AST only |
|
||||
| test.cpp:100:17:100:22 | buffer | test.cpp:93:18:93:18 | s | AST only |
|
||||
| test.cpp:100:17:100:22 | buffer | test.cpp:97:7:97:12 | buffer | AST only |
|
||||
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion | IR only |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:8:24:8:25 | s1 | AST only |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:11:36:11:37 | s2 | AST only |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:17:106:24 | userName | AST only |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:108:8:108:11 | copy | AST only |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:2:109:7 | call to strcpy | AST only |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:9:109:12 | copy | AST only |
|
||||
@@ -1,20 +0,0 @@
|
||||
import semmle.code.cpp.security.TaintTrackingImpl as AST
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IR
|
||||
import cpp
|
||||
|
||||
class SourceConfiguration extends IR::TaintedWithPath::TaintTrackingConfiguration {
|
||||
override predicate isSink(Element e) { any() }
|
||||
}
|
||||
|
||||
from Expr source, Element tainted, string side
|
||||
where
|
||||
AST::taintedIncludingGlobalVars(source, tainted, _) and
|
||||
not IR::TaintedWithPath::taintedWithPath(source, tainted, _, _) and
|
||||
not tainted.getLocation().getFile().getExtension() = "h" and
|
||||
side = "AST only"
|
||||
or
|
||||
IR::TaintedWithPath::taintedWithPath(source, tainted, _, _) and
|
||||
not AST::taintedIncludingGlobalVars(source, tainted, _) and
|
||||
not tainted.getLocation().getFile().getExtension() = "h" and
|
||||
side = "IR only"
|
||||
select source, tainted, side
|
||||
@@ -1,48 +0,0 @@
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_ir.ql:3,35-50)
|
||||
WARNING: Module TaintedWithPath has been deprecated and may be removed in future (tainted_ir.ql:9,3-18)
|
||||
| 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: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: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: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: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:18:70:25 | userName |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:71:12:71:15 | array to pointer conversion |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:71:12:71:15 | copy |
|
||||
| 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 *)... |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:33 | call to getenv |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:46 | (const char *)... |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:15:86:22 | userName |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:6:88:27 | ! ... |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:12 | call to strcmp |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy |
|
||||
| test.cpp:100:12:100:15 | call to gets | test.cpp:100:12:100:15 | call to gets |
|
||||
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | array to pointer conversion |
|
||||
| test.cpp:100:17:100:22 | buffer | test.cpp:100:17:100:22 | buffer |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:28:106:33 | call to getenv |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:106:28:106:46 | (const char *)... |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:109:15:109:22 | userName |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:6:111:27 | ! ... |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:7:111:12 | call to strcmp |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:7:111:27 | (bool)... |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | (const char *)... |
|
||||
| test.cpp:106:28:106:33 | call to getenv | test.cpp:111:14:111:17 | copy |
|
||||
@@ -1,11 +0,0 @@
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
|
||||
|
||||
class SourceConfiguration extends TaintedWithPath::TaintTrackingConfiguration {
|
||||
override predicate isSink(Element e) { any() }
|
||||
}
|
||||
|
||||
from Expr source, Element tainted
|
||||
where
|
||||
TaintedWithPath::taintedWithPath(source, tainted, _, _) and
|
||||
not tainted.getLocation().getFile().getExtension() = "h"
|
||||
select source, tainted
|
||||
@@ -1,114 +0,0 @@
|
||||
// Test for the general-purpose taint-tracking
|
||||
// mechanism that is used by several of the security queries.
|
||||
|
||||
///// Library functions //////
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
int strcmp(const char *s1, const char *s2);
|
||||
char *getenv(const char *name);
|
||||
size_t strlen(const char *s);
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
|
||||
void *malloc(size_t size);
|
||||
|
||||
int atoi(const char *nptr);
|
||||
|
||||
//// Test code /////
|
||||
|
||||
bool isAdmin = false;
|
||||
|
||||
void test1()
|
||||
{
|
||||
const char *envStr = getenv("USERINFO");
|
||||
|
||||
if (!strcmp(envStr, "admin")) {
|
||||
isAdmin = true;
|
||||
}
|
||||
|
||||
if (!strcmp(envStr, "none")) {
|
||||
isAdmin = false;
|
||||
}
|
||||
}
|
||||
|
||||
extern const char *specialUser;
|
||||
|
||||
void test2()
|
||||
{
|
||||
const char *envStr = getenv("USERINFO");
|
||||
|
||||
if (!strcmp(envStr, specialUser)) {
|
||||
isAdmin = true;
|
||||
}
|
||||
}
|
||||
|
||||
const char *envStrGlobal;
|
||||
|
||||
void test3()
|
||||
{
|
||||
const char *envStr = getenv("USERINFO");
|
||||
const char **envStr_ptr = &envStrGlobal;
|
||||
|
||||
*envStr_ptr = envStr;
|
||||
|
||||
if (!strcmp(envStrGlobal, "admin")) {
|
||||
isAdmin = true;
|
||||
}
|
||||
}
|
||||
|
||||
void bugWithBinop() {
|
||||
const char *userName = getenv("USER_NAME");
|
||||
|
||||
// The following is tainted, but should not cause
|
||||
// the whole program to be considered tainted.
|
||||
int bytes = strlen(userName) + 1;
|
||||
}
|
||||
|
||||
char* copying() {
|
||||
const char *userName = getenv("USER_NAME");
|
||||
char copy[1024];
|
||||
strcpy(copy, userName);
|
||||
return copy; // copy should be tainted
|
||||
}
|
||||
|
||||
void guard() {
|
||||
int len = atoi(getenv("FOOBAZ_BRANCHING"));
|
||||
if (len > 1000) return;
|
||||
char **node = (char **) malloc(len * sizeof(char *));
|
||||
}
|
||||
|
||||
const char *alias_global;
|
||||
|
||||
void mallocBuffer() {
|
||||
const char *userName = getenv("USER_NAME");
|
||||
char *alias = (char*)malloc(4096);
|
||||
char *copy = (char*)malloc(4096);
|
||||
strcpy(copy, userName);
|
||||
alias_global = alias; // to force a Chi node on all aliased memory
|
||||
if (!strcmp(copy, "admin")) { // copy should be tainted
|
||||
isAdmin = true;
|
||||
}
|
||||
}
|
||||
|
||||
char *gets(char *s);
|
||||
|
||||
void test_gets()
|
||||
{
|
||||
char buffer[1024];
|
||||
char *pointer;
|
||||
|
||||
pointer = gets(buffer);
|
||||
}
|
||||
|
||||
const char *alias_global_new;
|
||||
|
||||
void newBuffer() {
|
||||
const char *userName = getenv("USER_NAME");
|
||||
char *alias = new char[4096];
|
||||
char *copy = new char[4096];
|
||||
strcpy(copy, userName);
|
||||
alias_global_new = alias; // to force a Chi node on all aliased memory
|
||||
if (!strcmp(copy, "admin")) { // copy should be tainted
|
||||
isAdmin = true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user