Merge branch 'main' into python-add-typetracker

This commit is contained in:
Taus Brock-Nannestad
2020-08-27 17:05:26 +02:00
708 changed files with 33214 additions and 5768 deletions

View File

@@ -1,5 +1,6 @@
{ "provide": [ "*/ql/src/qlpack.yml",
"*/ql/test/qlpack.yml",
"*/ql/examples/qlpack.yml",
"*/upgrades/qlpack.yml",
"misc/legacy-support/*/qlpack.yml",
"misc/suite-helpers/qlpack.yml" ] }

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"omnisharp.autoStart": false
}

View File

@@ -28,27 +28,51 @@ The following changes in version 1.25 affect C# analysis in all applications.
such as `A<int>.B`, no longer are considered unbound generics. (Such nested types do,
however, still have relevant `.getSourceDeclaration()`s, for example `A<>.B`.)
* The data-flow library has been improved, which affects most security queries by potentially
adding more results. Flow through methods now takes nested field reads/writes into account.
For example, the library is able to track flow from `"taint"` to `Sink()` via the method
`GetF2F1()` in
```csharp
class C1
{
string F1;
}
adding more results:
- Flow through methods now takes nested field reads/writes into account.
For example, the library is able to track flow from `"taint"` to `Sink()` via the method
`GetF2F1()` in
```csharp
class C1
{
string F1;
}
class C2
{
C1 F2;
string GetF2F1() => F2.F1; // Nested field read
class C2
{
C1 F2;
void M()
{
F2 = new C1() { F1 = "taint" };
Sink(GetF2F1()); // NEW: "taint" reaches here
}
}
```
string GetF2F1() => F2.F1; // Nested field read
void M()
{
F2 = new C1() { F1 = "taint" };
Sink(GetF2F1()); // NEW: "taint" reaches here
}
}
```
- Flow through collections is now modeled precisely. For example, instead of modeling an array
store `a[i] = x` as a taint-step from `x` to `a`, we now model it as a data-flow step that
stores `x` into `a`. To get the value back out, a matching read step must be taken.
For source-code based data-flow analysis, the following constructs are modeled as stores into
collections:
- Direct array assignments, `a[i] = x`.
- Array initializers, `new [] { x }`.
- C# 6-style array initializers, `new C() { Array = { [i] = x } }`.
- Call arguments that match a `params` parameter, where the C# compiler creates an array under-the-hood.
- `yield return` statements.
The following source-code constructs read from a collection:
- Direct array reads, `a[i]`.
- `foreach` statements.
For calls out to library code, existing flow summaries have been refined to precisely
capture how they interact with collection contents. For example, a call to
`System.Collections.Generic.List<T>.Add(T)` stores the value of the argument into the
qualifier, and a call to `System.Collections.Generic.List<T>.get_Item(int)` (that is, an
indexer call) reads contents out of the qualifier. Moreover, the effect of
collection-clearing methods such as `System.Collections.Generic.List<T>.Clear()` is now
also modeled.
## Changes to autobuilder

View File

@@ -6,8 +6,10 @@
- [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
- [bluebird](http://bluebirdjs.com/)
- [express](https://www.npmjs.com/package/express)
- [execa](https://www.npmjs.com/package/execa)
- [fancy-log](https://www.npmjs.com/package/fancy-log)
- [fastify](https://www.npmjs.com/package/fastify)
- [foreground-child](https://www.npmjs.com/package/foreground-child)
- [fstream](https://www.npmjs.com/package/fstream)
- [jGrowl](https://github.com/stanlemon/jGrowl)
- [jQuery](https://jquery.com/)
@@ -17,6 +19,7 @@
- [mssql](https://www.npmjs.com/package/mssql)
- [mysql](https://www.npmjs.com/package/mysql)
- [npmlog](https://www.npmjs.com/package/npmlog)
- [opener](https://www.npmjs.com/package/opener)
- [pg](https://www.npmjs.com/package/pg)
- [sequelize](https://www.npmjs.com/package/sequelize)
- [spanner](https://www.npmjs.com/package/spanner)

View File

@@ -20,3 +20,4 @@ The following changes in version 1.25 affect Python analysis in all applications
## Changes to libraries
* Importing `semmle.python.web.HttpRequest` will no longer import `UntrustedStringKind` transitively. `UntrustedStringKind` is the most commonly used non-abstract subclass of `ExternalStringKind`. If not imported (by one mean or another), taint-tracking queries that concern `ExternalStringKind` will not produce any results. Please ensure such queries contain an explicit import (`import semmle.python.security.strings.Untrusted`).
* Added support for tainted f-strings.

View File

@@ -0,0 +1,25 @@
# Improvements to C/C++ analysis
The following changes in version 1.26 affect C/C++ analysis in all applications.
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
| Inconsistent direction of for loop (`cpp/inconsistent-loop-direction`) | Fewer false positive results | The query now accounts for intentional wrapping of an unsigned loop counter. |
| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | | The precision of this query has been decreased from "high" to "medium". As a result, the query is still run but results are no longer displayed on LGTM by default. |
| Comparison result is always the same (`cpp/constant-comparison`) | More correct results | Bounds on expressions involving multiplication can now be determined in more cases. |
## Changes to libraries
* The models library now models some taint flows through `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`.
* The models library now models many more taint flows through `std::string`.
* The `SimpleRangeAnalysis` library now supports multiplications of the form
`e1 * e2` and `x *= e2` when `e1` and `e2` are unsigned or constant.

View File

@@ -0,0 +1,29 @@
# Improvements to C# analysis
The following changes in version 1.26 affect C# analysis in all applications.
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
## Removal of old queries
## Changes to code extraction
* Partial method bodies are extracted. Previously, partial method bodies were skipped completely.
## Changes to libraries
## Changes to autobuilder
## Changes to tooling support
* The Abstract Syntax Tree of C# files can be printed in Visual Studio Code.

View File

@@ -0,0 +1,32 @@
# Improvements to JavaScript analysis
## General improvements
* Support for the following frameworks and libraries has been improved:
- [fast-json-stable-stringify](https://www.npmjs.com/package/fast-json-stable-stringify)
- [fast-safe-stringify](https://www.npmjs.com/package/fast-safe-stringify)
- [javascript-stringify](https://www.npmjs.com/package/javascript-stringify)
- [js-stringify](https://www.npmjs.com/package/js-stringify)
- [json-stable-stringify](https://www.npmjs.com/package/json-stable-stringify)
- [json-stringify-safe](https://www.npmjs.com/package/json-stringify-safe)
- [json3](https://www.npmjs.com/package/json3)
- [object-inspect](https://www.npmjs.com/package/object-inspect)
- [pretty-format](https://www.npmjs.com/package/pretty-format)
- [stringify-object](https://www.npmjs.com/package/stringify-object)
* Analyzing files with the ".cjs" extension is now supported.
## New queries
| **Query** | **Tags** | **Purpose** |
|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
| Incomplete URL substring sanitization (`js/incomplete-url-substring-sanitization`) | More results | This query now recognizes additional URLs when the substring check is an inclusion check. |
## Changes to libraries

View File

@@ -0,0 +1,3 @@
name: codeql-cpp-examples
version: 0.0.0
libraryPathDependencies: codeql-cpp

View File

@@ -50,7 +50,12 @@ predicate illDefinedDecrForStmt(
DataFlow::localFlowStep(DataFlow::exprNode(initialCondition), DataFlow::exprNode(lesserOperand)) and
// `initialCondition` < `terminalCondition`
(
upperBound(initialCondition) < lowerBound(terminalCondition)
upperBound(initialCondition) < lowerBound(terminalCondition) and
(
// exclude cases where the loop counter is `unsigned` (where wrapping behaviour can be used deliberately)
v.getUnspecifiedType().(IntegralType).isSigned() or
initialCondition.getValue().toInt() = 0
)
or
(forstmt.conditionAlwaysFalse() or forstmt.conditionAlwaysTrue())
)

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

@@ -9,3 +9,10 @@
tags contain:
- ide-contextual-queries/local-definitions
- ide-contextual-queries/local-references
- query: Metrics/Dependencies/ExternalDependencies.ql
- query: Metrics/Dependencies/ExternalDependenciesSourceLinks.ql
- query: Metrics/Files/FLinesOfCode.ql
- query: Metrics/Files/FLinesOfCommentedOutCode.ql
- query: Metrics/Files/FLinesOfComments.ql
- query: Metrics/Files/FLinesOfDuplicatedCode.ql
- query: Metrics/Files/FNumberOfTests.ql

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

@@ -214,6 +214,9 @@ abstract class ImplicitConversionFunction extends MemberFunction {
}
/**
* DEPRECATED: as of C++11 this class does not correspond perfectly with the
* language definition of a converting constructor.
*
* A C++ constructor that also defines an implicit conversion. For example the
* function `MyClass` in the following code is a `ConversionConstructor`:
* ```
@@ -225,15 +228,16 @@ abstract class ImplicitConversionFunction extends MemberFunction {
* };
* ```
*/
class ConversionConstructor extends Constructor, ImplicitConversionFunction {
deprecated class ConversionConstructor extends Constructor, ImplicitConversionFunction {
ConversionConstructor() {
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
not hasSpecifier("explicit") and
not this instanceof CopyConstructor
not hasSpecifier("explicit")
}
override string getAPrimaryQlClass() {
not this instanceof MoveConstructor and result = "ConversionConstructor"
not this instanceof CopyConstructor and
not this instanceof MoveConstructor and
result = "ConversionConstructor"
}
/** Gets the type this `ConversionConstructor` takes as input. */

View File

@@ -582,7 +582,7 @@ class TemplateVariable extends Variable {
* float a;
* }
*
* template<type T>
* template<typename T>
* void myTemplateFunction() {
* T b;
* }

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

@@ -484,6 +484,17 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
// Expr -> Expr
exprToExprStep_nocfg(nodeFrom.asExpr(), nodeTo.asExpr())
or
// Assignment -> LValue post-update node
//
// This is used for assignments whose left-hand side is not a variable
// assignment or a storeStep but is still modeled by other means. It could be
// a call to `operator*` or `operator[]` where taint should flow to the
// post-update node of the qualifier.
exists(AssignExpr assign |
nodeFrom.asExpr() = assign and
nodeTo.(PostUpdateNode).getPreUpdateNode().asExpr() = assign.getLValue()
)
or
// Node -> FlowVar -> VariableAccess
exists(FlowVar var |
(

View File

@@ -65,6 +65,15 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
// tracking. The flow from expression `x` into `x++` etc. is handled in the
// case above.
exprTo = DataFlow::getAnAccessToAssignedVariable(exprFrom.(PostfixCrementOperation))
or
// In `for (char c : s) { ... c ... }`, this rule propagates taint from `s`
// to `c`.
exists(RangeBasedForStmt rbf |
exprFrom = rbf.getRange() and
// It's guaranteed up to at least C++20 that the range-based for loop
// desugars to a variable with an initializer.
exprTo = rbf.getVariable().getInitializer().getExpr()
)
)
or
// Taint can flow through modeled functions
@@ -73,6 +82,19 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
exprToDefinitionByReferenceStep(nodeFrom.asExpr(), nodeTo.asDefiningArgument())
or
exprToPartialDefinitionStep(nodeFrom.asExpr(), nodeTo.asPartialDefinition())
or
// Reverse taint: taint that flows from the post-update node of a reference
// returned by a function call, back into the qualifier of that function.
// This allows taint to flow 'in' through references returned by a modeled
// function such as `operator[]`.
exists(TaintFunction f, Call call, FunctionInput inModel, FunctionOutput outModel |
call.getTarget() = f and
inModel.isReturnValueDeref() and
outModel.isQualifierObject() and
f.hasTaintFlow(inModel, outModel) and
nodeFrom.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = call and
nodeTo.asDefiningArgument() = call.getQualifier()
)
}
/**

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

@@ -699,13 +699,20 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)
*/
class BarrierGuard extends IRGuardCondition {
/** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */
abstract predicate checks(Instruction instr, boolean b);
predicate checksInstr(Instruction instr, boolean b) { none() }
/** Override this predicate to hold if this guard validates `expr` upon evaluating to `b`. */
predicate checks(Expr e, boolean b) { none() }
/** Gets a node guarded by this guard. */
final Node getAGuardedNode() {
exists(ValueNumber value, boolean edge |
(
this.checksInstr(value.getAnInstruction(), edge)
or
this.checks(value.getAnInstruction().getConvertedResultExpression(), edge)
) and
result.asInstruction() = value.getAnInstruction() and
this.checks(value.getAnInstruction(), edge) and
this.controls(result.asInstruction().getBlock(), edge)
)
}

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

@@ -7,9 +7,17 @@ import semmle.code.cpp.models.interfaces.DataFlow
import semmle.code.cpp.models.interfaces.Taint
/**
* Model for C++ conversion constructors.
* Model for C++ conversion constructors. As of C++11 this does not correspond
* perfectly with the language definition of a converting constructor, however,
* it does correspond with the constructors we are confident taint should flow
* through.
*/
class ConversionConstructorModel extends ConversionConstructor, TaintFunction {
class ConversionConstructorModel extends Constructor, TaintFunction {
ConversionConstructorModel() {
strictcount(Parameter p | p = getAParameter() and not p.hasInitializer()) = 1 and
not hasSpecifier("explicit")
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// taint flow from the first constructor argument to the returned object
input.isParameter(0) and

View File

@@ -0,0 +1,108 @@
/**
* 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", "deque", "list", "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", "push_front"]) or
this.hasQualifiedName("std", "list", ["push_back", "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", "back"]) or
this.hasQualifiedName("std", "vector", ["front", "back"]) or
this.hasQualifiedName("std", "deque", ["front", "back"]) or
this.hasQualifiedName("std", "list", ["front", "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", "vector", "deque", "list", "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()
}
}
/**
* The standard container functions `at` and `operator[]`.
*/
class StdSequenceContainerAt extends TaintFunction {
StdSequenceContainerAt() {
this.hasQualifiedName("std", ["vector", "array", "deque"], ["at", "operator[]"])
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to referenced return value
input.isQualifierObject() and
output.isReturnValueDeref()
or
// reverse flow from returned reference to the qualifier
input.isReturnValueDeref() and
output.isQualifierObject()
}
}

View File

@@ -1,10 +1,17 @@
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", "data"]) }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string itself (qualifier) to return value
@@ -12,3 +19,140 @@ 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+=", "append", "insert", "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()
}
}
/**
* The `std::string` functions `at` and `operator[]`.
*/
class StdStringAt extends TaintFunction {
StdStringAt() { this.hasQualifiedName("std", "basic_string", ["at", "operator[]"]) }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from qualifier to referenced return value
input.isQualifierObject() and
output.isReturnValueDeref()
or
// reverse flow from returned reference to the qualifier
input.isReturnValueDeref() and
output.isQualifierObject()
}
}

View File

@@ -10,7 +10,8 @@ private newtype TFunctionInput =
TInParameter(ParameterIndex i) or
TInParameterDeref(ParameterIndex i) or
TInQualifierObject() or
TInQualifierAddress()
TInQualifierAddress() or
TInReturnValueDeref()
/**
* An input to a function. This can be:
@@ -106,6 +107,31 @@ class FunctionInput extends TFunctionInput {
* (with type `C const *`) on entry to the function.
*/
predicate isQualifierAddress() { none() }
/**
* Holds if this is the input value pointed to by the return value of a
* function, if the function returns a pointer, or the input value referred
* to by the return value of a function, if the function returns a reference.
*
* Example:
* ```
* char* getPointer();
* float& getReference();
* int getInt();
* ```
* - `isReturnValueDeref()` holds for the `FunctionInput` that represents the
* value of `*getPointer()` (with type `char`).
* - `isReturnValueDeref()` holds for the `FunctionInput` that represents the
* value of `getReference()` (with type `float`).
* - There is no `FunctionInput` of `getInt()` for which
* `isReturnValueDeref()` holds because the return type of `getInt()` is
* neither a pointer nor a reference.
*
* Note that data flows in through function return values are relatively
* rare, but they do occur when a function returns a reference to itself,
* part of itself, or one of its other inputs.
*/
predicate isReturnValueDeref() { none() }
}
/**
@@ -199,6 +225,34 @@ class InQualifierAddress extends FunctionInput, TInQualifierAddress {
override predicate isQualifierAddress() { any() }
}
/**
* The input value pointed to by the return value of a function, if the
* function returns a pointer, or the input value referred to by the return
* value of a function, if the function returns a reference.
*
* Example:
* ```
* char* getPointer();
* float& getReference();
* int getInt();
* ```
* - `InReturnValueDeref` represents the value of `*getPointer()` (with type
* `char`).
* - `InReturnValueDeref` represents the value of `getReference()` (with type
* `float`).
* - `InReturnValueDeref` does not represent the return value of `getInt()`
* because the return type of `getInt()` is neither a pointer nor a reference.
*
* Note that data flows in through function return values are relatively
* rare, but they do occur when a function returns a reference to itself,
* part of itself, or one of its other inputs.
*/
class InReturnValueDeref extends FunctionInput, TInReturnValueDeref {
override string toString() { result = "InReturnValueDeref" }
override predicate isReturnValueDeref() { any() }
}
private newtype TFunctionOutput =
TOutParameterDeref(ParameterIndex i) or
TOutQualifierObject() or

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

@@ -2,6 +2,11 @@
* Provides a library for writing QL tests whose success or failure is based on expected results
* embedded in the test source code as comments, rather than a `.expected` file.
*
* To add this framework to a new language:
* - Add a file `InlineExpectationsTestPrivate.qll` that defines a `LineComment` class. This class
* must support a `getContents` method that returns the contents of the given comment, _excluding_
* the comment indicator itself. It should also define `toString` and `getLocation` as usual.
*
* To create a new inline expectations test:
* - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the
* new class, bind `this` to a unique string (usually the name of the test).
@@ -13,7 +18,7 @@
* `hasActualResult()`. Often this is just a single tag.
*
* Example:
* ```
* ```ql
* class ConstantValueTest extends InlineExpectationsTest {
* ConstantValueTest() { this = "ConstantValueTest" }
*
@@ -38,10 +43,10 @@
* There is no need to write a `select` clause or query predicate. All of the differences between
* expected results and actual results will be reported in the `failures()` query predicate.
*
* To annotate the test source code with an expected result, place a C++-style (`//`) comment on the
* To annotate the test source code with an expected result, place a comment on the
* same line as the expected result, with text of the following format as the body of the comment:
*
* `// $tag=expected-value`
* `$tag=expected-value`
*
* Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is
* the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be
@@ -53,7 +58,7 @@
* "Missing result: tag=expected-value".
*
* Example:
* ```
* ```cpp
* int i = x + 5; // $const=5
* int j = y + (7 - 3) // $const=7 $const=3 $const=4 // The result of the subtraction is a constant.
* ```
@@ -62,8 +67,8 @@
* annotate that a particular expected result is known to be a false positive, or that a particular
* missing result is known to be a false negative:
*
* `// $f+:tag=expected-value` // False positive
* `// $f-:tag=expected-value` // False negative
* `$f+:tag=expected-value` // False positive
* `$f-:tag=expected-value` // False negative
*
* A false positive expectation is treated as any other expected result, except that if there is no
* matching actual result, the message will be of the form "Fixed false positive: tag=value". A
@@ -74,14 +79,14 @@
* If the same result value is expected for two or more tags on the same line, there is a shorthand
* notation available:
*
* `// $tag1,tag2=expected-value`
* `$tag1,tag2=expected-value`
*
* is equivalent to:
*
* `// $tag1=expected-value $tag2=expected-value`
* `$tag1=expected-value $tag2=expected-value`
*/
import cpp
private import InlineExpectationsTestPrivate
/**
* Base class for tests with inline expectations. The test extends this class to provide the actual
@@ -150,12 +155,12 @@ abstract class InlineExpectationsTest extends string {
}
/**
* RegEx pattern to match a comment containing one or more expected results. The comment must be a
* C++-style (`//`) comment with `$` as its first non-whitespace character. Any subsequent character
* RegEx pattern to match a comment containing one or more expected results. The comment must have
* `$` as its first non-whitespace character. Any subsequent character
* is treated as part of the expected results, except that the comment may contain a `//` sequence
* to treat the remainder of the line as a regular (non-interpreted) comment.
*/
private string expectationCommentPattern() { result = "//\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" }
private string expectationCommentPattern() { result = "\\s*(\\$(?:[^/]|/[^/])*)(?://.*)?" }
/**
* RegEx pattern to match a single expected result, not including the leading `$`. It starts with an
@@ -166,7 +171,7 @@ private string expectationPattern() {
result = "(?:(f(?:\\+|-)):)?((?:[A-Za-z-_]+)(?:\\s*,\\s*[A-Za-z-_]+)*)(?:=(.*))?"
}
private string getAnExpectation(CppStyleComment comment) {
private string getAnExpectation(LineComment comment) {
result = comment.getContents().regexpCapture(expectationCommentPattern(), 1).splitAt("$").trim() and
result != ""
}
@@ -177,7 +182,7 @@ private newtype TFailureLocatable =
) {
test.hasActualResult(location, element, tag, value)
} or
TValidExpectation(CppStyleComment comment, string tag, string value, string knownFailure) {
TValidExpectation(LineComment comment, string tag, string value, string knownFailure) {
exists(string expectation |
expectation = getAnExpectation(comment) and
expectation.regexpMatch(expectationPattern()) and
@@ -194,7 +199,7 @@ private newtype TFailureLocatable =
)
)
} or
TInvalidExpectation(CppStyleComment comment, string expectation) {
TInvalidExpectation(LineComment comment, string expectation) {
expectation = getAnExpectation(comment) and
not expectation.regexpMatch(expectationPattern())
}
@@ -232,7 +237,7 @@ class ActualResult extends FailureLocatable, TActualResult {
}
abstract private class Expectation extends FailureLocatable {
CppStyleComment comment;
LineComment comment;
override string toString() { result = comment.toString() }

View File

@@ -0,0 +1,23 @@
import cpp
private newtype TLineComment = MkLineComment(CppStyleComment c)
/**
* Represents a line comment in the CPP style.
* Unlike the `CppStyleComment` class, however, the string returned by `getContents` does _not_
* include the preceding comment marker (`//`).
*/
class LineComment extends TLineComment {
CppStyleComment comment;
LineComment() { this = MkLineComment(comment) }
/** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */
string getContents() { result = comment.getContents().suffix(2) }
/** Gets a textual representation of this element. */
string toString() { result = comment.toString() }
/** Gets the location of this comment. */
Location getLocation() { result = comment.getLocation() }
}

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

@@ -10,7 +10,7 @@ import semmle.code.cpp.ir.IR
class TestBarrierGuard extends DataFlow::BarrierGuard {
TestBarrierGuard() { this.(CallInstruction).getStaticCallTarget().getName() = "guarded" }
override predicate checks(Instruction checked, boolean isTrue) {
override predicate checksInstr(Instruction checked, boolean isTrue) {
checked = this.(CallInstruction).getPositionalArgument(0) and
isTrue = true
}

View File

@@ -7,11 +7,14 @@
| example.c:17:19:17:22 | {...} | example.c:26:19:26:24 | coords |
| example.c:24:2:24:7 | coords [post update] | example.c:26:2:26:7 | coords |
| example.c:24:2:24:7 | coords [post update] | example.c:26:19:26:24 | coords |
| example.c:24:2:24:30 | ... = ... | example.c:24:9:24:9 | x [post update] |
| example.c:24:13:24:18 | coords [post update] | example.c:24:2:24:7 | coords |
| example.c:24:13:24:18 | coords [post update] | example.c:26:2:26:7 | coords |
| example.c:24:13:24:18 | coords [post update] | example.c:26:19:26:24 | coords |
| example.c:24:13:24:30 | ... = ... | example.c:24:2:24:30 | ... = ... |
| example.c:24:13:24:30 | ... = ... | example.c:24:20:24:20 | y [post update] |
| example.c:24:24:24:30 | ... + ... | example.c:24:13:24:30 | ... = ... |
| example.c:26:2:26:25 | ... = ... | example.c:26:9:26:9 | x [post update] |
| example.c:26:13:26:16 | call to getX | example.c:26:2:26:25 | ... = ... |
| example.c:26:18:26:24 | ref arg & ... | example.c:26:2:26:7 | coords |
| example.c:26:18:26:24 | ref arg & ... | example.c:26:19:26:24 | coords [inner post update] |

View File

@@ -1,204 +0,0 @@
typedef unsigned long size_t;
namespace std
{
template<class charT> struct char_traits;
typedef size_t streamsize;
template <class T> class allocator {
public:
allocator() throw();
};
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
class basic_string {
public:
explicit basic_string(const Allocator& a = Allocator());
basic_string(const charT* s, const Allocator& a = Allocator());
const charT* c_str() const;
};
typedef basic_string<char> string;
template <class charT, class traits = char_traits<charT> >
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
public:
basic_istream<charT,traits>& operator>>(int& n);
};
template <class charT, class traits = char_traits<charT> >
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
public:
typedef charT char_type;
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
basic_ostream<charT, traits>& operator<<(int n);
};
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
template<class charT, class traits = char_traits<charT>>
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
public:
};
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
class basic_stringstream : public basic_iostream<charT, traits> {
public:
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
basic_string<charT, traits, Allocator> str() const;
};
using stringstream = basic_stringstream<char>;
}
char *source();
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
}
}

View File

@@ -0,0 +1,176 @@
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:
using value_type = charT;
using reference = value_type&;
using const_reference = const value_type&;
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;
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
const_reference at(size_type n) const;
reference at(size_type n);
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,342 @@
#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
}
void test_string_at()
{
std::string a("123");
std::string b("123");
std::string c("123");
sink(a);
sink(b);
sink(c);
a[0] = ns_char::source();
b.at(0) = ns_char::source();
c[0] = a[0];
sink(a); // tainted
sink(b); // tainted
sink(c); // 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,21 +32,68 @@
| 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:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
| stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source |
| stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source |
| stl.cpp:125:13:125:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
| stl.cpp:129:13:129:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
| stl.cpp:132:13:132:17 | call to c_str | stl.cpp:117:10:117:15 | call to source |
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:24 | call to source |
| stl.cpp:143:7:143:8 | ss | stl.cpp:137:19:137:24 | call to source |
| stl.cpp:156:7:156:8 | cs | stl.cpp:148:19:148:24 | call to source |
| stl.cpp:157:7:157:8 | ss | stl.cpp:148:19:148:24 | call to source |
| stl.cpp:179:8:179:9 | s1 | stl.cpp:174:18:174:23 | call to source |
| stl.cpp:180:8:180:9 | s2 | stl.cpp:175:20:175:25 | call to source |
| stl.cpp:181:8:181:9 | s3 | stl.cpp:177:8:177:13 | call to source |
| stl.cpp:200:8:200:9 | s1 | stl.cpp:196:32:196:37 | call to source |
| stl.cpp:201:8:201:9 | s2 | stl.cpp:198:20:198:25 | call to source |
| 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 |
| string.cpp:339:7:339:7 | a | string.cpp:335:9:335:23 | call to source |
| string.cpp:340:7:340:7 | b | string.cpp:336:12:336:26 | call to source |
| string.cpp:341:7:341:7 | c | string.cpp:335:9:335:23 | 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 |
@@ -153,3 +200,40 @@
| 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:52:7:52:8 | v2 | vector.cpp:51:10:51:15 | call to source |
| vector.cpp:53:9:53:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source |
| vector.cpp:54:9:54:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source |
| vector.cpp:55:9:55:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source |
| vector.cpp:58:7:58:8 | v3 | vector.cpp:51:10:51:15 | call to source |
| vector.cpp:59:9:59:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source |
| vector.cpp:60:9:60:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source |
| vector.cpp:61:9:61:9 | call to operator[] | vector.cpp:51:10:51:15 | call to source |
| vector.cpp:64:7:64:8 | v4 | vector.cpp:63:10:63:15 | call to source |
| vector.cpp:65:9:65:9 | call to operator[] | vector.cpp:63:10:63:15 | call to source |
| vector.cpp:66:9:66:9 | call to operator[] | vector.cpp:63:10:63:15 | call to source |
| vector.cpp:67:9:67:9 | call to operator[] | vector.cpp:63:10:63:15 | call to source |
| 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:97:7:97:8 | v9 | vector.cpp:96:13:96:18 | call to source |
| vector.cpp:98:10:98:11 | call to at | vector.cpp:96:13:96:18 | call to source |
| vector.cpp:99:10:99:11 | call to at | vector.cpp:96:13:96:18 | call to source |
| vector.cpp:100:10:100:11 | call to at | vector.cpp:96:13:96:18 | 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 |
| vector.cpp:171:13:171:13 | call to operator[] | vector.cpp:170:14:170:19 | call to source |
| vector.cpp:180:13:180:13 | call to operator[] | vector.cpp:179:14:179:19 | call to source |
| vector.cpp:201:13:201:13 | call to operator[] | vector.cpp:200:14:200:19 | call to source |

View File

@@ -30,20 +30,67 @@
| 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:73:7:73:7 | stl.cpp:69:16:69:21 | AST only |
| stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only |
| stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only |
| stl.cpp:129:13:129:17 | stl.cpp:117:10:117:15 | AST only |
| stl.cpp:132:13:132:17 | stl.cpp:117:10:117:15 | AST only |
| stl.cpp:142:7:142:8 | stl.cpp:137:19:137:26 | IR only |
| stl.cpp:143:7:143:8 | stl.cpp:137:19:137:24 | AST only |
| stl.cpp:156:7:156:8 | stl.cpp:148:19:148:24 | AST only |
| stl.cpp:157:7:157:8 | stl.cpp:148:19:148:24 | AST only |
| stl.cpp:179:8:179:9 | stl.cpp:174:18:174:23 | AST only |
| stl.cpp:180:8:180:9 | stl.cpp:175:20:175:25 | AST only |
| stl.cpp:181:8:181:9 | stl.cpp:177:8:177:13 | AST only |
| stl.cpp:200:8:200:9 | stl.cpp:196:32:196:37 | AST only |
| stl.cpp:201:8:201:9 | stl.cpp:198:20:198:25 | 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 |
| string.cpp:339:7:339:7 | string.cpp:335:9:335:23 | AST only |
| string.cpp:340:7:340:7 | string.cpp:336:12:336:26 | AST only |
| string.cpp:341:7:341:7 | string.cpp:335:9:335:23 | 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 |
@@ -88,3 +135,41 @@
| 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:52:7:52:8 | vector.cpp:51:10:51:15 | AST only |
| vector.cpp:53:9:53:9 | vector.cpp:51:10:51:15 | AST only |
| vector.cpp:54:9:54:9 | vector.cpp:51:10:51:15 | AST only |
| vector.cpp:55:9:55:9 | vector.cpp:51:10:51:15 | AST only |
| vector.cpp:58:7:58:8 | vector.cpp:51:10:51:15 | AST only |
| vector.cpp:59:9:59:9 | vector.cpp:51:10:51:15 | AST only |
| vector.cpp:60:9:60:9 | vector.cpp:51:10:51:15 | AST only |
| vector.cpp:61:9:61:9 | vector.cpp:51:10:51:15 | AST only |
| vector.cpp:64:7:64:8 | vector.cpp:63:10:63:15 | AST only |
| vector.cpp:65:9:65:9 | vector.cpp:63:10:63:15 | AST only |
| vector.cpp:66:9:66:9 | vector.cpp:63:10:63:15 | AST only |
| vector.cpp:67:9:67:9 | vector.cpp:63:10:63:15 | 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:97:7:97:8 | vector.cpp:96:13:96:18 | AST only |
| vector.cpp:98:10:98:11 | vector.cpp:96:13:96:18 | AST only |
| vector.cpp:99:10:99:11 | vector.cpp:96:13:96:18 | AST only |
| vector.cpp:100:10:100:11 | vector.cpp:96:13:96:18 | 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 |
| vector.cpp:162:8:162:15 | vector.cpp:161:14:161:19 | IR only |
| vector.cpp:171:13:171:13 | vector.cpp:170:14:170:19 | AST only |
| vector.cpp:180:13:180:13 | vector.cpp:179:14:179:19 | AST only |
| vector.cpp:201:13:201:13 | vector.cpp:200:14:200:19 | 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:71:7:71:7 | (const char *)... | stl.cpp:67:12:67:17 | call to source |
| stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source |
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137:24 | call to source |
| stl.cpp:142:7:142:8 | cs | stl.cpp:137:19:137: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 |
@@ -79,3 +79,4 @@
| taint.cpp:465:7:465:7 | x | taint.cpp:462:6:462:11 | call to source |
| taint.cpp:470:7:470:7 | x | 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:162:8:162:15 | access to array | vector.cpp:161:14:161:19 | call to source |

View File

@@ -0,0 +1,214 @@
#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
sink(v2[0]); // tainted
sink(v2[1]); // [FALSE POSITIVE]
sink(v2[x]); // potentially tainted
v3 = v2;
sink(v3); // tainted
sink(v3[0]); // tainted
sink(v3[1]); // [FALSE POSITIVE]
sink(v3[x]); // potentially tainted
v4[x] = source();
sink(v4); // tainted
sink(v4[0]); // potentially tainted
sink(v4[1]); // potentially tainted
sink(v4[x]); // tainted
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
sink(v9.at(0)); // potentially tainted
sink(v9.at(1)); // potentially tainted
sink(v9.at(x)); // tainted
}
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);
}
struct MyPair
{
int a, b;
};
struct MyVectorContainer
{
std::vector<int> vs;
};
void test_nested_vectors()
{
{
int aa[10][20] = {0};
sink(aa[0][0]);
aa[0][0] = source();
sink(aa[0][0]); // tainted [IR ONLY]
}
{
std::vector<std::vector<int> > bb(30);
bb[0].push_back(0);
sink(bb[0][0]);
bb[0][0] = source();
sink(bb[0][0]); // tainted
}
{
std::vector<int> cc[40];
cc[0].push_back(0);
sink(cc[0][0]);
cc[0][0] = source();
sink(cc[0][0]); // tainted
}
{
std::vector<MyPair> dd;
MyPair mp = {0, 0};
dd.push_back(mp);
sink(dd[0].a);
sink(dd[0].b);
dd[0].a = source();
sink(dd[0].a); // tainted [NOT DETECTED]
sink(dd[0].b);
}
{
MyVectorContainer ee;
ee.vs.push_back(0);
sink(ee.vs[0]);
ee.vs[0] = source();
sink(ee.vs[0]); // tainted
}
{
std::vector<MyVectorContainer> ff;
MyVectorContainer mvc;
mvc.vs.push_back(0);
ff.push_back(mvc);
sink(ff[0].vs[0]);
ff[0].vs[0] = source();
sink(ff[0].vs[0]); // tainted [NOT DETECTED]
}
}

View File

@@ -20,7 +20,7 @@
| functions.cpp:23:7:23:11 | Table | Class | functions.cpp:30:8:30:13 | insert | |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:33:7:33:7 | operator= | |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:36:2:36:8 | MyClass | Constructor, NoArgConstructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:37:2:37:8 | MyClass | Constructor, ConversionConstructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:37:2:37:8 | MyClass | Constructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:38:2:38:8 | MyClass | Constructor, CopyConstructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:39:2:39:8 | MyClass | Constructor, ConversionConstructor, MoveConstructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:39:2:39:8 | MyClass | Constructor, MoveConstructor, getAConstructor() |
| functions.cpp:33:7:33:13 | MyClass | Class | functions.cpp:40:2:40:13 | operator int | ConversionOperator |

View File

@@ -19,9 +19,6 @@ string describe(Class c, MemberFunction f) {
f instanceof Destructor and
result = "Destructor"
or
f instanceof ConversionConstructor and
result = "ConversionConstructor"
or
f instanceof CopyConstructor and
result = "CopyConstructor"
or

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

@@ -0,0 +1,10 @@
// semmle-extractor-options: --edg --clang --edg --c++20
namespace cpp20 {
class TestConstexpr {
constexpr int member_constexpr() { return 0; } // not const in C++ >= 14
constexpr int member_const_constexpr() const { return 0; }
};
} // namespace cpp20

View File

@@ -1,5 +1,22 @@
| Class | specifiers2pp.cpp:8:7:8:13 | MyClass | MyClass | abstract |
| Class | specifiers2pp.cpp:24:7:24:14 | MyClass2 | MyClass2 | abstract |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | extern |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | extern |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | inline |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | inline |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | is_constexpr |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | is_constexpr |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | public |
| Function | cpp20.cpp:5:7:5:7 | operator= | operator= | public |
| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | declared_constexpr |
| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | inline |
| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | is_constexpr |
| Function | cpp20.cpp:6:19:6:34 | member_constexpr | member_constexpr | private |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | const |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | declared_constexpr |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | inline |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | is_constexpr |
| Function | cpp20.cpp:7:19:7:40 | member_const_constexpr | member_const_constexpr | private |
| Function | specifiers2.c:11:6:11:6 | f | f | extern |
| Function | specifiers2.c:12:13:12:13 | f | f | extern |
| Function | specifiers2.c:13:13:13:13 | f | f | extern |
@@ -79,6 +96,24 @@
| Function | specifiers2pp.cpp:41:16:41:23 | someFun2 | someFun2 | extern |
| Function | specifiers2pp.cpp:43:9:43:16 | someFun3 | someFun3 | extern |
| Function | specifiers2pp.cpp:44:16:44:23 | someFun4 | someFun4 | static |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | extern |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | inline |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | is_constexpr |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | is_constexpr |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:62:7:62:7 | operator= | operator= | public |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | const |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | declared_constexpr |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | inline |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | is_constexpr |
| Function | specifiers2pp.cpp:63:19:63:34 | member_constexpr | member_constexpr | private |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | const |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | declared_constexpr |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | inline |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | is_constexpr |
| Function | specifiers2pp.cpp:64:19:64:40 | member_const_constexpr | member_const_constexpr | private |
| FunctionDeclarationEntry | specifiers2.c:11:6:11:6 | declaration of f | f | c_linkage |
| FunctionDeclarationEntry | specifiers2.c:11:6:11:6 | declaration of f | f | void_param_list |
| FunctionDeclarationEntry | specifiers2.c:12:13:12:13 | declaration of f | f | c_linkage |

View File

@@ -58,3 +58,8 @@ template<typename T> using Const = const T;
using Const_int = Const<int>;
typedef volatile Const_int volatile_Const_int;
class TestConstexpr {
constexpr int member_constexpr() { return 0; } // const in C++11
constexpr int member_const_constexpr() const { return 0; }
};

View File

@@ -0,0 +1,6 @@
| a.c:4:5:4:6 | definition of is | array of {int} | 1 |
| a.h:2:12:2:13 | declaration of is | array of 4 {int} | 1 |
| file://:0:0:0:0 | definition of fp_offset | unsigned int | 1 |
| file://:0:0:0:0 | definition of gp_offset | unsigned int | 1 |
| file://:0:0:0:0 | definition of overflow_arg_area | pointer to {void} | 1 |
| file://:0:0:0:0 | definition of reg_save_area | pointer to {void} | 1 |

View File

@@ -0,0 +1,5 @@
import cpp
from VariableDeclarationEntry vd, Type t
where t = vd.getType()
select vd, t.explain(), count(Type u | u = vd.getType())

View File

@@ -1,5 +1,4 @@
| a.c:4:5:4:6 | is | array of 4 {int} | 2 |
| a.c:4:5:4:6 | is | array of {int} | 2 |
| a.c:4:5:4:6 | is | array of {int} | 1 |
| file://:0:0:0:0 | fp_offset | unsigned int | 1 |
| file://:0:0:0:0 | gp_offset | unsigned int | 1 |
| file://:0:0:0:0 | overflow_arg_area | pointer to {void} | 1 |

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

@@ -177,4 +177,43 @@ void FalseNegativeTestCases()
for (int i = 100; i > 0; i += 2) {}
// For comparison
for (int i = 100; i > 0; i ++ ) {} // BUG
}
}
void IntendedOverflow(unsigned char p)
{
const unsigned char m = 10;
unsigned char i;
signed char s;
for (i = 63; i < 64; i--) {} // GOOD (legitimate way to count down with an unsigned)
for (i = 63; i < 128; i--) {} // DUBIOUS (could still be a typo?)
for (i = 63; i < 255; i--) {} // GOOD
for (i = m - 1; i < m; i--) {} // GOOD
for (i = m - 2; i < m; i--) {} // DUBIOUS
for (i = m; i < m + 1; i--) {} // GOOD
for (s = 63; s < 64; s--) {} // BAD (signed numbers don't wrap at 0 / at all)
for (s = m + 1; s < m; s--) {} // BAD (never runs)
for (i = p - 1; i < p; i--) {} // GOOD
for (s = p - 1; s < p; s--) {} // BAD [NOT DETECTED]
{
int n;
n = 64;
for (i = n - 1; i < n; i--) {} // GOOD
n = 64;
for (i = n - 1; i < 64; i--) {} // GOOD
n = 64;
for (i = 63; i < n; i--) {} // GOOD
n = 64;
for (s = n - 1; s < n; s--) {} // BAD [NOT DETECTED]
n = 64;
for (s = n - 1; s < 64; s--) {} // BAD
n = 64;
for (s = 63; s < n; s--) {} // BAD [NOT DETECTED]
}
}

View File

@@ -20,3 +20,6 @@
| inconsistentLoopDirection.cpp:140:5:142:5 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (200), but the terminal condition is lower (0). |
| inconsistentLoopDirection.cpp:175:5:175:36 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts downward from a value (0), but the terminal condition is higher (10). |
| inconsistentLoopDirection.cpp:179:5:179:38 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "i" counts upward from a value (100), but the terminal condition is lower (0). |
| inconsistentLoopDirection.cpp:196:5:196:32 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (63), but the terminal condition is higher (64). |
| inconsistentLoopDirection.cpp:197:5:197:34 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (... + ...), but the terminal condition is always false. |
| inconsistentLoopDirection.cpp:215:3:215:33 | for(...;...;...) ... | Ill-defined for-loop: a loop using variable "s" counts downward from a value (... - ...), but the terminal condition is higher (64). |

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 |

213
csharp/.editorconfig Normal file
View File

@@ -0,0 +1,213 @@
# Taken as is from https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2019
# Customizations are added at the bottom of the file.
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true
# C# files
[*.cs]
#### Core EditorConfig Options ####
# Indentation and spacing
indent_size = 4
indent_style = space
tab_width = 4
# New line preferences
end_of_line = crlf
insert_final_newline = false
#### .NET Coding Conventions ####
# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
# this. and Me. preferences
dotnet_style_qualification_for_event = false:silent
dotnet_style_qualification_for_field = false:silent
dotnet_style_qualification_for_method = false:silent
dotnet_style_qualification_for_property = false:silent
# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true:silent
dotnet_style_predefined_type_for_member_access = true:silent
# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
# Expression-level preferences
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_object_initializer = true:suggestion
dotnet_style_prefer_auto_properties = true:silent
dotnet_style_prefer_compound_assignment = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_simplified_interpolation = true:suggestion
# Field preferences
dotnet_style_readonly_field = true:suggestion
# Parameter preferences
dotnet_code_quality_unused_parameters = all:suggestion
#### C# Coding Conventions ####
# var preferences
csharp_style_var_elsewhere = false:silent
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
# Expression-bodied members
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = false:silent
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_operators = false:silent
csharp_style_expression_bodied_properties = true:silent
# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_prefer_switch_expression = true:suggestion
# Null-checking preferences
csharp_style_conditional_delegate_call = true:suggestion
# Modifier preferences
csharp_prefer_static_local_function = true:suggestion
csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
# Code-block preferences
csharp_prefer_braces = true:silent
csharp_prefer_simple_using_statement = true:suggestion
# Expression-level preferences
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_prefer_index_operator = true:suggestion
csharp_style_prefer_range_operator = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# 'using' directive preferences
csharp_using_directive_placement = outside_namespace:silent
#### C# Formatting Rules ####
# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
#### Naming styles ####
# Naming rules
dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
# Symbol specifications
dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers =
# Naming styles
dotnet_naming_style.pascal_case.required_prefix =
dotnet_naming_style.pascal_case.required_suffix =
dotnet_naming_style.pascal_case.word_separator =
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix =
dotnet_naming_style.begins_with_i.word_separator =
dotnet_naming_style.begins_with_i.capitalization = pascal_case
#
# Customizations
#
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = warning
[extractor/Semmle.Extraction/Tuples.cs,
extractor/Semmle.Extraction.CSharp/Tuples.cs,
extractor/Semmle.Extraction.CIL/Tuples.cs]
dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = none

3
csharp/.gitignore vendored
View File

@@ -10,4 +10,5 @@ csharp.log
**/bin/Release
*.tlog
.vs
*.user
*.user
.vscode/launch.json

9
csharp/.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,9 @@
{
"recommendations": [
"github.vscode-codeql",
"ms-dotnettools.csharp",
"formulahendry.dotnet-test-explorer",
"hbenl.vscode-test-explorer"
],
"unwantedRecommendations": []
}

13
csharp/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,13 @@
{
"dotnet-test-explorer.enableTelemetry": false,
"dotnet-test-explorer.testProjectPath": "**/*Tests.@(csproj|vbproj|fsproj)",
"dotnet-test-explorer.testArguments": "/property:GenerateTargetFrameworkAttribute=false",
"csharp.supressBuildAssetsNotification": true,
"csharp.suppressDotnetRestoreNotification": true,
"[csharp]": {
"editor.defaultFormatter": "ms-dotnettools.csharp"
},
"omnisharp.enableMsBuildLoadProjectsOnDemand": true,
"omnisharp.enableEditorConfigSupport": true,
"omnisharp.enableRoslynAnalyzers": true,
}

53
csharp/.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,53 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "dotnet build",
"command": "dotnet",
"type": "shell",
"args": [
"build",
// Ask dotnet build to generate full paths for file names.
"/property:GenerateFullPaths=true",
// Do not generate summary otherwise it leads to duplicate errors in Problems panel
"/consoleloggerparameters:NoSummary"
],
"group": "build",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
},
{
"label": "dotnet rebuild",
"command": "dotnet",
"type": "shell",
"args": [
"build",
"--no-incremental",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"group": "build",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
},
{
"label": "dotnet test",
"command": "dotnet",
"type": "shell",
"args": [
"test",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"group": "test",
"presentation": {
"reveal": "always"
},
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -19,15 +19,15 @@ namespace Semmle.Autobuild.CSharp.Tests
/// <summary>
/// List of strings passed to FileDelete.
/// </summary>
public IList<string> FileDeleteIn = new List<string>();
public readonly IList<string> FileDeleteIn = new List<string>();
void IBuildActions.FileDelete(string file)
{
FileDeleteIn.Add(file);
}
public IList<string> FileExistsIn = new List<string>();
public IDictionary<string, bool> FileExists = new Dictionary<string, bool>();
public readonly IList<string> FileExistsIn = new List<string>();
public readonly IDictionary<string, bool> FileExists = new Dictionary<string, bool>();
bool IBuildActions.FileExists(string file)
{
@@ -39,10 +39,10 @@ namespace Semmle.Autobuild.CSharp.Tests
throw new ArgumentException("Missing FileExists " + file);
}
public IList<string> RunProcessIn = new List<string>();
public IDictionary<string, int> RunProcess = new Dictionary<string, int>();
public IDictionary<string, string> RunProcessOut = new Dictionary<string, string>();
public IDictionary<string, string> RunProcessWorkingDirectory = new Dictionary<string, string>();
public readonly IList<string> RunProcessIn = new List<string>();
public readonly IDictionary<string, int> RunProcess = new Dictionary<string, int>();
public readonly IDictionary<string, string> RunProcessOut = new Dictionary<string, string>();
public readonly IDictionary<string, string> RunProcessWorkingDirectory = new Dictionary<string, string>();
int IBuildActions.RunProcess(string cmd, string args, string? workingDirectory, IDictionary<string, string>? env, out IList<string> stdOut)
{
@@ -72,14 +72,14 @@ namespace Semmle.Autobuild.CSharp.Tests
throw new ArgumentException("Missing RunProcess " + pattern);
}
public IList<string> DirectoryDeleteIn = new List<string>();
public readonly IList<string> DirectoryDeleteIn = new List<string>();
void IBuildActions.DirectoryDelete(string dir, bool recursive)
{
DirectoryDeleteIn.Add(dir);
}
public IDictionary<string, bool> DirectoryExists = new Dictionary<string, bool>();
public readonly IDictionary<string, bool> DirectoryExists = new Dictionary<string, bool>();
bool IBuildActions.DirectoryExists(string dir)
{
@@ -88,7 +88,7 @@ namespace Semmle.Autobuild.CSharp.Tests
throw new ArgumentException("Missing DirectoryExists " + dir);
}
public IDictionary<string, string?> GetEnvironmentVariable = new Dictionary<string, string?>();
public readonly IDictionary<string, string?> GetEnvironmentVariable = new Dictionary<string, string?>();
string? IBuildActions.GetEnvironmentVariable(string name)
{
@@ -104,7 +104,7 @@ namespace Semmle.Autobuild.CSharp.Tests
return GetCurrentDirectory;
}
public IDictionary<string, string> EnumerateFiles = new Dictionary<string, string>();
public readonly IDictionary<string, string> EnumerateFiles = new Dictionary<string, string>();
IEnumerable<string> IBuildActions.EnumerateFiles(string dir)
{
@@ -113,7 +113,7 @@ namespace Semmle.Autobuild.CSharp.Tests
throw new ArgumentException("Missing EnumerateFiles " + dir);
}
public IDictionary<string, string> EnumerateDirectories = new Dictionary<string, string>();
public readonly IDictionary<string, string> EnumerateDirectories = new Dictionary<string, string>();
IEnumerable<string> IBuildActions.EnumerateDirectories(string dir)
{
@@ -137,7 +137,8 @@ namespace Semmle.Autobuild.CSharp.Tests
{
}
public IDictionary<string, XmlDocument> LoadXml = new Dictionary<string, XmlDocument>();
public readonly IDictionary<string, XmlDocument> LoadXml = new Dictionary<string, XmlDocument>();
XmlDocument IBuildActions.LoadXml(string filename)
{
if (LoadXml.TryGetValue(filename, out var xml))
@@ -178,10 +179,10 @@ namespace Semmle.Autobuild.CSharp.Tests
public class BuildScriptTests
{
TestActions Actions = new TestActions();
readonly TestActions Actions = new TestActions();
// Records the arguments passed to StartCallback.
IList<string> StartCallbackIn = new List<string>();
readonly IList<string> StartCallbackIn = new List<string>();
void StartCallback(string s, bool silent)
{
@@ -189,8 +190,8 @@ namespace Semmle.Autobuild.CSharp.Tests
}
// Records the arguments passed to EndCallback
IList<string> EndCallbackIn = new List<string>();
IList<int> EndCallbackReturn = new List<int>();
readonly IList<string> EndCallbackIn = new List<string>();
readonly IList<int> EndCallbackReturn = new List<int>();
void EndCallback(int ret, string s, bool silent)
{
@@ -449,15 +450,22 @@ Microsoft.NETCore.App 2.2.5 [/usr/local/share/dotnet/shared/Microsoft.NETCore.Ap
Actions.GetEnvironmentVariable["ProgramFiles(x86)"] = @"C:\Program Files (x86)";
Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = true;
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 0;
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "C:\\VS1\nC:\\VS2";
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = "10.0\n11.0";
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "C:\\VS1\nC:\\VS2\nC:\\VS3";
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = "10.0\n11.0\n16.0";
var candidates = BuildTools.GetCandidateVcVarsFiles(Actions).ToArray();
Assert.Equal("C:\\VS1\\VC\\vcvarsall.bat", candidates[0].Path);
Assert.Equal(10, candidates[0].ToolsVersion);
Assert.Equal("C:\\VS2\\VC\\vcvarsall.bat", candidates[1].Path);
Assert.Equal(11, candidates[1].ToolsVersion);
Assert.Equal(@"C:\VS3\VC\Auxiliary\Build\vcvars32.bat", candidates[2].Path);
Assert.Equal(16, candidates[2].ToolsVersion);
Assert.Equal(@"C:\VS3\VC\Auxiliary\Build\vcvars64.bat", candidates[3].Path);
Assert.Equal(16, candidates[3].ToolsVersion);
Assert.Equal(@"C:\VS3\Common7\Tools\VsDevCmd.bat", candidates[4].Path);
Assert.Equal(16, candidates[4].ToolsVersion);
Assert.Equal(5, candidates.Length);
}
[Fact]

View File

@@ -19,6 +19,12 @@ namespace Semmle.Autobuild.Shared
BuildScript Analyse(Autobuilder builder, bool auto);
}
/// <summary>
/// A delegate used to wrap a build script in an environment where an appropriate
/// version of .NET Core is automatically installed.
/// </summary>
public delegate BuildScript WithDotNet(Autobuilder builder, Func<IDictionary<string, string>?, BuildScript> f);
/// <summary>
/// Exception indicating that environment variables are missing or invalid.
/// </summary>

View File

@@ -11,9 +11,9 @@ namespace Semmle.Autobuild.Shared
/// </summary>
public class BuildCommandAutoRule : IBuildRule
{
private readonly Func<Autobuilder, Func<IDictionary<string, string>?, BuildScript>, BuildScript> withDotNet;
private readonly WithDotNet withDotNet;
public BuildCommandAutoRule(Func<Autobuilder, Func<IDictionary<string, string>?, BuildScript>, BuildScript> withDotNet)
public BuildCommandAutoRule(WithDotNet withDotNet)
{
this.withDotNet = withDotNet;
}

View File

@@ -8,9 +8,9 @@ namespace Semmle.Autobuild.Shared
/// </summary>
public class BuildCommandRule : IBuildRule
{
private readonly Func<Autobuilder, Func<IDictionary<string, string>?, BuildScript>, BuildScript> withDotNet;
private readonly WithDotNet withDotNet;
public BuildCommandRule(Func<Autobuilder, Func<IDictionary<string, string>?, BuildScript>, BuildScript> withDotNet)
public BuildCommandRule(WithDotNet withDotNet)
{
this.withDotNet = withDotNet;
}

View File

@@ -11,13 +11,11 @@ namespace Semmle.Autobuild.Shared
{
public readonly int ToolsVersion;
public readonly string Path;
public readonly string[] Platform;
public VcVarsBatFile(string path, int version, params string[] platform)
public VcVarsBatFile(string path, int version)
{
Path = path;
ToolsVersion = version;
Platform = platform;
}
};
@@ -51,12 +49,15 @@ namespace Semmle.Autobuild.Shared
{
if (majorVersion < 15)
{
yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\vcvarsall.bat"), majorVersion, "x86");
// Visual Studio 2015 and below
yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\vcvarsall.bat"), majorVersion);
}
else
{
yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars32.bat"), majorVersion, "x86");
yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars64.bat"), majorVersion, "x64");
// Visual Studio 2017 and above
yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars32.bat"), majorVersion);
yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"VC\Auxiliary\Build\vcvars64.bat"), majorVersion);
yield return new VcVarsBatFile(actions.PathCombine(vsInstallation.InstallationPath, @"Common7\Tools\VsDevCmd.bat"), majorVersion);
}
}
// else: Skip installation without a version
@@ -66,10 +67,10 @@ namespace Semmle.Autobuild.Shared
}
// vswhere not installed or didn't run correctly - return legacy Visual Studio versions
yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 14.0\VC\vcvarsall.bat"), 14, "x86");
yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 12.0\VC\vcvarsall.bat"), 12, "x86");
yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 11.0\VC\vcvarsall.bat"), 11, "x86");
yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 10.0\VC\vcvarsall.bat"), 10, "x86");
yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 14.0\VC\vcvarsall.bat"), 14);
yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 12.0\VC\vcvarsall.bat"), 12);
yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 11.0\VC\vcvarsall.bat"), 11);
yield return new VcVarsBatFile(actions.PathCombine(programFilesx86, @"Microsoft Visual Studio 10.0\VC\vcvarsall.bat"), 10);
}
/// <summary>

View File

@@ -33,7 +33,7 @@ namespace Semmle.Autobuild.Shared
if (vsTools == null && builder.Actions.IsWindows())
{
builder.Log(Severity.Warning, "Could not find a suitable version of vcvarsall.bat");
builder.Log(Severity.Warning, "Could not find a suitable version of VsDevCmd.bat/vcvarsall.bat");
}
var nuget =

View File

@@ -0,0 +1,18 @@
name: "csharp"
display_name: "C#"
version: 1.22.1
column_kind: "utf16"
extra_env_vars:
DOTNET_GENERATE_ASPNET_CERTIFICATE: "false"
COR_ENABLE_PROFILING: "1"
COR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}"
COR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}"
CORECLR_ENABLE_PROFILING: "1"
CORECLR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}"
CORECLR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}"
file_types:
- name: cs
display_name: C# sources
extensions:
- .cs
legacy_qltest_extraction: true

View File

@@ -50,6 +50,13 @@ namespace Semmle.Extraction.CIL.Driver
return an;
}
/// <summary>
/// Initializes a new instance of the <see cref="AssemblyInfo"/> class.
/// </summary>
/// <param name="path">Path of the assembly.</param>
/// <exception cref="Semmle.Extraction.CIL.Driver.InvalidAssemblyException">
/// Thrown when the input file is not a valid assembly.
/// </exception>
public AssemblyInfo(string path)
{
filename = path;
@@ -60,13 +67,11 @@ namespace Semmle.Extraction.CIL.Driver
{
try
{
isAssembly = peReader.HasMetadata;
if (!isAssembly) return;
if (!peReader.HasMetadata) throw new InvalidAssemblyException();
var mdReader = peReader.GetMetadataReader();
isAssembly = mdReader.IsAssembly;
if (!mdReader.IsAssembly) return;
if (!mdReader.IsAssembly) throw new InvalidAssemblyException();
// Get our own assembly name
name = CreateAssemblyName(mdReader, mdReader.GetAssemblyDefinition());
@@ -81,7 +86,7 @@ namespace Semmle.Extraction.CIL.Driver
// This failed on one of the Roslyn tests that includes
// a deliberately malformed assembly.
// In this case, we just skip the extraction of this assembly.
isAssembly = false;
throw new InvalidAssemblyException();
}
}
}
@@ -89,7 +94,6 @@ namespace Semmle.Extraction.CIL.Driver
public readonly AssemblyName name;
public readonly string filename;
public bool extract;
public readonly bool isAssembly;
public readonly AssemblyName[] references;
}
@@ -102,11 +106,12 @@ namespace Semmle.Extraction.CIL.Driver
{
class AssemblyNameComparer : IEqualityComparer<AssemblyName>
{
bool IEqualityComparer<AssemblyName>.Equals(AssemblyName x, AssemblyName y) =>
x.Name == y.Name && x.Version == y.Version;
bool IEqualityComparer<AssemblyName>.Equals(AssemblyName? x, AssemblyName? y) =>
object.ReferenceEquals(x, y) ||
x?.Name == y?.Name && x?.Version == y?.Version;
int IEqualityComparer<AssemblyName>.GetHashCode(AssemblyName obj) =>
obj.Name.GetHashCode() + 7 * obj.Version.GetHashCode();
(obj.Name, obj.Version).GetHashCode();
}
readonly Dictionary<AssemblyName, AssemblyInfo> assembliesRead = new Dictionary<AssemblyName, AssemblyInfo>(new AssemblyNameComparer());
@@ -116,13 +121,15 @@ namespace Semmle.Extraction.CIL.Driver
if (!filesAnalyzed.Contains(assemblyPath))
{
filesAnalyzed.Add(assemblyPath);
var info = new AssemblyInfo(assemblyPath);
if (info.isAssembly)
try
{
var info = new AssemblyInfo(assemblyPath);
info.extract = extractAll;
if (!assembliesRead.ContainsKey(info.name))
assembliesRead.Add(info.name, info);
}
catch (InvalidAssemblyException)
{ }
}
}
@@ -137,8 +144,7 @@ namespace Semmle.Extraction.CIL.Driver
while (assembliesToReference.Any())
{
var item = assembliesToReference.Pop();
AssemblyInfo info;
if (assembliesRead.TryGetValue(item, out info))
if (assembliesRead.TryGetValue(item, out AssemblyInfo? info))
{
if (!info.extract)
{
@@ -165,6 +171,21 @@ namespace Semmle.Extraction.CIL.Driver
{
readonly AssemblyList assemblyList = new AssemblyList();
public ExtractorOptions(string[] args)
{
Verbosity = Verbosity.Info;
Threads = System.Environment.ProcessorCount;
PDB = true;
TrapCompression = TrapWriter.CompressionMode.Gzip;
ParseArgs(args);
AddFrameworkDirectories(false);
assemblyList.ResolveReferences();
AssembliesToExtract = assemblyList.AssembliesToExtract.ToArray();
}
public void AddDirectory(string directory, bool extractAll)
{
foreach (var file in
@@ -192,7 +213,12 @@ namespace Semmle.Extraction.CIL.Driver
if (File.Exists(path))
{
assemblyList.AddFile(path, true);
AddDirectory(Path.GetDirectoryName(path), false);
string? directory = Path.GetDirectoryName(path);
if (directory is null)
{
throw new InternalError($"Directory of path '{path}' is null");
}
AddDirectory(directory, false);
}
else if (Directory.Exists(path))
{
@@ -200,13 +226,7 @@ namespace Semmle.Extraction.CIL.Driver
}
}
void ResolveReferences()
{
assemblyList.ResolveReferences();
AssembliesToExtract = assemblyList.AssembliesToExtract.ToArray();
}
public IEnumerable<AssemblyInfo> AssembliesToExtract { get; private set; }
public IEnumerable<AssemblyInfo> AssembliesToExtract { get; }
/// <summary>
/// Gets the assemblies that were referenced but were not available to be
@@ -215,55 +235,43 @@ namespace Semmle.Extraction.CIL.Driver
/// </summary>
public IEnumerable<AssemblyName> MissingReferences => assemblyList.missingReferences;
public static ExtractorOptions ParseCommandLine(string[] args)
private void ParseArgs(string[] args)
{
var options = new ExtractorOptions();
options.Verbosity = Verbosity.Info;
options.Threads = System.Environment.ProcessorCount;
options.PDB = true;
options.TrapCompression = TrapWriter.CompressionMode.Gzip;
foreach (var arg in args)
{
if (arg == "--verbose")
{
options.Verbosity = Verbosity.All;
Verbosity = Verbosity.All;
}
else if (arg == "--silent")
{
options.Verbosity = Verbosity.Off;
Verbosity = Verbosity.Off;
}
else if (arg.StartsWith("--verbosity:"))
{
options.Verbosity = (Verbosity)int.Parse(arg.Substring(12));
Verbosity = (Verbosity)int.Parse(arg.Substring(12));
}
else if (arg == "--dotnet")
{
options.AddFrameworkDirectories(true);
AddFrameworkDirectories(true);
}
else if (arg == "--nocache")
{
options.NoCache = true;
NoCache = true;
}
else if (arg.StartsWith("--threads:"))
{
options.Threads = int.Parse(arg.Substring(10));
Threads = int.Parse(arg.Substring(10));
}
else if (arg == "--no-pdb")
{
options.PDB = false;
PDB = false;
}
else
{
options.AddFileOrDirectory(arg);
AddFileOrDirectory(arg);
}
}
options.AddFrameworkDirectories(false);
options.ResolveReferences();
return options;
}
}
}

View File

@@ -0,0 +1,7 @@
using System;
namespace Semmle.Extraction.CIL.Driver
{
class InvalidAssemblyException : Exception
{ }
}

Some files were not shown because too many files have changed in this diff Show More