Merge remote-tracking branch 'origin/main' into alternative-instruction-operand-flow

This commit is contained in:
Mathias Vorreiter Pedersen
2020-08-23 18:46:07 +02:00
277 changed files with 20078 additions and 3191 deletions

View File

@@ -18,6 +18,9 @@ Expr getTest() {
or
// boost tests; http://www.boost.org/
result.(FunctionCall).getTarget().hasQualifiedName("boost::unit_test", "make_test_case")
or
// googletest tests; https://github.com/google/googletest/
result.(FunctionCall).getTarget().hasQualifiedName("testing::internal", "MakeAndRegisterTestInfo")
}
from File f, int n

View File

@@ -1,5 +1,13 @@
/**
* Provides classes for identifying and reasoning about Microsoft source code
* annotation language (SAL) macros.
*/
import cpp
/**
* A SAL macro defined in `sal.h` or a similar header file.
*/
class SALMacro extends Macro {
SALMacro() {
exists(string filename | filename = this.getFile().getBaseName() |
@@ -20,27 +28,34 @@ class SALMacro extends Macro {
}
pragma[noinline]
predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
private predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
/**
* An invocation of a SAL macro (excluding invocations inside other macros).
*/
class SALAnnotation extends MacroInvocation {
SALAnnotation() {
this.getMacro() instanceof SALMacro and
isTopLevelMacroAccess(this)
}
/** Returns the `Declaration` annotated by `this`. */
/** Gets the `Declaration` annotated by `this`. */
Declaration getDeclaration() {
annotatesAt(this, result.getADeclarationEntry(), _, _) and
not result instanceof Type // exclude typedefs
}
/** Returns the `DeclarationEntry` annotated by `this`. */
/** Gets the `DeclarationEntry` annotated by `this`. */
DeclarationEntry getDeclarationEntry() {
annotatesAt(this, result, _, _) and
not result instanceof TypeDeclarationEntry // exclude typedefs
}
}
/**
* A SAL macro indicating that the return value of a function should always be
* checked.
*/
class SALCheckReturn extends SALAnnotation {
SALCheckReturn() {
exists(SALMacro m | m = this.getMacro() |
@@ -50,6 +65,10 @@ class SALCheckReturn extends SALAnnotation {
}
}
/**
* A SAL macro indicating that a pointer variable or return value should not be
* `NULL`.
*/
class SALNotNull extends SALAnnotation {
SALNotNull() {
exists(SALMacro m | m = this.getMacro() |
@@ -69,6 +88,9 @@ class SALNotNull extends SALAnnotation {
}
}
/**
* A SAL macro indicating that a value may be `NULL`.
*/
class SALMaybeNull extends SALAnnotation {
SALMaybeNull() {
exists(SALMacro m | m = this.getMacro() |
@@ -79,13 +101,29 @@ class SALMaybeNull extends SALAnnotation {
}
}
/**
* A parameter annotated by one or more SAL annotations.
*/
class SALParameter extends Parameter {
/** One of this parameter's annotations. */
SALAnnotation a;
SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
}
///////////////////////////////////////////////////////////////////////////////
// Implementation details
/**
* Holds if `a` annotates the declaration entry `d` and
* its start position is the `idx`th position in `file` that holds a SAL element.
*/
predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
private predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx)
}
@@ -109,22 +147,6 @@ private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File
)
}
/**
* A parameter annotated by one or more SAL annotations.
*/
class SALParameter extends Parameter {
/** One of this parameter's annotations. */
SALAnnotation a;
SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") }
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
}
/**
* A SAL element, that is, a SAL annotation or a declaration entry
* that may have SAL annotations.

View File

@@ -4,7 +4,7 @@
* user can result in integer overflow.
* @kind path-problem
* @problem.severity error
* @precision high
* @precision medium
* @id cpp/uncontrolled-allocation-size
* @tags reliability
* security

View File

@@ -0,0 +1,25 @@
///// Library routines /////
int scanf(const char *format, ...);
int sscanf(const char *str, const char *format, ...);
int fscanf(const char *str, const char *format, ...);
///// EXAMPLES /////
int main(int argc, char **argv)
{
// BAD, do not use scanf without specifying a length first
char buf1[10];
scanf("%s", buf1);
// GOOD, length is specified. The length should be one less than the size of the buffer, since the last character is the NULL terminator.
char buf2[10];
sscanf(buf2, "%9s");
// BAD, do not use scanf without specifying a length first
char file[10];
fscanf(file, "%s", buf2);
return 0;
}

View File

@@ -0,0 +1,25 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>It is bad practice to use any of the <code>scanf</code> functions without including a specified length within the format parameter, as it will be vulnerable to buffer overflows.</p>
</overview>
<recommendation>
<p>Specify a length within the format string parameter, and make this length one less than the size of the buffer, since the last character should be reserved for the NULL terminator.</p>
</recommendation>
<example>
<p>The following example demonstrates safe and unsafe uses of <code>scanf</code> type functions.</p>
<sample src="MemoryUnsafeFunctionScan.cpp" />
</example>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,19 @@
/**
* @name Scanf function without a specified length
* @description Use of one of the scanf functions without a specified length.
* @kind problem
* @problem.severity warning
* @id cpp/memory-unsafe-function-scan
* @tags reliability
* security
* external/cwe/cwe-120
*/
import cpp
import semmle.code.cpp.commons.Scanf
from FunctionCall call, ScanfFunction sff
where
call.getTarget() = sff and
call.getArgument(sff.getFormatParameterIndex()).getValue().regexpMatch(".*%l?s.*")
select call, "Dangerous use of one of the scanf functions"

View File

@@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Private data that is stored in a file or buffer unencrypted is accessible to an attacker who gains access to the
storage.</p>
</overview>
<recommendation>
<p>Ensure that private data is always encrypted before being stored.
It may be wise to encrypt information before it is put into a buffer that may be readable in memory.</p>
<p>In general, decrypt private data only at the point where it is necessary for it to be used in
cleartext.</p>
</recommendation>
<references>
<li><a href="https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/Top_10-2017_A3-Sensitive_Data_Exposure">OWASP Sensitive_Data_Exposure</a></li>
<li>M. Dowd, J. McDonald and J. Schuhm, <i>The Art of Software Security Assessment</i>, 1st Edition, Chapter 2 - 'Common Vulnerabilities of Encryption', p. 43. Addison Wesley, 2006.</li>
<li>M. Howard and D. LeBlanc, <i>Writing Secure Code</i>, 2nd Edition, Chapter 9 - 'Protecting Secret Data', p. 299. Microsoft, 2002.</li>
<!-- LocalWords: CWE
-->
</references>
</qhelp>

View File

@@ -0,0 +1,21 @@
/**
* @name Exposure of private information
* @description If private information is written to an external location, it may be accessible by
* unauthorized persons.
* @kind path-problem
* @problem.severity error
* @id cpp/private-cleartext-write
* @tags security
* external/cwe/cwe-359
*/
import cpp
import experimental.semmle.code.cpp.security.PrivateCleartextWrite
import experimental.semmle.code.cpp.security.PrivateCleartextWrite::PrivateCleartextWrite
import DataFlow::PathGraph
from WriteConfig b, DataFlow::PathNode source, DataFlow::PathNode sink
where b.hasFlowPath(source, sink)
select sink.getNode(),
"This write into the external location '" + sink + "' may contain unencrypted data from $@",
source, "this source."

View File

@@ -0,0 +1,78 @@
/**
* EXPERIMENTAL: The API of this module may change without notice.
*
* Provides a class for modeling `Expr`s with a restricted range.
*/
import cpp
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
/**
* EXPERIMENTAL: The API of this class may change without notice.
*
* An expression for which a range can be deduced. Extend this class to add
* functionality to the range analysis library.
*/
abstract class SimpleRangeAnalysisExpr extends Expr {
/**
* Gets the lower bound of the expression.
*
* Implementations of this predicate should use
* `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for
* recursive calls to get the bounds of their children.
*/
abstract float getLowerBounds();
/**
* Gets the upper bound of the expression.
*
* Implementations of this predicate should use
* `getFullyConvertedLowerBounds` and `getFullyConvertedUpperBounds` for
* recursive calls to get the bounds of their children.
*/
abstract float getUpperBounds();
/**
* Holds if the range this expression depends on the definition `srcDef` for
* StackVariable `srcVar`.
*
* Because this predicate cannot be recursive, most implementations should
* override `dependsOnChild` instead.
*/
predicate dependsOnDef(RangeSsaDefinition srcDef, StackVariable srcVar) { none() }
/**
* Holds if this expression depends on the range of its unconverted
* subexpression `child`. This information is used to inform the range
* analysis about cyclic dependencies. Without this information, range
* analysis might work for simple cases but will go into infinite loops on
* complex code.
*
* For example, when modeling a function call whose return value depends on
* all of its arguments, implement this predicate as
* `child = this.getAnArgument()`.
*/
abstract predicate dependsOnChild(Expr child);
}
import SimpleRangeAnalysisInternal
/**
* This class exists to prevent the QL front end from emitting compile errors
* inside `SimpleRangeAnalysis.qll` about certain conjuncts being empty
* because the overrides of `SimpleRangeAnalysisExpr` that happen to be in
* scope do not make use of every feature it offers.
*/
private class Empty extends SimpleRangeAnalysisExpr {
Empty() {
// This predicate is complicated enough that the QL type checker doesn't
// see it as empty but simple enough that the optimizer should.
this = this and none()
}
override float getLowerBounds() { none() }
override float getUpperBounds() { none() }
override predicate dependsOnChild(Expr child) { none() }
}

View File

@@ -0,0 +1,63 @@
/**
* Provides a taint-tracking configuration for reasoning about private information flowing unencrypted to an external location.
*/
import cpp
import semmle.code.cpp.dataflow.TaintTracking
import experimental.semmle.code.cpp.security.PrivateData
import semmle.code.cpp.security.FileWrite
import semmle.code.cpp.security.BufferWrite
import semmle.code.cpp.dataflow.TaintTracking
module PrivateCleartextWrite {
/**
* A data flow source for private information flowing unencrypted to an external location.
*/
abstract class Source extends DataFlow::ExprNode { }
/**
* A data flow sink for private information flowing unencrypted to an external location.
*/
abstract class Sink extends DataFlow::ExprNode { }
/**
* A sanitizer for private information flowing unencrypted to an external location.
*/
abstract class Sanitizer extends DataFlow::ExprNode { }
/** A call to any method whose name suggests that it encodes or encrypts the parameter. */
class ProtectSanitizer extends Sanitizer {
ProtectSanitizer() {
exists(Function m, string s |
this.getExpr().(FunctionCall).getTarget() = m and
m.getName().regexpMatch("(?i).*" + s + ".*")
|
s = "protect" or s = "encode" or s = "encrypt"
)
}
}
class WriteConfig extends TaintTracking::Configuration {
WriteConfig() { this = "Write configuration" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
}
class PrivateDataSource extends Source {
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }
}
class WriteSink extends Sink {
WriteSink() {
exists(FileWrite f, BufferWrite b |
this.asExpr() = f.getASource()
or
this.asExpr() = b.getAChild()
)
}
}
}

View File

@@ -0,0 +1,53 @@
/**
* Provides classes and predicates for identifying private data and functions for security.
*
* 'Private' data in general is anything that would compromise user privacy if exposed. This
* library tries to guess where private data may either be stored in a variable or produced by a
* function.
*
* This library is not concerned with credentials. See `SensitiveActions` for expressions related
* to credentials.
*/
import cpp
/** A string for `match` that identifies strings that look like they represent private data. */
private string privateNames() {
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
// Government identifiers, such as Social Security Numbers
result = "%social%security%number%" or
// Contact information, such as home addresses and telephone numbers
result = "%postcode%" or
result = "%zipcode%" or
// result = "%telephone%" or
// Geographic location - where the user is (or was)
result = "%latitude%" or
result = "%longitude%" or
// Financial data - such as credit card numbers, salary, bank accounts, and debts
result = "%creditcard%" or
result = "%salary%" or
result = "%bankaccount%" or
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
// result = "%email%" or
// result = "%mobile%" or
result = "%employer%" or
// Health - medical conditions, insurance status, prescription records
result = "%medical%"
}
/** An expression that might contain private data. */
abstract class PrivateDataExpr extends Expr { }
/** A functiond call that might produce private data. */
class PrivateFunctionCall extends PrivateDataExpr, FunctionCall {
PrivateFunctionCall() {
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
}
}
/** An access to a variable that might contain private data. */
class PrivateVariableAccess extends PrivateDataExpr, VariableAccess {
PrivateVariableAccess() {
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
}
}

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -512,13 +512,19 @@ abstract class CallContext extends TCallContext {
abstract predicate relevantFor(DataFlowCallable callable);
}
class CallContextAny extends CallContext, TAnyCallContext {
abstract class CallContextNoCall extends CallContext { }
class CallContextAny extends CallContextNoCall, TAnyCallContext {
override string toString() { result = "CcAny" }
override predicate relevantFor(DataFlowCallable callable) { any() }
}
abstract class CallContextCall extends CallContext { }
abstract class CallContextCall extends CallContext {
/** Holds if this call context may be `call`. */
bindingset[call]
abstract predicate matchesCall(DataFlowCall call);
}
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
override string toString() {
@@ -529,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
recordDataFlowCallSite(getCall(), callable)
}
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
DataFlowCall getCall() { this = TSpecificCall(result) }
}
@@ -538,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
override predicate relevantFor(DataFlowCallable callable) {
exists(ParameterNode p | p.getEnclosingCallable() = callable)
}
override predicate matchesCall(DataFlowCall call) { any() }
}
class CallContextReturn extends CallContext, TReturn {
class CallContextReturn extends CallContextNoCall, TReturn {
override string toString() {
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
}

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -539,6 +539,17 @@ class BinaryOperation extends Operation, @bin_op_expr {
/** Gets the right operand of this binary operation. */
Expr getRightOperand() { this.hasChild(result, 1) }
/**
* Holds if `e1` and `e2` (in either order) are the two operands of this
* binary operation.
*/
predicate hasOperands(Expr e1, Expr e2) {
exists(int i | i in [0, 1] |
this.hasChild(e1, i) and
this.hasChild(e2, 1 - i)
)
}
override string toString() { result = "... " + this.getOperator() + " ..." }
override predicate mayBeImpure() {

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -1066,7 +1066,7 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
private predicate localFlowEntry(Node node, Configuration config) {
predicate localFlowEntry(Node node, Configuration config) {
nodeCand2(node, config) and
(
config.isSource(node) or
@@ -1650,53 +1650,53 @@ private class AccessPathOption extends TAccessPathOption {
* Holds if `node` is reachable with access path `ap` from a source in
* the configuration `config`.
*
* The Boolean `fromArg` records whether the node is reached through an
* The call context `cc` records whether the node is reached through an
* argument in a call, and if so, `argAp` records the access path of that
* argument.
*/
private predicate flowFwd(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwd0(node, fromArg, argAp, apf, ap, config) and
flowFwd0(node, cc, argAp, apf, ap, config) and
flowCand(node, _, _, apf, config)
}
private predicate flowFwd0(
Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowCand(node, _, _, _, config) and
config.isSource(node) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
or
flowCand(node, _, _, _, unbind(config)) and
(
exists(Node mid |
flowFwd(mid, fromArg, argAp, apf, ap, config) and
localFlowBigStep(mid, node, true, _, config, _)
exists(Node mid, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, apf, ap, localCC, config) and
localFlowBigStep(mid, node, true, _, config, localCC)
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, fromArg, argAp, _, nil, config) and
localFlowBigStep(mid, node, false, apf, config, _) and
exists(Node mid, AccessPathNil nil, LocalCallContext localCC |
flowFwdLocalEntry(mid, cc, argAp, _, nil, localCC, config) and
localFlowBigStep(mid, node, false, apf, config, localCC) and
apf = ap.(AccessPathNil).getFront()
)
or
exists(Node mid |
flowFwd(mid, _, _, apf, ap, config) and
jumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone()
)
or
exists(Node mid, AccessPathNil nil |
flowFwd(mid, _, _, _, nil, config) and
additionalJumpStep(mid, node, config) and
fromArg = false and
cc instanceof CallContextAny and
argAp = TAccessPathNone() and
ap = TNil(getNodeType(node)) and
apf = ap.(AccessPathNil).getFront()
@@ -1704,40 +1704,51 @@ private predicate flowFwd0(
)
or
// store
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, fromArg, argAp, config))
exists(TypedContent tc | flowFwdStore(node, tc, pop(tc, ap), apf, cc, argAp, config))
or
// read
exists(TypedContent tc |
flowFwdRead(node, _, push(tc, ap), apf, fromArg, argAp, config) and
flowFwdRead(node, _, push(tc, ap), apf, cc, argAp, config) and
flowFwdConsCand(tc, apf, ap, config)
)
or
// flow into a callable
flowFwdIn(_, node, _, _, apf, ap, config) and
fromArg = true and
flowFwdIn(_, node, _, cc, _, apf, ap, config) and
if flowCand(node, true, _, apf, config)
then argAp = TAccessPathSome(ap)
else argAp = TAccessPathNone()
or
// flow out of a callable
exists(DataFlowCall call |
flowFwdOut(call, node, fromArg, argAp, apf, ap, config) and
fromArg = false
exists(DataFlowCallable c |
flowFwdOut(call, node, any(CallContextNoCall innercc), c, argAp, apf, ap, config) and
if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext()
)
or
exists(AccessPath argAp0 |
flowFwdOutFromArg(call, node, argAp0, apf, ap, config) and
flowFwdIsEntered(call, fromArg, argAp, argAp0, config)
flowFwdIsEntered(call, cc, argAp, argAp0, config)
)
)
}
pragma[nomagic]
private predicate flowFwdLocalEntry(
Node node, CallContext cc, AccessPathOption argAp, AccessPathFront apf, AccessPath ap,
LocalCallContext localCC, Configuration config
) {
flowFwd(node, cc, argAp, apf, ap, config) and
localFlowEntry(node, config) and
localCC = getLocalCallContext(cc, node.getEnclosingCallable())
}
pragma[nomagic]
private predicate flowFwdStore(
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, TypedContent tc, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, AccessPathFront apf0 |
flowFwd(mid, fromArg, argAp, apf0, ap0, config) and
flowFwd(mid, cc, argAp, apf0, ap0, config) and
flowFwdStore0(mid, tc, node, apf0, apf, config)
)
}
@@ -1764,20 +1775,20 @@ private predicate flowFwdStore0(
pragma[nomagic]
private predicate flowFwdRead0(
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2,
boolean fromArg, AccessPathOption argAp, Configuration config
Node node1, TypedContent tc, AccessPathFrontHead apf0, AccessPath ap0, Node node2, CallContext cc,
AccessPathOption argAp, Configuration config
) {
flowFwd(node1, fromArg, argAp, apf0, ap0, config) and
flowFwd(node1, cc, argAp, apf0, ap0, config) and
readCandFwd(node1, tc, apf0, node2, config)
}
pragma[nomagic]
private predicate flowFwdRead(
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, boolean fromArg,
Node node, AccessPathFrontHead apf0, AccessPath ap0, AccessPathFront apf, CallContext cc,
AccessPathOption argAp, Configuration config
) {
exists(Node mid, TypedContent tc |
flowFwdRead0(mid, tc, apf0, ap0, node, fromArg, argAp, config) and
flowFwdRead0(mid, tc, apf0, ap0, node, cc, argAp, config) and
flowCand(node, _, _, apf, unbind(config)) and
flowCandConsCand(tc, apf, unbind(config))
)
@@ -1795,13 +1806,16 @@ private predicate flowFwdConsCand(
pragma[nomagic]
private predicate flowFwdIn(
DataFlowCall call, ParameterNode p, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, ParameterNode p, CallContext outercc, CallContext innercc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ArgumentNode arg, boolean allowsFieldFlow |
flowFwd(arg, fromArg, argAp, apf, ap, config) and
exists(ArgumentNode arg, boolean allowsFieldFlow, DataFlowCallable c |
flowFwd(arg, outercc, argAp, apf, ap, config) and
flowIntoCallNodeCand2(call, arg, p, allowsFieldFlow, config) and
flowCand(p, _, _, _, unbind(config))
c = p.getEnclosingCallable() and
c = resolveCall(call, outercc) and
flowCand(p, _, _, _, unbind(config)) and
if recordDataFlowCallSite(call, c) then innercc = TSpecificCall(call) else innercc = TSomeCall()
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1809,13 +1823,19 @@ private predicate flowFwdIn(
pragma[nomagic]
private predicate flowFwdOut(
DataFlowCall call, Node node, boolean fromArg, AccessPathOption argAp, AccessPathFront apf,
AccessPath ap, Configuration config
DataFlowCall call, Node node, CallContext innercc, DataFlowCallable innerc,
AccessPathOption argAp, AccessPathFront apf, AccessPath ap, Configuration config
) {
exists(ReturnNodeExt ret, boolean allowsFieldFlow |
flowFwd(ret, fromArg, argAp, apf, ap, config) and
flowFwd(ret, innercc, argAp, apf, ap, config) and
flowOutOfCallNodeCand2(call, ret, node, allowsFieldFlow, config) and
flowCand(node, _, _, _, unbind(config))
innerc = ret.getEnclosingCallable() and
flowCand(node, _, _, _, unbind(config)) and
(
resolveReturn(innercc, innerc, call)
or
innercc.(CallContextCall).matchesCall(call)
)
|
ap instanceof AccessPathNil or allowsFieldFlow = true
)
@@ -1826,7 +1846,7 @@ private predicate flowFwdOutFromArg(
DataFlowCall call, Node node, AccessPath argAp, AccessPathFront apf, AccessPath ap,
Configuration config
) {
flowFwdOut(call, node, true, TAccessPathSome(argAp), apf, ap, config)
flowFwdOut(call, node, any(CallContextCall ccc), _, TAccessPathSome(argAp), apf, ap, config)
}
/**
@@ -1834,10 +1854,10 @@ private predicate flowFwdOutFromArg(
*/
pragma[nomagic]
private predicate flowFwdIsEntered(
DataFlowCall call, boolean fromArg, AccessPathOption argAp, AccessPath ap, Configuration config
DataFlowCall call, CallContext cc, AccessPathOption argAp, AccessPath ap, Configuration config
) {
exists(ParameterNode p, AccessPathFront apf |
flowFwdIn(call, p, fromArg, argAp, apf, ap, config) and
flowFwdIn(call, p, cc, _, argAp, apf, ap, config) and
flowCand(p, true, TAccessPathFrontSome(_), apf, config)
)
}
@@ -1920,7 +1940,7 @@ private predicate flow0(
// flow out of a callable
flowOut(_, node, _, _, ap, config) and
toReturn = true and
if flowFwd(node, true, TAccessPathSome(_), _, ap, config)
if flowFwd(node, any(CallContextCall ccc), TAccessPathSome(_), _, ap, config)
then returnAp = TAccessPathSome(ap)
else returnAp = TAccessPathNone()
}
@@ -2006,9 +2026,10 @@ private predicate flowIsReturned(
DataFlowCall call, boolean toReturn, AccessPathOption returnAp, AccessPath ap,
Configuration config
) {
exists(ReturnNodeExt ret |
exists(ReturnNodeExt ret, CallContextCall ccc |
flowOut(call, ret, toReturn, returnAp, ap, config) and
flowFwd(ret, true, TAccessPathSome(_), _, ap, config)
flowFwd(ret, ccc, TAccessPathSome(_), _, ap, config) and
ccc.matchesCall(call)
)
}
@@ -2031,7 +2052,7 @@ private newtype TSummaryCtx =
exists(ReturnNodeExt ret, Configuration config, AccessPath ap0 |
parameterFlow(p, ap, ret.getEnclosingCallable(), config) and
flow(ret, true, TAccessPathSome(_), ap0, config) and
flowFwd(ret, true, TAccessPathSome(ap), _, ap0, config)
flowFwd(ret, any(CallContextCall ccc), TAccessPathSome(ap), _, ap0, config)
)
}
@@ -2352,7 +2373,7 @@ private predicate pathOutOfCallable0(
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}
@@ -2867,7 +2888,7 @@ private module FlowExploration {
) {
pos = getReturnPosition(mid.getNode()) and
innercc = mid.getCallContext() and
not innercc instanceof CallContextCall and
innercc instanceof CallContextNoCall and
ap = mid.getAp() and
config = mid.getConfiguration()
}

View File

@@ -512,13 +512,19 @@ abstract class CallContext extends TCallContext {
abstract predicate relevantFor(DataFlowCallable callable);
}
class CallContextAny extends CallContext, TAnyCallContext {
abstract class CallContextNoCall extends CallContext { }
class CallContextAny extends CallContextNoCall, TAnyCallContext {
override string toString() { result = "CcAny" }
override predicate relevantFor(DataFlowCallable callable) { any() }
}
abstract class CallContextCall extends CallContext { }
abstract class CallContextCall extends CallContext {
/** Holds if this call context may be `call`. */
bindingset[call]
abstract predicate matchesCall(DataFlowCall call);
}
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
override string toString() {
@@ -529,6 +535,8 @@ class CallContextSpecificCall extends CallContextCall, TSpecificCall {
recordDataFlowCallSite(getCall(), callable)
}
override predicate matchesCall(DataFlowCall call) { call = this.getCall() }
DataFlowCall getCall() { this = TSpecificCall(result) }
}
@@ -538,9 +546,11 @@ class CallContextSomeCall extends CallContextCall, TSomeCall {
override predicate relevantFor(DataFlowCallable callable) {
exists(ParameterNode p | p.getEnclosingCallable() = callable)
}
override predicate matchesCall(DataFlowCall call) { any() }
}
class CallContextReturn extends CallContext, TReturn {
class CallContextReturn extends CallContextNoCall, TReturn {
override string toString() {
exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")")
}

View File

@@ -13,6 +13,7 @@ private import implementations.Strcat
private import implementations.Strcpy
private import implementations.Strdup
private import implementations.Strftime
private import implementations.StdContainer
private import implementations.StdString
private import implementations.Swap
private import implementations.GetDelim

View File

@@ -0,0 +1,102 @@
/**
* Provides models for C++ containers `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`.
*/
import semmle.code.cpp.models.interfaces.Taint
/**
* Additional model for standard container constructors that reference the
* value type of the container (that is, the `T` in `std::vector<T>`). For
* example the fill constructor:
* ```
* std::vector<std::string> v(100, potentially_tainted_string);
* ```
*/
class StdSequenceContainerConstructor extends Constructor, TaintFunction {
StdSequenceContainerConstructor() {
this.getDeclaringType().hasQualifiedName("std", "vector") or
this.getDeclaringType().hasQualifiedName("std", "deque") or
this.getDeclaringType().hasQualifiedName("std", "list") or
this.getDeclaringType().hasQualifiedName("std", "forward_list")
}
/**
* Gets the index of a parameter to this function that is a reference to the
* value type of the container.
*/
int getAValueTypeParameterIndex() {
getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
getDeclaringType().getTemplateArgument(0) // i.e. the `T` of this `std::vector<T>`
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// taint flow from any parameter of the value type to the returned object
input.isParameterDeref(getAValueTypeParameterIndex()) and
output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported.
}
}
/**
* The standard container functions `push_back` and `push_front`.
*/
class StdSequenceContainerPush extends TaintFunction {
StdSequenceContainerPush() {
this.hasQualifiedName("std", "vector", "push_back") or
this.hasQualifiedName("std", "deque", "push_back") or
this.hasQualifiedName("std", "deque", "push_front") or
this.hasQualifiedName("std", "list", "push_back") or
this.hasQualifiedName("std", "list", "push_front") or
this.hasQualifiedName("std", "forward_list", "push_front")
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from parameter to qualifier
input.isParameterDeref(0) and
output.isQualifierObject()
}
}
/**
* The standard container functions `front` and `back`.
*/
class StdSequenceContainerFrontBack extends TaintFunction {
StdSequenceContainerFrontBack() {
this.hasQualifiedName("std", "array", "front") or
this.hasQualifiedName("std", "array", "back") or
this.hasQualifiedName("std", "vector", "front") or
this.hasQualifiedName("std", "vector", "back") or
this.hasQualifiedName("std", "deque", "front") or
this.hasQualifiedName("std", "deque", "back") or
this.hasQualifiedName("std", "list", "front") or
this.hasQualifiedName("std", "list", "back") or
this.hasQualifiedName("std", "forward_list", "front")
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from object to returned reference
input.isQualifierObject() and
output.isReturnValueDeref()
}
}
/**
* The standard container `swap` functions.
*/
class StdSequenceContainerSwap extends TaintFunction {
StdSequenceContainerSwap() {
this.hasQualifiedName("std", "array", "swap") or
this.hasQualifiedName("std", "vector", "swap") or
this.hasQualifiedName("std", "deque", "swap") or
this.hasQualifiedName("std", "list", "swap") or
this.hasQualifiedName("std", "forward_list", "swap")
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// container1.swap(container2)
input.isQualifierObject() and
output.isParameterDeref(0)
or
input.isParameterDeref(0) and
output.isQualifierObject()
}
}

View File

@@ -1,10 +1,20 @@
import semmle.code.cpp.models.interfaces.Taint
/**
* The standard function `std::string.c_str`.
* The `std::basic_string` template class.
*/
class StdBasicString extends TemplateClass {
StdBasicString() { this.hasQualifiedName("std", "basic_string") }
}
/**
* The `std::string` functions `c_str` and `data`.
*/
class StdStringCStr extends TaintFunction {
StdStringCStr() { this.hasQualifiedName("std", "basic_string", "c_str") }
StdStringCStr() {
this.hasQualifiedName("std", "basic_string", "c_str") or
this.hasQualifiedName("std", "basic_string", "data")
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string itself (qualifier) to return value
@@ -12,3 +22,126 @@ class StdStringCStr extends TaintFunction {
output.isReturnValue()
}
}
/**
* The `std::string` function `operator+`.
*/
class StdStringPlus extends TaintFunction {
StdStringPlus() {
this.hasQualifiedName("std", "operator+") and
this.getUnspecifiedType() = any(StdBasicString s).getAnInstantiation()
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from parameters to return value
(
input.isParameterDeref(0) or
input.isParameterDeref(1)
) and
output.isReturnValue()
}
}
/**
* The `std::string` functions `operator+=`, `append`, `insert` and
* `replace`. All of these functions combine the existing string
* with a new string (or character) from one of the arguments.
*/
class StdStringAppend extends TaintFunction {
StdStringAppend() {
this.hasQualifiedName("std", "basic_string", "operator+=") or
this.hasQualifiedName("std", "basic_string", "append") or
this.hasQualifiedName("std", "basic_string", "insert") or
this.hasQualifiedName("std", "basic_string", "replace")
}
/**
* Gets the index of a parameter to this function that is a string (or
* character).
*/
int getAStringParameter() {
getParameter(result).getType() instanceof PointerType or
getParameter(result).getType() instanceof ReferenceType or
getParameter(result).getType() = getDeclaringType().getTemplateArgument(0) // i.e. `std::basic_string::CharT`
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string and parameter to string (qualifier) and return value
(
input.isQualifierObject() or
input.isParameterDeref(getAStringParameter())
) and
(
output.isQualifierObject() or
output.isReturnValueDeref()
)
}
}
/**
* The standard function `std::string.assign`.
*/
class StdStringAssign extends TaintFunction {
StdStringAssign() { this.hasQualifiedName("std", "basic_string", "assign") }
/**
* Gets the index of a parameter to this function that is a string (or
* character).
*/
int getAStringParameter() {
getParameter(result).getType() instanceof PointerType or
getParameter(result).getType() instanceof ReferenceType or
getParameter(result).getType() = getDeclaringType().getTemplateArgument(0) // i.e. `std::basic_string::CharT`
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from parameter to string itself (qualifier) and return value
input.isParameterDeref(getAStringParameter()) and
(
output.isQualifierObject() or
output.isReturnValueDeref()
)
}
}
/**
* The standard function `std::string.copy`.
*/
class StdStringCopy extends TaintFunction {
StdStringCopy() { this.hasQualifiedName("std", "basic_string", "copy") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// copy(dest, num, pos)
input.isQualifierObject() and
output.isParameterDeref(0)
}
}
/**
* The standard function `std::string.substr`.
*/
class StdStringSubstr extends TaintFunction {
StdStringSubstr() { this.hasQualifiedName("std", "basic_string", "substr") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// substr(pos, num)
input.isQualifierObject() and
output.isReturnValue()
}
}
/**
* The standard function `std::string.swap`.
*/
class StdStringSwap extends TaintFunction {
StdStringSwap() { this.hasQualifiedName("std", "basic_string", "swap") }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// str1.swap(str2)
input.isQualifierObject() and
output.isParameterDeref(0)
or
input.isParameterDeref(0) and
output.isQualifierObject()
}
}

View File

@@ -44,6 +44,7 @@
import cpp
private import RangeAnalysisUtils
private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
import RangeSSA
import SimpleRangeAnalysisCached
private import NanAnalysis
@@ -126,7 +127,7 @@ private class UnsignedBitwiseAndExpr extends BitwiseAndExpr {
UnsignedBitwiseAndExpr() {
(
getLeftOperand().getFullyConverted().getType().getUnderlyingType().(IntegralType).isUnsigned() or
getLeftOperand().getFullyConverted().getValue().toInt() >= 0
getValue(getLeftOperand().getFullyConverted()).toInt() >= 0
) and
(
getRightOperand()
@@ -135,7 +136,7 @@ private class UnsignedBitwiseAndExpr extends BitwiseAndExpr {
.getUnderlyingType()
.(IntegralType)
.isUnsigned() or
getRightOperand().getFullyConverted().getValue().toInt() >= 0
getValue(getRightOperand().getFullyConverted()).toInt() >= 0
)
}
}
@@ -156,6 +157,92 @@ float safeFloor(float v) {
result = v
}
/** A `MulExpr` where exactly one operand is constant. */
private class MulByConstantExpr extends MulExpr {
float constant;
Expr operand;
MulByConstantExpr() {
exists(Expr constantExpr |
this.hasOperands(constantExpr, operand) and
constant = getValue(constantExpr.getFullyConverted()).toFloat() and
not exists(getValue(operand.getFullyConverted()).toFloat())
)
}
/** Gets the value of the constant operand. */
float getConstant() { result = constant }
/** Gets the non-constant operand. */
Expr getOperand() { result = operand }
}
private class UnsignedMulExpr extends MulExpr {
UnsignedMulExpr() {
this.getType().(IntegralType).isUnsigned() and
// Avoid overlap. It should be slightly cheaper to analyze
// `MulByConstantExpr`.
not this instanceof MulByConstantExpr
}
}
/**
* Holds if `expr` is effectively a multiplication of `operand` with the
* positive constant `positive`.
*/
private predicate effectivelyMultipliesByPositive(Expr expr, Expr operand, float positive) {
operand = expr.(MulByConstantExpr).getOperand() and
positive = expr.(MulByConstantExpr).getConstant() and
positive >= 0.0 // includes positive zero
or
operand = expr.(UnaryPlusExpr).getOperand() and
positive = 1.0
or
operand = expr.(CommaExpr).getRightOperand() and
positive = 1.0
or
operand = expr.(StmtExpr).getResultExpr() and
positive = 1.0
}
/**
* Holds if `expr` is effectively a multiplication of `operand` with the
* negative constant `negative`.
*/
private predicate effectivelyMultipliesByNegative(Expr expr, Expr operand, float negative) {
operand = expr.(MulByConstantExpr).getOperand() and
negative = expr.(MulByConstantExpr).getConstant() and
negative < 0.0 // includes negative zero
or
operand = expr.(UnaryMinusExpr).getOperand() and
negative = -1.0
}
private class AssignMulByConstantExpr extends AssignMulExpr {
float constant;
AssignMulByConstantExpr() { constant = getValue(this.getRValue().getFullyConverted()).toFloat() }
float getConstant() { result = constant }
}
private class AssignMulByPositiveConstantExpr extends AssignMulByConstantExpr {
AssignMulByPositiveConstantExpr() { constant >= 0.0 }
}
private class AssignMulByNegativeConstantExpr extends AssignMulByConstantExpr {
AssignMulByNegativeConstantExpr() { constant < 0.0 }
}
private class UnsignedAssignMulExpr extends AssignMulExpr {
UnsignedAssignMulExpr() {
this.getType().(IntegralType).isUnsigned() and
// Avoid overlap. It should be slightly cheaper to analyze
// `AssignMulByConstantExpr`.
not this instanceof AssignMulByConstantExpr
}
}
/** Set of expressions which we know how to analyze. */
private predicate analyzableExpr(Expr e) {
// The type of the expression must be arithmetic. We reuse the logic in
@@ -164,9 +251,9 @@ private predicate analyzableExpr(Expr e) {
(
exists(getValue(e).toFloat())
or
e instanceof UnaryPlusExpr
effectivelyMultipliesByPositive(e, _, _)
or
e instanceof UnaryMinusExpr
effectivelyMultipliesByNegative(e, _, _)
or
e instanceof MinExpr
or
@@ -178,20 +265,22 @@ private predicate analyzableExpr(Expr e) {
or
e instanceof SubExpr
or
e instanceof UnsignedMulExpr
or
e instanceof AssignExpr
or
e instanceof AssignAddExpr
or
e instanceof AssignSubExpr
or
e instanceof UnsignedAssignMulExpr
or
e instanceof AssignMulByConstantExpr
or
e instanceof CrementOperation
or
e instanceof RemExpr
or
e instanceof CommaExpr
or
e instanceof StmtExpr
or
// A conversion is analyzable, provided that its child has an arithmetic
// type. (Sometimes the child is a reference type, and so does not get
// any bounds.) Rather than checking whether the type of the child is
@@ -206,7 +295,10 @@ private predicate analyzableExpr(Expr e) {
e instanceof UnsignedBitwiseAndExpr
or
// `>>` by a constant
exists(e.(RShiftExpr).getRightOperand().getValue())
exists(getValue(e.(RShiftExpr).getRightOperand()))
or
// A modeled expression for range analysis
e instanceof SimpleRangeAnalysisExpr
)
}
@@ -227,25 +319,33 @@ private predicate defDependsOnDef(
// Definitions with a defining value.
exists(Expr expr | assignmentDef(def, v, expr) | exprDependsOnDef(expr, srcDef, srcVar))
or
exists(AssignAddExpr assignAdd, RangeSsaDefinition nextDef |
exists(AssignAddExpr assignAdd |
def = assignAdd and
assignAdd.getLValue() = nextDef.getAUse(v)
|
defDependsOnDef(nextDef, v, srcDef, srcVar) or
exprDependsOnDef(assignAdd.getRValue(), srcDef, srcVar)
def.getAVariable() = v and
exprDependsOnDef(assignAdd.getAnOperand(), srcDef, srcVar)
)
or
exists(AssignSubExpr assignSub, RangeSsaDefinition nextDef |
exists(AssignSubExpr assignSub |
def = assignSub and
assignSub.getLValue() = nextDef.getAUse(v)
|
defDependsOnDef(nextDef, v, srcDef, srcVar) or
exprDependsOnDef(assignSub.getRValue(), srcDef, srcVar)
def.getAVariable() = v and
exprDependsOnDef(assignSub.getAnOperand(), srcDef, srcVar)
)
or
exists(UnsignedAssignMulExpr assignMul |
def = assignMul and
def.getAVariable() = v and
exprDependsOnDef(assignMul.getAnOperand(), srcDef, srcVar)
)
or
exists(AssignMulByConstantExpr assignMul |
def = assignMul and
def.getAVariable() = v and
exprDependsOnDef(assignMul.getLValue(), srcDef, srcVar)
)
or
exists(CrementOperation crem |
def = crem and
crem.getOperand() = v.getAnAccess() and
def.getAVariable() = v and
exprDependsOnDef(crem.getOperand(), srcDef, srcVar)
)
or
@@ -258,12 +358,14 @@ private predicate defDependsOnDef(
* the structure of `getLowerBoundsImpl` and `getUpperBoundsImpl`.
*/
private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVariable srcVar) {
exists(UnaryMinusExpr negateExpr | e = negateExpr |
exprDependsOnDef(negateExpr.getOperand(), srcDef, srcVar)
exists(Expr operand |
effectivelyMultipliesByNegative(e, operand, _) and
exprDependsOnDef(operand, srcDef, srcVar)
)
or
exists(UnaryPlusExpr plusExpr | e = plusExpr |
exprDependsOnDef(plusExpr.getOperand(), srcDef, srcVar)
exists(Expr operand |
effectivelyMultipliesByPositive(e, operand, _) and
exprDependsOnDef(operand, srcDef, srcVar)
)
or
exists(MinExpr minExpr | e = minExpr | exprDependsOnDef(minExpr.getAnOperand(), srcDef, srcVar))
@@ -278,6 +380,10 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
or
exists(SubExpr subExpr | e = subExpr | exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar))
or
exists(UnsignedMulExpr mulExpr | e = mulExpr |
exprDependsOnDef(mulExpr.getAnOperand(), srcDef, srcVar)
)
or
exists(AssignExpr addExpr | e = addExpr | exprDependsOnDef(addExpr.getRValue(), srcDef, srcVar))
or
exists(AssignAddExpr addExpr | e = addExpr |
@@ -288,20 +394,20 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
exprDependsOnDef(subExpr.getAnOperand(), srcDef, srcVar)
)
or
exists(UnsignedAssignMulExpr mulExpr | e = mulExpr |
exprDependsOnDef(mulExpr.getAnOperand(), srcDef, srcVar)
)
or
exists(AssignMulByConstantExpr mulExpr | e = mulExpr |
exprDependsOnDef(mulExpr.getLValue(), srcDef, srcVar)
)
or
exists(CrementOperation crementExpr | e = crementExpr |
exprDependsOnDef(crementExpr.getOperand(), srcDef, srcVar)
)
or
exists(RemExpr remExpr | e = remExpr | exprDependsOnDef(remExpr.getAnOperand(), srcDef, srcVar))
or
exists(CommaExpr commaExpr | e = commaExpr |
exprDependsOnDef(commaExpr.getRightOperand(), srcDef, srcVar)
)
or
exists(StmtExpr stmtExpr | e = stmtExpr |
exprDependsOnDef(stmtExpr.getResultExpr(), srcDef, srcVar)
)
or
exists(Conversion convExpr | e = convExpr | exprDependsOnDef(convExpr.getExpr(), srcDef, srcVar))
or
// unsigned `&`
@@ -313,11 +419,21 @@ private predicate exprDependsOnDef(Expr e, RangeSsaDefinition srcDef, StackVaria
// `>>` by a constant
exists(RShiftExpr rs |
rs = e and
exists(rs.getRightOperand().getValue()) and
exists(getValue(rs.getRightOperand())) and
exprDependsOnDef(rs.getLeftOperand(), srcDef, srcVar)
)
or
e = srcDef.getAUse(srcVar)
or
// A modeled expression for range analysis
exists(SimpleRangeAnalysisExpr rae | rae = e |
rae.dependsOnDef(srcDef, srcVar)
or
exists(Expr child |
rae.dependsOnChild(child) and
exprDependsOnDef(child, srcDef, srcVar)
)
)
}
/**
@@ -381,9 +497,17 @@ private predicate assignmentDef(RangeSsaDefinition def, StackVariable v, Expr ex
)
}
/** See comment above sourceDef. */
/** See comment above assignmentDef. */
private predicate analyzableDef(RangeSsaDefinition def, StackVariable v) {
assignmentDef(def, v, _) or defDependsOnDef(def, v, _, _)
assignmentDef(def, v, _)
or
analyzableExpr(def.(AssignOperation)) and
v = def.getAVariable()
or
analyzableExpr(def.(CrementOperation)) and
v = def.getAVariable()
or
phiDependsOnDef(def, v, _, _)
}
/**
@@ -435,13 +559,6 @@ private float addRoundingDownSmall(float x, float small) {
if (x + small) - x > small then result = (x + small).nextDown() else result = (x + small)
}
/**
* Gets the truncated lower bounds of the fully converted expression.
*/
private float getFullyConvertedLowerBounds(Expr expr) {
result = getTruncatedLowerBounds(expr.getFullyConverted())
}
/**
* Gets the lower bounds of the expression.
*
@@ -488,13 +605,6 @@ private float getTruncatedLowerBounds(Expr expr) {
result = exprMinVal(expr)
}
/**
* Gets the truncated upper bounds of the fully converted expression.
*/
private float getFullyConvertedUpperBounds(Expr expr) {
result = getTruncatedUpperBounds(expr.getFullyConverted())
}
/**
* Gets the upper bounds of the expression.
*
@@ -562,15 +672,16 @@ deprecated predicate positive_overflow(Expr expr) { exprMightOverflowPositively(
/** Only to be called by `getTruncatedLowerBounds`. */
private float getLowerBoundsImpl(Expr expr) {
exists(UnaryPlusExpr plusExpr |
expr = plusExpr and
result = getFullyConvertedLowerBounds(plusExpr.getOperand())
exists(Expr operand, float operandLow, float positive |
effectivelyMultipliesByPositive(expr, operand, positive) and
operandLow = getFullyConvertedLowerBounds(operand) and
result = positive * operandLow
)
or
exists(UnaryMinusExpr negateExpr, float xHigh |
expr = negateExpr and
xHigh = getFullyConvertedUpperBounds(negateExpr.getOperand()) and
result = -xHigh
exists(Expr operand, float operandHigh, float negative |
effectivelyMultipliesByNegative(expr, operand, negative) and
operandHigh = getFullyConvertedUpperBounds(operand) and
result = negative * operandHigh
)
or
exists(MinExpr minExpr |
@@ -625,6 +736,13 @@ private float getLowerBoundsImpl(Expr expr) {
result = addRoundingDown(xLow, -yHigh)
)
or
exists(UnsignedMulExpr mulExpr, float xLow, float yLow |
expr = mulExpr and
xLow = getFullyConvertedLowerBounds(mulExpr.getLeftOperand()) and
yLow = getFullyConvertedLowerBounds(mulExpr.getRightOperand()) and
result = xLow * yLow
)
or
exists(AssignExpr assign |
expr = assign and
result = getFullyConvertedLowerBounds(assign.getRValue())
@@ -644,6 +762,25 @@ private float getLowerBoundsImpl(Expr expr) {
result = addRoundingDown(xLow, -yHigh)
)
or
exists(UnsignedAssignMulExpr mulExpr, float xLow, float yLow |
expr = mulExpr and
xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and
yLow = getFullyConvertedLowerBounds(mulExpr.getRValue()) and
result = xLow * yLow
)
or
exists(AssignMulByPositiveConstantExpr mulExpr, float xLow |
expr = mulExpr and
xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and
result = xLow * mulExpr.getConstant()
)
or
exists(AssignMulByNegativeConstantExpr mulExpr, float xHigh |
expr = mulExpr and
xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and
result = xHigh * mulExpr.getConstant()
)
or
exists(PrefixIncrExpr incrExpr, float xLow |
expr = incrExpr and
xLow = getFullyConvertedLowerBounds(incrExpr.getOperand()) and
@@ -688,16 +825,6 @@ private float getLowerBoundsImpl(Expr expr) {
)
)
or
exists(CommaExpr commaExpr |
expr = commaExpr and
result = getFullyConvertedLowerBounds(commaExpr.getRightOperand())
)
or
exists(StmtExpr stmtExpr |
expr = stmtExpr and
result = getFullyConvertedLowerBounds(stmtExpr.getResultExpr())
)
or
// If the conversion is to an arithmetic type then we just return the
// lower bound of the child. We do not need to handle truncation and
// overflow here, because that is done in `getTruncatedLowerBounds`.
@@ -711,7 +838,9 @@ private float getLowerBoundsImpl(Expr expr) {
or
// Use SSA to get the lower bounds for a variable use.
exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) |
result = getDefLowerBounds(def, v)
result = getDefLowerBounds(def, v) and
// Not explicitly modeled by a SimpleRangeAnalysisExpr
not expr instanceof SimpleRangeAnalysisExpr
)
or
// unsigned `&` (tighter bounds may exist)
@@ -724,22 +853,29 @@ private float getLowerBoundsImpl(Expr expr) {
exists(RShiftExpr rsExpr, float left, int right |
rsExpr = expr and
left = getFullyConvertedLowerBounds(rsExpr.getLeftOperand()) and
right = rsExpr.getRightOperand().getFullyConverted().getValue().toInt() and
right = getValue(rsExpr.getRightOperand().getFullyConverted()).toInt() and
result = safeFloor(left / 2.pow(right))
)
or
// A modeled expression for range analysis
exists(SimpleRangeAnalysisExpr rangeAnalysisExpr |
rangeAnalysisExpr = expr and
result = rangeAnalysisExpr.getLowerBounds()
)
}
/** Only to be called by `getTruncatedUpperBounds`. */
private float getUpperBoundsImpl(Expr expr) {
exists(UnaryPlusExpr plusExpr |
expr = plusExpr and
result = getFullyConvertedUpperBounds(plusExpr.getOperand())
exists(Expr operand, float operandHigh, float positive |
effectivelyMultipliesByPositive(expr, operand, positive) and
operandHigh = getFullyConvertedUpperBounds(operand) and
result = positive * operandHigh
)
or
exists(UnaryMinusExpr negateExpr, float xLow |
expr = negateExpr and
xLow = getFullyConvertedLowerBounds(negateExpr.getOperand()) and
result = -xLow
exists(Expr operand, float operandLow, float negative |
effectivelyMultipliesByNegative(expr, operand, negative) and
operandLow = getFullyConvertedLowerBounds(operand) and
result = negative * operandLow
)
or
exists(MaxExpr maxExpr |
@@ -794,6 +930,13 @@ private float getUpperBoundsImpl(Expr expr) {
result = addRoundingUp(xHigh, -yLow)
)
or
exists(UnsignedMulExpr mulExpr, float xHigh, float yHigh |
expr = mulExpr and
xHigh = getFullyConvertedUpperBounds(mulExpr.getLeftOperand()) and
yHigh = getFullyConvertedUpperBounds(mulExpr.getRightOperand()) and
result = xHigh * yHigh
)
or
exists(AssignExpr assign |
expr = assign and
result = getFullyConvertedUpperBounds(assign.getRValue())
@@ -813,6 +956,25 @@ private float getUpperBoundsImpl(Expr expr) {
result = addRoundingUp(xHigh, -yLow)
)
or
exists(UnsignedAssignMulExpr mulExpr, float xHigh, float yHigh |
expr = mulExpr and
xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and
yHigh = getFullyConvertedUpperBounds(mulExpr.getRValue()) and
result = xHigh * yHigh
)
or
exists(AssignMulByPositiveConstantExpr mulExpr, float xHigh |
expr = mulExpr and
xHigh = getFullyConvertedUpperBounds(mulExpr.getLValue()) and
result = xHigh * mulExpr.getConstant()
)
or
exists(AssignMulByNegativeConstantExpr mulExpr, float xLow |
expr = mulExpr and
xLow = getFullyConvertedLowerBounds(mulExpr.getLValue()) and
result = xLow * mulExpr.getConstant()
)
or
exists(PrefixIncrExpr incrExpr, float xHigh |
expr = incrExpr and
xHigh = getFullyConvertedUpperBounds(incrExpr.getOperand()) and
@@ -855,16 +1017,6 @@ private float getUpperBoundsImpl(Expr expr) {
)
)
or
exists(CommaExpr commaExpr |
expr = commaExpr and
result = getFullyConvertedUpperBounds(commaExpr.getRightOperand())
)
or
exists(StmtExpr stmtExpr |
expr = stmtExpr and
result = getFullyConvertedUpperBounds(stmtExpr.getResultExpr())
)
or
// If the conversion is to an arithmetic type then we just return the
// upper bound of the child. We do not need to handle truncation and
// overflow here, because that is done in `getTruncatedUpperBounds`.
@@ -878,7 +1030,9 @@ private float getUpperBoundsImpl(Expr expr) {
or
// Use SSA to get the upper bounds for a variable use.
exists(RangeSsaDefinition def, StackVariable v | expr = def.getAUse(v) |
result = getDefUpperBounds(def, v)
result = getDefUpperBounds(def, v) and
// Not explicitly modeled by a SimpleRangeAnalysisExpr
not expr instanceof SimpleRangeAnalysisExpr
)
or
// unsigned `&` (tighter bounds may exist)
@@ -893,9 +1047,15 @@ private float getUpperBoundsImpl(Expr expr) {
exists(RShiftExpr rsExpr, float left, int right |
rsExpr = expr and
left = getFullyConvertedUpperBounds(rsExpr.getLeftOperand()) and
right = rsExpr.getRightOperand().getFullyConverted().getValue().toInt() and
right = getValue(rsExpr.getRightOperand().getFullyConverted()).toInt() and
result = safeFloor(left / 2.pow(right))
)
or
// A modeled expression for range analysis
exists(SimpleRangeAnalysisExpr rangeAnalysisExpr |
rangeAnalysisExpr = expr and
result = rangeAnalysisExpr.getUpperBounds()
)
}
/**
@@ -1041,6 +1201,28 @@ private float getDefLowerBoundsImpl(RangeSsaDefinition def, StackVariable v) {
result = addRoundingDown(lhsLB, -rhsUB)
)
or
exists(UnsignedAssignMulExpr assignMul, RangeSsaDefinition nextDef, float lhsLB, float rhsLB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsLB = getDefLowerBounds(nextDef, v) and
rhsLB = getFullyConvertedLowerBounds(assignMul.getRValue()) and
result = lhsLB * rhsLB
)
or
exists(AssignMulByPositiveConstantExpr assignMul, RangeSsaDefinition nextDef, float lhsLB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsLB = getDefLowerBounds(nextDef, v) and
result = lhsLB * assignMul.getConstant()
)
or
exists(AssignMulByNegativeConstantExpr assignMul, RangeSsaDefinition nextDef, float lhsUB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsUB = getDefUpperBounds(nextDef, v) and
result = lhsUB * assignMul.getConstant()
)
or
exists(IncrementOperation incr, float newLB |
def = incr and
incr.getOperand() = v.getAnAccess() and
@@ -1083,6 +1265,28 @@ private float getDefUpperBoundsImpl(RangeSsaDefinition def, StackVariable v) {
result = addRoundingUp(lhsUB, -rhsLB)
)
or
exists(UnsignedAssignMulExpr assignMul, RangeSsaDefinition nextDef, float lhsUB, float rhsUB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsUB = getDefUpperBounds(nextDef, v) and
rhsUB = getFullyConvertedUpperBounds(assignMul.getRValue()) and
result = lhsUB * rhsUB
)
or
exists(AssignMulByPositiveConstantExpr assignMul, RangeSsaDefinition nextDef, float lhsUB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsUB = getDefUpperBounds(nextDef, v) and
result = lhsUB * assignMul.getConstant()
)
or
exists(AssignMulByNegativeConstantExpr assignMul, RangeSsaDefinition nextDef, float lhsLB |
def = assignMul and
assignMul.getLValue() = nextDef.getAUse(v) and
lhsLB = getDefLowerBounds(nextDef, v) and
result = lhsLB * assignMul.getConstant()
)
or
exists(IncrementOperation incr, float newUB |
def = incr and
incr.getOperand() = v.getAnAccess() and
@@ -1480,3 +1684,25 @@ private module SimpleRangeAnalysisCached {
convertedExprMightOverflowPositively(expr)
}
}
/**
* INTERNAL: do not use. This module contains utilities for use in the
* experimental `SimpleRangeAnalysisExpr` module.
*/
module SimpleRangeAnalysisInternal {
/**
* Gets the truncated lower bounds of the fully converted expression.
*/
float getFullyConvertedLowerBounds(Expr expr) {
result = getTruncatedLowerBounds(expr.getFullyConverted())
}
/**
* Gets the truncated upper bounds of the fully converted expression.
*/
float getFullyConvertedUpperBounds(Expr expr) {
result = getTruncatedUpperBounds(expr.getFullyConverted())
}
}
private import SimpleRangeAnalysisInternal

View File

@@ -0,0 +1,9 @@
/// Adds its arguments (has custom modeling in QL)
int custom_add_function(int a, int b);
int test_extensibility_add(int x) {
if (x >= -10 && x <= 10) {
int result = custom_add_function(x, 100);
return result; // 90 .. 110
}
}

View File

@@ -0,0 +1,4 @@
| extensibility.c:5:7:5:7 | x | -2.147483648E9 | 2.147483647E9 |
| extensibility.c:5:19:5:19 | x | -10.0 | 2.147483647E9 |
| extensibility.c:6:38:6:38 | x | -10.0 | 10.0 |
| extensibility.c:7:12:7:17 | result | 90.0 | 110.0 |

View File

@@ -0,0 +1,32 @@
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
class CustomAddFunctionCall extends SimpleRangeAnalysisExpr, FunctionCall {
CustomAddFunctionCall() { this.getTarget().hasGlobalName("custom_add_function") }
override float getLowerBounds() {
exists(float lower0, float lower1 |
lower0 = getFullyConvertedLowerBounds(this.getArgument(0)) and
lower1 = getFullyConvertedLowerBounds(this.getArgument(1)) and
// Note: this rounds toward 0, not -Inf as it should
result = lower0 + lower1
)
}
override float getUpperBounds() {
exists(float upper0, float upper1 |
upper0 = getFullyConvertedUpperBounds(this.getArgument(0)) and
upper1 = getFullyConvertedUpperBounds(this.getArgument(1)) and
// Note: this rounds toward 0, not Inf as it should
result = upper0 + upper1
)
}
override predicate dependsOnChild(Expr child) { child = this.getAnArgument() }
}
from VariableAccess expr, float lower, float upper
where
lower = lowerBound(expr) and
upper = upperBound(expr)
select expr, lower, upper

View File

@@ -0,0 +1,21 @@
edges
| test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp |
| test.cpp:81:17:81:20 | call to func | test.cpp:82:24:82:28 | buff5 |
| test.cpp:81:22:81:28 | medical | test.cpp:81:17:81:20 | call to func |
nodes
| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
| test.cpp:74:24:74:30 | medical | semmle.label | medical |
| test.cpp:77:16:77:22 | medical | semmle.label | medical |
| test.cpp:78:24:78:27 | temp | semmle.label | temp |
| test.cpp:81:17:81:20 | call to func | semmle.label | call to func |
| test.cpp:81:22:81:28 | medical | semmle.label | medical |
| test.cpp:82:24:82:28 | buff5 | semmle.label | buff5 |
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
#select
| test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:57:9:57:18 | theZipcode | this source. |
| test.cpp:74:24:74:30 | medical | This write into the external location 'medical' may contain unencrypted data from $@ | test.cpp:74:24:74:30 | medical | this source. |
| test.cpp:78:24:78:27 | temp | This write into the external location 'temp' may contain unencrypted data from $@ | test.cpp:77:16:77:22 | medical | this source. |
| test.cpp:82:24:82:28 | buff5 | This write into the external location 'buff5' may contain unencrypted data from $@ | test.cpp:81:22:81:28 | medical | this source. |
| test.cpp:96:37:96:46 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:96:37:96:46 | theZipcode | this source. |
| test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@ | test.cpp:99:42:99:51 | theZipcode | this source. |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-359/PrivateCleartextWrite.ql

View File

@@ -0,0 +1,105 @@
#define FILE int
#define wchar char
#define size_t int
typedef int streamsize;
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
int fputs(const char *s, FILE *stream);
int fputc(int c, FILE *stream);
int fprintf(FILE *stream, const char *format, ...);
int sprintf(char *s, const char *format, ...);
size_t strlen(const char *s);
namespace std
{
template <class charT>
struct char_traits;
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);
};
template <class charT, class traits = char_traits<charT>>
class basic_ofstream : public basic_ostream<charT, traits>
{
public:
};
template <class charT, class traits>
basic_ostream<charT, traits> &operator<<(basic_ostream<charT, traits> &, const charT *);
typedef basic_ostream<char> ostream;
typedef basic_ofstream<char> ofstream;
}; // namespace std
using namespace std;
char *encrypt(char *buffer)
{
return buffer;
}
char *func(char *buffer)
{
return buffer;
}
// test for CleartextFileWrite
void file()
{
char *theZipcode = "cleartext zipcode!";
FILE *file;
// BAD: write zipcode to file in cleartext
fputs(theZipcode, file);
// GOOD: encrypt first
char *encrypted = encrypt(theZipcode);
fwrite(encrypted, sizeof(encrypted), 1, file);
}
// test for CleartextBufferWrite
int main(int argc, char **argv)
{
char *medical = "medical";
char *buff1;
char *buff2;
char *buff3;
char *buff4;
// BAD: write medical to buffer in cleartext
sprintf(buff1, "%s", medical);
// BAD: write medical to buffer in cleartext
char *temp = medical;
sprintf(buff2, "%s", temp);
// BAD: write medical to buffer in cleartext
char *buff5 = func(medical);
sprintf(buff3, "%s", buff5);
char *buff6 = encrypt(medical);
// GOOD: encrypt first
sprintf(buff4, "%s", buff6);
}
// test for CleartextFileWrite
void stream()
{
char *theZipcode = "cleartext zipcode!";
ofstream mystream;
// BAD: write zipcode to file in cleartext
mystream << "the zipcode is: " << theZipcode;
// BAD: write zipcode to file in cleartext
(mystream << "the zipcode is: ").write(theZipcode, strlen(theZipcode));
// GOOD: encrypt first
char *encrypted = encrypt(theZipcode);
mystream << "the zipcode is: " << encrypted;
(mystream << "the zipcode is: ").write(encrypted, strlen(encrypted));
}

View File

@@ -0,0 +1,25 @@
///// Library routines /////
int scanf(const char *format, ...);
int sscanf(const char *str, const char *format, ...);
int fscanf(const char *str, const char *format, ...);
///// Test code /////
int main(int argc, char **argv)
{
// BAD, do not use scanf without specifying a length first
char buf1[10];
scanf("%s", buf1);
// GOOD, length is specified
char buf2[10];
sscanf(buf2, "%9s");
// BAD, do not use scanf without specifying a length first
char file[10];
fscanf(file, "%s", buf2);
return 0;
}

View File

@@ -0,0 +1,2 @@
| MemoryUnsafeFunctionScan.cpp:14:5:14:9 | call to scanf | Dangerous use of one of the scanf functions |
| MemoryUnsafeFunctionScan.cpp:22:5:22:10 | call to fscanf | Dangerous use of one of the scanf functions |

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.ql

View File

@@ -1,310 +0,0 @@
typedef unsigned long size_t;
namespace std
{
template<class charT> struct char_traits;
typedef size_t streamsize;
struct ptrdiff_t;
template <class iterator_category,
class value_type,
class difference_type = ptrdiff_t,
class pointer_type = value_type*,
class reference_type = value_type&>
struct iterator {
iterator &operator++();
iterator operator++(int);
bool operator==(iterator other) const;
bool operator!=(iterator other) const;
reference_type operator*() const;
};
struct input_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
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 std::iterator<random_access_iterator_tag, charT> iterator;
typedef std::iterator<random_access_iterator_tag, const charT> const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() 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();
void sink(const char *s) {};
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); // tainted
sink(b);
sink(c); // tainted
sink(b.c_str());
sink(c.c_str()); // tainted
}
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); // tainted [NOT DETECTED]
sink(ss3); // tainted [NOT DETECTED]
sink(ss4); // tainted [NOT DETECTED]
sink(ss5); // tainted [NOT DETECTED]
sink(ss1.str());
sink(ss2.str()); // tainted [NOT DETECTED]
sink(ss3.str()); // tainted [NOT DETECTED]
sink(ss4.str()); // tainted [NOT DETECTED]
sink(ss5.str()); // tainted [NOT DETECTED]
}
void test_stringstream_int(int source)
{
std::stringstream ss1, ss2;
ss1 << 1234;
ss2 << source;
sink(ss1);
sink(ss2); // tainted [NOT DETECTED]
sink(ss1.str());
sink(ss2.str()); // tainted [NOT DETECTED]
}
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"); // tainted
string path2;
path2 = user_input();
sink(path2.c_str(), "r"); // tainted
string path3(user_input());
sink(path3.c_str(), "r"); // tainted
}
void test_string3()
{
const char *cs = source();
// convert char * -> std::string
std::string ss(cs);
sink(cs); // tainted
sink(ss); // tainted
}
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); // tainted
sink(ss); // tainted
}
void test_string_constructors_assignments()
{
{
std::string s1("hello");
std::string s2 = "hello";
std::string s3;
s3 = "hello";
sink(s1);
sink(s2);
sink(s3);
}
{
std::string s1(source());
std::string s2 = source();
std::string s3;
s3 = source();
sink(s1); // tainted
sink(s2); // tainted
sink(s3); // tainted
}
{
std::string s1;
std::string s2 = s1;
std::string s3;
s3 = s1;
sink(s1);
sink(s2);
sink(s3);
}
{
std::string s1 = std::string(source());
std::string s2;
s2 = std::string(source());
sink(s1); // tainted
sink(s2); // tainted
}
}
void sink(char) {}
void test_range_based_for_loop_string() {
std::string s(source());
for(char c : s) {
sink(c); // tainted [NOT DETECTED by IR]
}
for(std::string::iterator it = s.begin(); it != s.end(); ++it) {
sink(*it); // tainted [NOT DETECTED]
}
for(char& c : s) {
sink(c); // tainted [NOT DETECTED by IR]
}
const std::string const_s(source());
for(const char& c : const_s) {
sink(c); // tainted [NOT DETECTED by IR]
}
}
namespace std {
template <class T>
class vector {
private:
void *data_;
public:
vector(int size);
T& operator[](int idx);
const T& operator[](int idx) const;
typedef std::iterator<random_access_iterator_tag, T> iterator;
typedef std::iterator<random_access_iterator_tag, const T> const_iterator;
iterator begin() noexcept;
iterator end() noexcept;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
};
}
void sink(int);
void test_range_based_for_loop_vector(int source1) {
// Tainting the vector by allocating a tainted length. This doesn't represent
// how a vector would typically get tainted, but it allows this test to avoid
// being concerned with std::vector modeling.
std::vector<int> v(source1);
for(int x : v) {
sink(x); // tainted [NOT DETECTED by IR]
}
for(std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
sink(*it); // tainted [NOT DETECTED]
}
for(int& x : v) {
sink(x); // tainted [NOT DETECTED by IR]
}
const std::vector<int> const_v(source1);
for(const int& x : const_v) {
sink(x); // tainted [NOT DETECTED by IR]
}
}

View File

@@ -0,0 +1,169 @@
typedef unsigned long size_t;
// --- string ---
namespace std
{
template<class charT> struct char_traits;
typedef size_t streamsize;
struct ptrdiff_t;
template <class iterator_category,
class value_type,
class difference_type = ptrdiff_t,
class pointer_type = value_type*,
class reference_type = value_type&>
struct iterator {
iterator &operator++();
iterator operator++(int);
bool operator==(iterator other) const;
bool operator!=(iterator other) const;
reference_type operator*() const;
};
struct input_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
template <class T> class allocator {
public:
allocator() throw();
typedef size_t size_type;
};
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
class basic_string {
public:
typedef typename Allocator::size_type size_type;
static const size_type npos = -1;
explicit basic_string(const Allocator& a = Allocator());
basic_string(const charT* s, const Allocator& a = Allocator());
const charT* c_str() const;
charT* data() noexcept;
size_t length() const;
typedef std::iterator<random_access_iterator_tag, charT> iterator;
typedef std::iterator<random_access_iterator_tag, const charT> const_iterator;
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
const_iterator cbegin() const;
const_iterator cend() const;
template<class T> basic_string& operator+=(const T& t);
basic_string& operator+=(const charT* s);
basic_string& append(const basic_string& str);
basic_string& append(const charT* s);
basic_string& append(size_type n, charT c);
basic_string& assign(const basic_string& str);
basic_string& assign(size_type n, charT c);
basic_string& insert(size_type pos, const basic_string& str);
basic_string& insert(size_type pos, size_type n, charT c);
basic_string& replace(size_type pos1, size_type n1, const basic_string& str);
basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c);
size_type copy(charT* s, size_type n, size_type pos = 0) const;
void clear() noexcept;
basic_string substr(size_type pos = 0, size_type n = npos) const;
void swap(basic_string& s) noexcept/*(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value)*/;
};
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> operator+(const basic_string<charT, traits, Allocator>& lhs, const basic_string<charT, traits, Allocator>& rhs);
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> operator+(const basic_string<charT, traits, Allocator>& lhs, const charT* rhs);
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>;
}
// --- vector ---
namespace std {
template<class T, class Allocator = allocator<T>>
class vector {
public:
using value_type = T;
using reference = value_type&;
using const_reference = const value_type&;
using size_type = unsigned int;
using iterator = std::iterator<random_access_iterator_tag, T>;
using const_iterator = std::iterator<random_access_iterator_tag, const T>;
vector() noexcept(noexcept(Allocator())) : vector(Allocator()) { }
explicit vector(const Allocator&) noexcept;
explicit vector(size_type n, const Allocator& = Allocator());
vector(size_type n, const T& value, const Allocator& = Allocator());
~vector();
vector& operator=(const vector& x);
vector& operator=(vector&& x) noexcept/*(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value)*/;
iterator begin() noexcept;
const_iterator begin() const noexcept;
iterator end() noexcept;
const_iterator end() const noexcept;
size_type size() const noexcept;
reference operator[](size_type n);
const_reference operator[](size_type n) const;
const_reference at(size_type n) const;
reference at(size_type n);
reference front();
const_reference front() const;
reference back();
const_reference back() const;
T* data() noexcept;
const T* data() const noexcept;
void push_back(const T& x);
void push_back(T&& x);
iterator insert(const_iterator position, const T& x);
iterator insert(const_iterator position, T&& x);
iterator insert(const_iterator position, size_type n, const T& x);
void swap(vector&) noexcept/*(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value)*/;
void clear() noexcept;
};
}

View File

@@ -0,0 +1,323 @@
#include "stl.h"
using namespace std;
char *source();
namespace ns_char
{
char source();
}
char *user_input() {
return source();
}
void sink(const char *s) {};
void sink(const std::string &s) {};
void sink(const char *filename, const char *mode);
void sink(char) {}
void test_string()
{
char *a = source();
std::string b("123");
std::string c(source());
sink(a); // tainted
sink(b);
sink(c); // tainted
sink(b.c_str());
sink(c.c_str()); // tainted
}
void test_strings2()
{
string path1 = user_input();
sink(path1.c_str(), "r"); // tainted
string path2;
path2 = user_input();
sink(path2.c_str(), "r"); // tainted
string path3(user_input());
sink(path3.c_str(), "r"); // tainted
}
void test_string3()
{
const char *cs = source();
// convert char * -> std::string
std::string ss(cs);
sink(cs); // tainted
sink(ss); // tainted
}
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); // tainted
sink(ss); // tainted
}
void test_string_constructors_assignments()
{
{
std::string s1("hello");
std::string s2 = "hello";
std::string s3;
s3 = "hello";
sink(s1);
sink(s2);
sink(s3);
}
{
std::string s1(source());
std::string s2 = source();
std::string s3;
s3 = source();
sink(s1); // tainted
sink(s2); // tainted
sink(s3); // tainted
}
{
std::string s1;
std::string s2 = s1;
std::string s3;
s3 = s1;
sink(s1);
sink(s2);
sink(s3);
}
{
std::string s1 = std::string(source());
std::string s2;
s2 = std::string(source());
sink(s1); // tainted
sink(s2); // tainted
}
}
void test_range_based_for_loop_string() {
std::string s(source());
for(char c : s) {
sink(c); // tainted [NOT DETECTED by IR]
}
for(std::string::iterator it = s.begin(); it != s.end(); ++it) {
sink(*it); // tainted [NOT DETECTED]
}
for(char& c : s) {
sink(c); // tainted [NOT DETECTED by IR]
}
const std::string const_s(source());
for(const char& c : const_s) {
sink(c); // tainted [NOT DETECTED by IR]
}
}
void test_string_append() {
{
std::string s1("hello");
std::string s2(source());
sink(s1 + s1);
sink(s1 + s2); // tainted
sink(s2 + s1); // tainted
sink(s2 + s2); // tainted
sink(s1 + " world");
sink(s1 + source()); // tainted
}
{
std::string s3("abc");
std::string s4(source());
std::string s5, s6, s7, s8, s9;
s5 = s3 + s4;
sink(s5); // tainted
s6 = s3;
s6 += s4;
sink(s6); // tainted
s7 = s3;
s7 += source();
s7 += " ";
sink(s7); // tainted
s8 = s3;
s8.append(s4);
sink(s8); // tainted
s9 = s3;
s9.append(source());
s9.append(" ");
sink(s9); // tainted
}
{
std::string s10("abc");
char c = ns_char::source();
s10.append(1, c);
sink(s10); // tainted
}
}
void test_string_assign() {
std::string s1("hello");
std::string s2(source());
char c = ns_char::source();
std::string s3, s4, s5;
std::string s6(source());
sink(s3.assign(s1));
sink(s3);
sink(s4.assign(s2)); // tainted
sink(s4); // tainted
sink(s5.assign(10, c)); // tainted
sink(s5); // tainted
sink(s6.assign(s1));
sink(s6); // [FALSE POSITIVE]
}
void test_string_insert() {
std::string s1("hello");
std::string s2(source());
char c = ns_char::source();
std::string s3, s4, s5, s6;
s3 = s1;
sink(s3.insert(0, s1));
sink(s3);
s4 = s2;
sink(s4.insert(0, s1)); // tainted
sink(s4); // tainted
s5 = s1;
sink(s5.insert(0, s2)); // tainted
sink(s5); // tainted
s6 = s1;
sink(s6.insert(0, 10, c)); // tainted
sink(s6); // tainted
}
void test_string_replace() {
std::string s1("hello");
std::string s2(source());
char c = ns_char::source();
std::string s3, s4, s5, s6;
s3 = s1;
sink(s3.replace(1, 2, s1));
sink(s3);
s4 = s2;
sink(s4.replace(1, 2, s1)); // tainted
sink(s4); // tainted
s5 = s1;
sink(s5.replace(1, 2, s2)); // tainted
sink(s5); // tainted
s6 = s1;
sink(s6.replace(1, 2, 10, c)); // tainted
sink(s6); // tainted
}
void test_string_copy() {
char b1[1024] = {0};
char b2[1024] = {0};
std::string s1("hello");
std::string s2(source());
s1.copy(b1, s1.length(), 0);
sink(b1);
s2.copy(b2, s1.length(), 0);
sink(b2); // tainted
}
void test_string_swap() {
std::string s1("hello");
std::string s2(source());
std::string s3("world");
std::string s4(source());
sink(s1);
sink(s2); // tainted
sink(s3);
sink(s4); // tainted
s1.swap(s2);
s4.swap(s3);
sink(s1); // tainted
sink(s2); // [FALSE POSITIVE]
sink(s3); // tainted
sink(s4); // [FALSE POSITIVE]
}
void test_string_clear() {
std::string s1(source());
std::string s2(source());
std::string s3(source());
sink(s1); // tainted
sink(s2); // tainted
sink(s3); // tainted
s1.clear();
s2 = "";
s3 = s3;
sink(s1); // [FALSE POSITIVE]
sink(s2);
sink(s3); // tainted
}
void test_string_data()
{
std::string a("123");
std::string b(source());
sink(a.data());
sink(b.data()); // tainted
sink(a.length());
sink(b.length());
}
void test_string_substr()
{
std::string a("123");
std::string b(source());
sink(a.substr(0, a.length()));
sink(b.substr(0, b.length())); // tainted
}

View File

@@ -0,0 +1,45 @@
#include "stl.h"
using namespace std;
char *source();
void sink(const std::string &s) {};
void sink(const std::stringstream &s) {};
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); // tainted [NOT DETECTED]
sink(ss3); // tainted [NOT DETECTED]
sink(ss4); // tainted [NOT DETECTED]
sink(ss5); // tainted [NOT DETECTED]
sink(ss1.str());
sink(ss2.str()); // tainted [NOT DETECTED]
sink(ss3.str()); // tainted [NOT DETECTED]
sink(ss4.str()); // tainted [NOT DETECTED]
sink(ss5.str()); // tainted [NOT DETECTED]
}
void test_stringstream_int(int source)
{
std::stringstream ss1, ss2;
ss1 << 1234;
ss2 << source;
sink(ss1);
sink(ss2); // tainted [NOT DETECTED]
sink(ss1.str());
sink(ss2.str()); // tainted [NOT DETECTED]
}

View File

@@ -32,27 +32,65 @@
| movableclass.cpp:55:8:55:9 | s2 | movableclass.cpp:52:23:52:28 | call to source |
| movableclass.cpp:64:8:64:9 | s2 | movableclass.cpp:23:55:23:60 | call to source |
| movableclass.cpp:65:11:65:11 | call to operator= | movableclass.cpp:65:13:65:18 | call to source |
| stl.cpp:101:7:101:7 | a | stl.cpp:97:12:97:17 | call to source |
| stl.cpp:103:7:103:7 | c | stl.cpp:99:16:99:21 | call to source |
| stl.cpp:105:9:105:13 | call to c_str | stl.cpp:99:16:99:21 | call to source |
| stl.cpp:155:13:155:17 | call to c_str | stl.cpp:147:10:147:15 | call to source |
| stl.cpp:159:13:159:17 | call to c_str | stl.cpp:147:10:147:15 | call to source |
| stl.cpp:162:13:162:17 | call to c_str | stl.cpp:147:10:147:15 | call to source |
| stl.cpp:172:7:172:8 | cs | stl.cpp:167:19:167:24 | call to source |
| stl.cpp:173:7:173:8 | ss | stl.cpp:167:19:167:24 | call to source |
| stl.cpp:186:7:186:8 | cs | stl.cpp:178:19:178:24 | call to source |
| stl.cpp:187:7:187:8 | ss | stl.cpp:178:19:178:24 | call to source |
| stl.cpp:209:8:209:9 | s1 | stl.cpp:204:18:204:23 | call to source |
| stl.cpp:210:8:210:9 | s2 | stl.cpp:205:20:205:25 | call to source |
| stl.cpp:211:8:211:9 | s3 | stl.cpp:207:8:207:13 | call to source |
| stl.cpp:230:8:230:9 | s1 | stl.cpp:226:32:226:37 | call to source |
| stl.cpp:231:8:231:9 | s2 | stl.cpp:228:20:228:25 | call to source |
| stl.cpp:240:8:240:8 | c | stl.cpp:238:16:238:21 | call to source |
| stl.cpp:248:8:248:8 | c | stl.cpp:238:16:238:21 | call to source |
| stl.cpp:253:8:253:8 | c | stl.cpp:251:28:251:33 | call to source |
| stl.cpp:295:8:295:8 | x | stl.cpp:288:43:288:49 | source1 |
| stl.cpp:303:8:303:8 | x | stl.cpp:288:43:288:49 | source1 |
| stl.cpp:308:8:308:8 | x | stl.cpp:288:43:288:49 | source1 |
| string.cpp:28:7:28:7 | a | string.cpp:24:12:24:17 | call to source |
| string.cpp:30:7:30:7 | c | string.cpp:26:16:26:21 | call to source |
| string.cpp:32:9:32:13 | call to c_str | string.cpp:26:16:26:21 | call to source |
| string.cpp:38:13:38:17 | call to c_str | string.cpp:14:10:14:15 | call to source |
| string.cpp:42:13:42:17 | call to c_str | string.cpp:14:10:14:15 | call to source |
| string.cpp:45:13:45:17 | call to c_str | string.cpp:14:10:14:15 | call to source |
| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:24 | call to source |
| string.cpp:56:7:56:8 | ss | string.cpp:50:19:50:24 | call to source |
| string.cpp:69:7:69:8 | cs | string.cpp:61:19:61:24 | call to source |
| string.cpp:70:7:70:8 | ss | string.cpp:61:19:61:24 | call to source |
| string.cpp:92:8:92:9 | s1 | string.cpp:87:18:87:23 | call to source |
| string.cpp:93:8:93:9 | s2 | string.cpp:88:20:88:25 | call to source |
| string.cpp:94:8:94:9 | s3 | string.cpp:90:8:90:13 | call to source |
| string.cpp:113:8:113:9 | s1 | string.cpp:109:32:109:37 | call to source |
| string.cpp:114:8:114:9 | s2 | string.cpp:111:20:111:25 | call to source |
| string.cpp:121:8:121:8 | c | string.cpp:119:16:119:21 | call to source |
| string.cpp:129:8:129:8 | c | string.cpp:119:16:119:21 | call to source |
| string.cpp:134:8:134:8 | c | string.cpp:132:28:132:33 | call to source |
| string.cpp:144:11:144:11 | call to operator+ | string.cpp:141:18:141:23 | call to source |
| string.cpp:145:11:145:11 | call to operator+ | string.cpp:141:18:141:23 | call to source |
| string.cpp:146:11:146:11 | call to operator+ | string.cpp:141:18:141:23 | call to source |
| string.cpp:149:11:149:11 | call to operator+ | string.cpp:149:13:149:18 | call to source |
| string.cpp:158:8:158:9 | s5 | string.cpp:154:18:154:23 | call to source |
| string.cpp:162:8:162:9 | s6 | string.cpp:154:18:154:23 | call to source |
| string.cpp:167:8:167:9 | s7 | string.cpp:165:9:165:14 | call to source |
| string.cpp:171:8:171:9 | s8 | string.cpp:154:18:154:23 | call to source |
| string.cpp:176:8:176:9 | s9 | string.cpp:174:13:174:18 | call to source |
| string.cpp:184:8:184:10 | s10 | string.cpp:181:12:181:26 | call to source |
| string.cpp:198:10:198:15 | call to assign | string.cpp:190:17:190:22 | call to source |
| string.cpp:199:7:199:8 | s4 | string.cpp:190:17:190:22 | call to source |
| string.cpp:201:10:201:15 | call to assign | string.cpp:191:11:191:25 | call to source |
| string.cpp:202:7:202:8 | s5 | string.cpp:191:11:191:25 | call to source |
| string.cpp:205:7:205:8 | s6 | string.cpp:193:17:193:22 | call to source |
| string.cpp:219:10:219:15 | call to insert | string.cpp:210:17:210:22 | call to source |
| string.cpp:220:7:220:8 | s4 | string.cpp:210:17:210:22 | call to source |
| string.cpp:223:10:223:15 | call to insert | string.cpp:210:17:210:22 | call to source |
| string.cpp:224:7:224:8 | s5 | string.cpp:210:17:210:22 | call to source |
| string.cpp:227:10:227:15 | call to insert | string.cpp:211:11:211:25 | call to source |
| string.cpp:228:7:228:8 | s6 | string.cpp:211:11:211:25 | call to source |
| string.cpp:242:10:242:16 | call to replace | string.cpp:233:17:233:22 | call to source |
| string.cpp:243:7:243:8 | s4 | string.cpp:233:17:233:22 | call to source |
| string.cpp:246:10:246:16 | call to replace | string.cpp:233:17:233:22 | call to source |
| string.cpp:247:7:247:8 | s5 | string.cpp:233:17:233:22 | call to source |
| string.cpp:250:10:250:16 | call to replace | string.cpp:234:11:234:25 | call to source |
| string.cpp:251:7:251:8 | s6 | string.cpp:234:11:234:25 | call to source |
| string.cpp:264:7:264:8 | b2 | string.cpp:258:17:258:22 | call to source |
| string.cpp:274:7:274:8 | s2 | string.cpp:269:17:269:22 | call to source |
| string.cpp:276:7:276:8 | s4 | string.cpp:271:17:271:22 | call to source |
| string.cpp:281:7:281:8 | s1 | string.cpp:269:17:269:22 | call to source |
| string.cpp:282:7:282:8 | s2 | string.cpp:269:17:269:22 | call to source |
| string.cpp:283:7:283:8 | s3 | string.cpp:271:17:271:22 | call to source |
| string.cpp:284:7:284:8 | s4 | string.cpp:271:17:271:22 | call to source |
| string.cpp:292:7:292:8 | s1 | string.cpp:288:17:288:22 | call to source |
| string.cpp:293:7:293:8 | s2 | string.cpp:289:17:289:22 | call to source |
| string.cpp:294:7:294:8 | s3 | string.cpp:290:17:290:22 | call to source |
| string.cpp:300:7:300:8 | s1 | string.cpp:288:17:288:22 | call to source |
| string.cpp:302:7:302:8 | s3 | string.cpp:290:17:290:22 | call to source |
| string.cpp:311:9:311:12 | call to data | string.cpp:308:16:308:21 | call to source |
| string.cpp:322:9:322:14 | call to substr | string.cpp:319:16:319:21 | call to source |
| structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source |
| structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source |
| structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source |
@@ -159,3 +197,21 @@
| taint.cpp:470:7:470:7 | x | taint.cpp:462:6:462:11 | call to source |
| taint.cpp:471:7:471:7 | y | taint.cpp:462:6:462:11 | call to source |
| taint.cpp:485:7:485:10 | line | taint.cpp:480:26:480:32 | source1 |
| vector.cpp:20:8:20:8 | x | vector.cpp:16:43:16:49 | source1 |
| vector.cpp:28:8:28:8 | x | vector.cpp:16:43:16:49 | source1 |
| vector.cpp:33:8:33:8 | x | vector.cpp:16:43:16:49 | source1 |
| vector.cpp:70:7:70:8 | v5 | vector.cpp:69:15:69:20 | call to source |
| vector.cpp:71:10:71:14 | call to front | vector.cpp:69:15:69:20 | call to source |
| vector.cpp:72:10:72:13 | call to back | vector.cpp:69:15:69:20 | call to source |
| vector.cpp:109:7:109:8 | v1 | vector.cpp:106:15:106:20 | call to source |
| vector.cpp:112:7:112:8 | v4 | vector.cpp:107:15:107:20 | call to source |
| vector.cpp:117:7:117:8 | v1 | vector.cpp:106:15:106:20 | call to source |
| vector.cpp:118:7:118:8 | v2 | vector.cpp:106:15:106:20 | call to source |
| vector.cpp:119:7:119:8 | v3 | vector.cpp:107:15:107:20 | call to source |
| vector.cpp:120:7:120:8 | v4 | vector.cpp:107:15:107:20 | call to source |
| vector.cpp:130:7:130:8 | v1 | vector.cpp:126:15:126:20 | call to source |
| vector.cpp:131:7:131:8 | v2 | vector.cpp:127:15:127:20 | call to source |
| vector.cpp:132:7:132:8 | v3 | vector.cpp:128:15:128:20 | call to source |
| vector.cpp:139:7:139:8 | v1 | vector.cpp:126:15:126:20 | call to source |
| vector.cpp:140:7:140:8 | v2 | vector.cpp:127:15:127:20 | call to source |
| vector.cpp:141:7:141:8 | v3 | vector.cpp:128:15:128:20 | call to source |

View File

@@ -30,26 +30,64 @@
| movableclass.cpp:55:8:55:9 | movableclass.cpp:52:23:52:28 | AST only |
| movableclass.cpp:64:8:64:9 | movableclass.cpp:23:55:23:60 | AST only |
| movableclass.cpp:65:11:65:11 | movableclass.cpp:65:13:65:18 | AST only |
| stl.cpp:103:7:103:7 | stl.cpp:99:16:99:21 | AST only |
| stl.cpp:105:9:105:13 | stl.cpp:99:16:99:21 | AST only |
| stl.cpp:155:13:155:17 | stl.cpp:147:10:147:15 | AST only |
| stl.cpp:159:13:159:17 | stl.cpp:147:10:147:15 | AST only |
| stl.cpp:162:13:162:17 | stl.cpp:147:10:147:15 | AST only |
| stl.cpp:172:7:172:8 | stl.cpp:167:19:167:26 | IR only |
| stl.cpp:173:7:173:8 | stl.cpp:167:19:167:24 | AST only |
| stl.cpp:186:7:186:8 | stl.cpp:178:19:178:24 | AST only |
| stl.cpp:187:7:187:8 | stl.cpp:178:19:178:24 | AST only |
| stl.cpp:209:8:209:9 | stl.cpp:204:18:204:23 | AST only |
| stl.cpp:210:8:210:9 | stl.cpp:205:20:205:25 | AST only |
| stl.cpp:211:8:211:9 | stl.cpp:207:8:207:13 | AST only |
| stl.cpp:230:8:230:9 | stl.cpp:226:32:226:37 | AST only |
| stl.cpp:231:8:231:9 | stl.cpp:228:20:228:25 | AST only |
| stl.cpp:240:8:240:8 | stl.cpp:238:16:238:21 | AST only |
| stl.cpp:248:8:248:8 | stl.cpp:238:16:238:21 | AST only |
| stl.cpp:253:8:253:8 | stl.cpp:251:28:251:33 | AST only |
| stl.cpp:295:8:295:8 | stl.cpp:288:43:288:49 | AST only |
| stl.cpp:303:8:303:8 | stl.cpp:288:43:288:49 | AST only |
| stl.cpp:308:8:308:8 | stl.cpp:288:43:288:49 | AST only |
| string.cpp:30:7:30:7 | string.cpp:26:16:26:21 | AST only |
| string.cpp:32:9:32:13 | string.cpp:26:16:26:21 | AST only |
| string.cpp:38:13:38:17 | string.cpp:14:10:14:15 | AST only |
| string.cpp:42:13:42:17 | string.cpp:14:10:14:15 | AST only |
| string.cpp:45:13:45:17 | string.cpp:14:10:14:15 | AST only |
| string.cpp:55:7:55:8 | string.cpp:50:19:50:26 | IR only |
| string.cpp:56:7:56:8 | string.cpp:50:19:50:24 | AST only |
| string.cpp:69:7:69:8 | string.cpp:61:19:61:24 | AST only |
| string.cpp:70:7:70:8 | string.cpp:61:19:61:24 | AST only |
| string.cpp:92:8:92:9 | string.cpp:87:18:87:23 | AST only |
| string.cpp:93:8:93:9 | string.cpp:88:20:88:25 | AST only |
| string.cpp:94:8:94:9 | string.cpp:90:8:90:13 | AST only |
| string.cpp:113:8:113:9 | string.cpp:109:32:109:37 | AST only |
| string.cpp:114:8:114:9 | string.cpp:111:20:111:25 | AST only |
| string.cpp:121:8:121:8 | string.cpp:119:16:119:21 | AST only |
| string.cpp:129:8:129:8 | string.cpp:119:16:119:21 | AST only |
| string.cpp:134:8:134:8 | string.cpp:132:28:132:33 | AST only |
| string.cpp:144:11:144:11 | string.cpp:141:18:141:23 | AST only |
| string.cpp:145:11:145:11 | string.cpp:141:18:141:23 | AST only |
| string.cpp:146:11:146:11 | string.cpp:141:18:141:23 | AST only |
| string.cpp:149:11:149:11 | string.cpp:149:13:149:18 | AST only |
| string.cpp:158:8:158:9 | string.cpp:154:18:154:23 | AST only |
| string.cpp:162:8:162:9 | string.cpp:154:18:154:23 | AST only |
| string.cpp:167:8:167:9 | string.cpp:165:9:165:14 | AST only |
| string.cpp:171:8:171:9 | string.cpp:154:18:154:23 | AST only |
| string.cpp:176:8:176:9 | string.cpp:174:13:174:18 | AST only |
| string.cpp:184:8:184:10 | string.cpp:181:12:181:26 | AST only |
| string.cpp:198:10:198:15 | string.cpp:190:17:190:22 | AST only |
| string.cpp:199:7:199:8 | string.cpp:190:17:190:22 | AST only |
| string.cpp:201:10:201:15 | string.cpp:191:11:191:25 | AST only |
| string.cpp:202:7:202:8 | string.cpp:191:11:191:25 | AST only |
| string.cpp:205:7:205:8 | string.cpp:193:17:193:22 | AST only |
| string.cpp:219:10:219:15 | string.cpp:210:17:210:22 | AST only |
| string.cpp:220:7:220:8 | string.cpp:210:17:210:22 | AST only |
| string.cpp:223:10:223:15 | string.cpp:210:17:210:22 | AST only |
| string.cpp:224:7:224:8 | string.cpp:210:17:210:22 | AST only |
| string.cpp:227:10:227:15 | string.cpp:211:11:211:25 | AST only |
| string.cpp:228:7:228:8 | string.cpp:211:11:211:25 | AST only |
| string.cpp:242:10:242:16 | string.cpp:233:17:233:22 | AST only |
| string.cpp:243:7:243:8 | string.cpp:233:17:233:22 | AST only |
| string.cpp:246:10:246:16 | string.cpp:233:17:233:22 | AST only |
| string.cpp:247:7:247:8 | string.cpp:233:17:233:22 | AST only |
| string.cpp:250:10:250:16 | string.cpp:234:11:234:25 | AST only |
| string.cpp:251:7:251:8 | string.cpp:234:11:234:25 | AST only |
| string.cpp:264:7:264:8 | string.cpp:258:17:258:22 | AST only |
| string.cpp:274:7:274:8 | string.cpp:269:17:269:22 | AST only |
| string.cpp:276:7:276:8 | string.cpp:271:17:271:22 | AST only |
| string.cpp:281:7:281:8 | string.cpp:269:17:269:22 | AST only |
| string.cpp:282:7:282:8 | string.cpp:269:17:269:22 | AST only |
| string.cpp:283:7:283:8 | string.cpp:271:17:271:22 | AST only |
| string.cpp:284:7:284:8 | string.cpp:271:17:271:22 | AST only |
| string.cpp:292:7:292:8 | string.cpp:288:17:288:22 | AST only |
| string.cpp:293:7:293:8 | string.cpp:289:17:289:22 | AST only |
| string.cpp:294:7:294:8 | string.cpp:290:17:290:22 | AST only |
| string.cpp:300:7:300:8 | string.cpp:288:17:288:22 | AST only |
| string.cpp:302:7:302:8 | string.cpp:290:17:290:22 | AST only |
| string.cpp:311:9:311:12 | string.cpp:308:16:308:21 | AST only |
| string.cpp:322:9:322:14 | string.cpp:319:16:319:21 | AST only |
| structlikeclass.cpp:35:8:35:9 | structlikeclass.cpp:29:22:29:27 | AST only |
| structlikeclass.cpp:36:8:36:9 | structlikeclass.cpp:30:24:30:29 | AST only |
| structlikeclass.cpp:37:8:37:9 | structlikeclass.cpp:29:22:29:27 | AST only |
@@ -94,3 +132,21 @@
| taint.cpp:446:7:446:7 | taint.cpp:445:14:445:28 | AST only |
| taint.cpp:447:9:447:17 | taint.cpp:445:14:445:28 | AST only |
| taint.cpp:471:7:471:7 | taint.cpp:462:6:462:11 | AST only |
| vector.cpp:20:8:20:8 | vector.cpp:16:43:16:49 | AST only |
| vector.cpp:28:8:28:8 | vector.cpp:16:43:16:49 | AST only |
| vector.cpp:33:8:33:8 | vector.cpp:16:43:16:49 | AST only |
| vector.cpp:70:7:70:8 | vector.cpp:69:15:69:20 | AST only |
| vector.cpp:71:10:71:14 | vector.cpp:69:15:69:20 | AST only |
| vector.cpp:72:10:72:13 | vector.cpp:69:15:69:20 | AST only |
| vector.cpp:109:7:109:8 | vector.cpp:106:15:106:20 | AST only |
| vector.cpp:112:7:112:8 | vector.cpp:107:15:107:20 | AST only |
| vector.cpp:117:7:117:8 | vector.cpp:106:15:106:20 | AST only |
| vector.cpp:118:7:118:8 | vector.cpp:106:15:106:20 | AST only |
| vector.cpp:119:7:119:8 | vector.cpp:107:15:107:20 | AST only |
| vector.cpp:120:7:120:8 | vector.cpp:107:15:107:20 | AST only |
| vector.cpp:130:7:130:8 | vector.cpp:126:15:126:20 | AST only |
| vector.cpp:131:7:131:8 | vector.cpp:127:15:127:20 | AST only |
| vector.cpp:132:7:132:8 | vector.cpp:128:15:128:20 | AST only |
| vector.cpp:139:7:139:8 | vector.cpp:126:15:126:20 | AST only |
| vector.cpp:140:7:140:8 | vector.cpp:127:15:127:20 | AST only |
| vector.cpp:141:7:141:8 | vector.cpp:128:15:128:20 | AST only |

View File

@@ -1,10 +1,10 @@
| format.cpp:157:7:157:22 | (int)... | format.cpp:147:12:147:25 | call to source |
| format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source |
| format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source |
| stl.cpp:101:7:101:7 | (const char *)... | stl.cpp:97:12:97:17 | call to source |
| stl.cpp:101:7:101:7 | a | stl.cpp:97:12:97:17 | call to source |
| stl.cpp:172:7:172:8 | cs | stl.cpp:167:19:167:24 | call to source |
| stl.cpp:172:7:172:8 | cs | stl.cpp:167:19:167:26 | (const char *)... |
| string.cpp:28:7:28:7 | (const char *)... | string.cpp:24:12:24:17 | call to source |
| string.cpp:28:7:28:7 | a | string.cpp:24:12:24:17 | call to source |
| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:24 | call to source |
| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:26 | (const char *)... |
| structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source |
| structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source |
| structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source |

View File

@@ -0,0 +1,143 @@
#include "stl.h"
using namespace std;
int source();
namespace ns_char
{
char source();
}
void sink(int);
void sink(std::vector<int> &);
void test_range_based_for_loop_vector(int source1) {
std::vector<int> v(100, source1);
for(int x : v) {
sink(x); // tainted [NOT DETECTED by IR]
}
for(std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
sink(*it); // tainted [NOT DETECTED]
}
for(int& x : v) {
sink(x); // tainted [NOT DETECTED by IR]
}
const std::vector<int> const_v(100, source1);
for(const int& x : const_v) {
sink(x); // tainted [NOT DETECTED by IR]
}
}
void test_element_taint(int x) {
std::vector<int> v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10), v9(10);
v1[0] = 0;
v1[1] = 0;
v1[x] = 0;
v1.push_back(1);
sink(v1);
sink(v1[0]);
sink(v1[1]);
sink(v1[x]);
sink(v1.front());
sink(v1.back());
v2[0] = source();
sink(v2); // tainted [NOT DETECTED]
sink(v2[0]); // tainted [NOT DETECTED]
sink(v2[1]);
sink(v2[x]); // potentially tainted
v3 = v2;
sink(v3); // tainted [NOT DETECTED]
sink(v3[0]); // tainted [NOT DETECTED]
sink(v3[1]);
sink(v3[x]); // potentially tainted
v4[x] = source();
sink(v4); // tainted [NOT DETECTED]
sink(v4[0]); // potentially tainted
sink(v4[1]); // potentially tainted
sink(v4[x]); // tainted [NOT DETECTED]
v5.push_back(source());
sink(v5); // tainted
sink(v5.front()); // [FALSE POSITIVE]
sink(v5.back()); // tainted
v6.data()[2] = source();
sink(v6); // tainted [NOT DETECTED]
sink(v6.data()[2]); // tainted [NOT DETECTED]
{
const std::vector<int> &v7c = v7; // (workaround because our iterators don't convert to const_iterator)
std::vector<int>::const_iterator it = v7c.begin();
v7.insert(it, source());
}
sink(v7); // tainted [NOT DETECTED]
sink(v7.front()); // tainted [NOT DETECTED]
sink(v7.back());
{
const std::vector<int> &v8c = v8;
std::vector<int>::const_iterator it = v8c.begin();
v8.insert(it, 10, ns_char::source());
}
sink(v8); // tainted [NOT DETECTED]
sink(v8.front()); // tainted [NOT DETECTED]
sink(v8.back());
v9.at(x) = source();
sink(v9); // tainted [NOT DETECTED]
sink(v9.at(0)); // potentially tainted
sink(v9.at(1)); // potentially tainted
sink(v9.at(x)); // tainted [NOT DETECTED]
}
void test_vector_swap() {
std::vector<int> v1(10), v2(10), v3(10), v4(10);
v1.push_back(source());
v4.push_back(source());
sink(v1); // tainted
sink(v2);
sink(v3);
sink(v4); // tainted
v1.swap(v2);
v3.swap(v4);
sink(v1); // [FALSE POSITIVE]
sink(v2); // tainted
sink(v3); // tainted
sink(v4); // [FALSE POSITIVE]
}
void test_vector_clear() {
std::vector<int> v1(10), v2(10), v3(10), v4(10);
v1.push_back(source());
v2.push_back(source());
v3.push_back(source());
sink(v1); // tainted
sink(v2); // tainted
sink(v3); // tainted
sink(v4);
v1.clear();
v2 = v2;
v3 = v4;
sink(v1); // [FALSE POSITIVE]
sink(v2); // tainted
sink(v3); // [FALSE POSITIVE]
sink(v4);
}

View File

@@ -291,7 +291,7 @@ struct A {
Point *NewAliasing(int x) {
Point* p = new Point;
Point* q = new Point;
int j = new A(new A(x))->i;
int j = (new A(new A(x)))->i;
A* a = new A;
return p;
}
@@ -310,4 +310,4 @@ class ThisAliasTest {
void setX(int arg) {
this->x = arg;
}
};
};

View File

@@ -1,4 +1,4 @@
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
from VariableAccess expr
select expr, lowerBound(expr)
select expr, lowerBound(expr).toString()

View File

@@ -422,7 +422,7 @@ void test17() {
out(i); // 50
i = 20 + (j -= 10);
out(i); // 60 [BUG: the analysis thinks it's 2^-31 .. 2^31-1]
out(i); // 60
}
// Tests for unsigned multiplication.
@@ -463,3 +463,73 @@ int test_unsigned_mult02(unsigned b) {
return total;
}
unsigned long mult_rounding() {
unsigned long x, y, xy;
x = y = 1000000003UL; // 1e9 + 3
xy = x * y;
return xy; // BUG: upper bound should be >= 1000000006000000009UL
}
unsigned long mult_overflow() {
unsigned long x, y, xy;
x = 274177UL;
y = 67280421310721UL;
xy = x * y;
return xy; // BUG: upper bound should be >= 18446744073709551617UL
}
unsigned long mult_lower_bound(unsigned int ui, unsigned long ul) {
if (ui >= 10) {
unsigned long result = (unsigned long)ui * ui;
return result; // BUG: upper bound should be >= 18446744065119617025
}
if (ul >= 10) {
unsigned long result = ul * ul;
return result; // lower bound is correctly 0 (overflow is possible)
}
return 0;
}
unsigned long mul_assign(unsigned int ui) {
if (ui <= 10 && ui >= 2) {
ui *= ui + 0;
return ui; // 4 .. 100
}
unsigned int uiconst = 10;
uiconst *= 4;
unsigned long ulconst = 10;
ulconst *= 4;
return uiconst + ulconst; // 40 .. 40 for both
}
int mul_by_constant(int i, int j) {
if (i >= -1 && i <= 2) {
i = 5 * i;
out(i); // -5 .. 10
i = i * -3;
out(i); // -30 .. 15
i *= 7;
out(i); // -210 .. 105
i *= -11;
out(i); // -1155 .. 2310
}
if (i == -1) {
i = i * (int)0xffFFffFF; // fully converted literal is -1
out(i); // 1 .. 1
}
i = i * -1;
out( i); // -2^31 .. 2^31-1
signed char sc = 1;
i = (*&sc *= 2);
out(sc); // demonstrate that we couldn't analyze the LHS of the `*=` above...
out(i); // -128 .. 127 // ... but we can still bound its result by its type.
return 0;
}

View File

@@ -1,4 +1,4 @@
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
from VariableAccess expr
select expr, upperBound(expr)
select expr, upperBound(expr).toString()

View File

@@ -385,10 +385,39 @@ void bitwise_ands()
void unsigned_mult(unsigned int x, unsigned int y) {
if(x < 13 && y < 35) {
if(x * y > 1024) {} // always false [NOT DETECTED]
if(x * y > 1024) {} // always false
if(x * y < 204) {}
if(x >= 3 && y >= 2) {
if(x * y < 5) {} // always false [NOT DETECTED]
if(x * y < 5) {} // always false
}
}
}
}
void mult_rounding() {
unsigned long x, y, xy;
x = y = 1000000003UL; // 1e9 + 3
xy = 1000000006000000009UL; // x * y, precisely
// Even though the range analysis wrongly considers x*y to be xy - 9, there
// are no PointlessComparison false positives in these tests because alerts
// are suppressed when ulp() < 1, which roughly means that the number is
// larger than 2^53.
if (x * y < xy) {} // always false [NOT DETECTED]
if (x * y > xy) {} // always false [NOT DETECTED]
}
void mult_overflow() {
unsigned long x, y;
// The following two numbers multiply to 2^64 + 1, which is 1 when truncated
// to 64-bit unsigned.
x = 274177UL;
y = 67280421310721UL;
if (x * y == 1) {} // always true [BUG: reported as always false]
// This bug appears to be caused by
// `RangeAnalysisUtils::typeUpperBound(unsigned long)` having a result of
// 2**64 + 384, making the range analysis think that the multiplication can't
// overflow. The correct `typeUpperBound` would be 2**64 - 1, but we can't
// represent that with a QL float or int. We could make `typeUpperBound`
// exclusive instead of inclusive, but there is no exclusive upper bound for
// floats.
}

View File

@@ -41,7 +41,10 @@
| PointlessComparison.c:372:6:372:16 | ... >= ... | Comparison is always true because ... >> ... >= 1. |
| PointlessComparison.c:373:6:373:16 | ... >= ... | Comparison is always false because ... >> ... <= 1. |
| PointlessComparison.c:383:6:383:17 | ... >= ... | Comparison is always false because ... & ... <= 2. |
| PointlessComparison.cpp:36:6:36:33 | ... >= ... | Comparison is always false because ... >> ... <= 9223372036854776000. |
| PointlessComparison.c:388:10:388:21 | ... > ... | Comparison is always false because ... * ... <= 408. |
| PointlessComparison.c:391:12:391:20 | ... < ... | Comparison is always false because ... * ... >= 6. |
| PointlessComparison.c:414:7:414:16 | ... == ... | Comparison is always false because ... * ... >= 18446744073709551616. |
| PointlessComparison.cpp:36:6:36:33 | ... >= ... | Comparison is always false because ... >> ... <= 9223372036854775808. |
| PointlessComparison.cpp:41:6:41:29 | ... >= ... | Comparison is always false because ... >> ... <= 140737488355327.5. |
| PointlessComparison.cpp:42:6:42:29 | ... >= ... | Comparison is always false because ... >> ... <= 140737488355327.5. |
| PointlessComparison.cpp:43:6:43:29 | ... >= ... | Comparison is always true because ... >> ... >= 140737488355327.5. |

View File

@@ -2,5 +2,8 @@
| test3.c:13:16:13:19 | * ... | $@ flows to here and is used in an expression which might overflow negatively. | test3.c:11:15:11:18 | argv | User-provided value |
| test4.cpp:13:17:13:20 | access to array | $@ flows to here and is used in an expression which might overflow negatively. | test4.cpp:9:13:9:16 | argv | User-provided value |
| test5.cpp:10:9:10:15 | call to strtoul | $@ flows to here and is used in an expression which might overflow. | test5.cpp:9:7:9:9 | buf | User-provided value |
| test5.cpp:17:6:17:27 | ... * ... | $@ flows to here and is used in an expression which might overflow. | test5.cpp:9:7:9:9 | buf | User-provided value |
| test5.cpp:19:6:19:13 | ... * ... | $@ flows to here and is used in an expression which might overflow. | test5.cpp:9:7:9:9 | buf | User-provided value |
| test.c:14:15:14:35 | ... * ... | $@ flows to here and is used in an expression which might overflow. | test.c:11:29:11:32 | argv | User-provided value |
| test.c:44:7:44:12 | ... -- | $@ flows to here and is used in an expression which might overflow negatively. | test.c:41:17:41:20 | argv | User-provided value |
| test.c:54:7:54:12 | ... -- | $@ flows to here and is used in an expression which might overflow negatively. | test.c:51:17:51:20 | argv | User-provided value |