mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge branch 'main' into asyncCalls
This commit is contained in:
25
change-notes/1.26/analysis-cpp.md
Normal file
25
change-notes/1.26/analysis-cpp.md
Normal 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.
|
||||
29
change-notes/1.26/analysis-csharp.md
Normal file
29
change-notes/1.26/analysis-csharp.md
Normal 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.
|
||||
32
change-notes/1.26/analysis-javascript.md
Normal file
32
change-notes/1.26/analysis-javascript.md
Normal 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
|
||||
@@ -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())
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
@@ -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>
|
||||
@@ -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."
|
||||
@@ -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() }
|
||||
}
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()))
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 + ")")
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 + ")")
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* Provides models for C++ containers `std::array`, `std::vector`, `std::deque`, `std::list` and `std::forward_list`.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/**
|
||||
* Additional model for standard container constructors that reference the
|
||||
* value type of the container (that is, the `T` in `std::vector<T>`). For
|
||||
* example the fill constructor:
|
||||
* ```
|
||||
* std::vector<std::string> v(100, potentially_tainted_string);
|
||||
* ```
|
||||
*/
|
||||
class StdSequenceContainerConstructor extends Constructor, TaintFunction {
|
||||
StdSequenceContainerConstructor() {
|
||||
this.getDeclaringType().hasQualifiedName("std", "vector") or
|
||||
this.getDeclaringType().hasQualifiedName("std", "deque") or
|
||||
this.getDeclaringType().hasQualifiedName("std", "list") or
|
||||
this.getDeclaringType().hasQualifiedName("std", "forward_list")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a reference to the
|
||||
* value type of the container.
|
||||
*/
|
||||
int getAValueTypeParameterIndex() {
|
||||
getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
getDeclaringType().getTemplateArgument(0) // i.e. the `T` of this `std::vector<T>`
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// taint flow from any parameter of the value type to the returned object
|
||||
input.isParameterDeref(getAValueTypeParameterIndex()) and
|
||||
output.isReturnValue() // TODO: this should be `isQualifierObject` by our current definitions, but that flow is not yet supported.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `push_back` and `push_front`.
|
||||
*/
|
||||
class StdSequenceContainerPush extends TaintFunction {
|
||||
StdSequenceContainerPush() {
|
||||
this.hasQualifiedName("std", "vector", "push_back") or
|
||||
this.hasQualifiedName("std", "deque", "push_back") or
|
||||
this.hasQualifiedName("std", "deque", "push_front") or
|
||||
this.hasQualifiedName("std", "list", "push_back") or
|
||||
this.hasQualifiedName("std", "list", "push_front") or
|
||||
this.hasQualifiedName("std", "forward_list", "push_front")
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to qualifier
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container functions `front` and `back`.
|
||||
*/
|
||||
class StdSequenceContainerFrontBack extends TaintFunction {
|
||||
StdSequenceContainerFrontBack() {
|
||||
this.hasQualifiedName("std", "array", "front") or
|
||||
this.hasQualifiedName("std", "array", "back") or
|
||||
this.hasQualifiedName("std", "vector", "front") or
|
||||
this.hasQualifiedName("std", "vector", "back") or
|
||||
this.hasQualifiedName("std", "deque", "front") or
|
||||
this.hasQualifiedName("std", "deque", "back") or
|
||||
this.hasQualifiedName("std", "list", "front") or
|
||||
this.hasQualifiedName("std", "list", "back") or
|
||||
this.hasQualifiedName("std", "forward_list", "front")
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from object to returned reference
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard container `swap` functions.
|
||||
*/
|
||||
class StdSequenceContainerSwap extends TaintFunction {
|
||||
StdSequenceContainerSwap() {
|
||||
this.hasQualifiedName("std", "array", "swap") or
|
||||
this.hasQualifiedName("std", "vector", "swap") or
|
||||
this.hasQualifiedName("std", "deque", "swap") or
|
||||
this.hasQualifiedName("std", "list", "swap") or
|
||||
this.hasQualifiedName("std", "forward_list", "swap")
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// container1.swap(container2)
|
||||
input.isQualifierObject() and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,20 @@
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/**
|
||||
* The standard function `std::string.c_str`.
|
||||
* The `std::basic_string` template class.
|
||||
*/
|
||||
class StdBasicString extends TemplateClass {
|
||||
StdBasicString() { this.hasQualifiedName("std", "basic_string") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` functions `c_str` and `data`.
|
||||
*/
|
||||
class StdStringCStr extends TaintFunction {
|
||||
StdStringCStr() { this.hasQualifiedName("std", "basic_string", "c_str") }
|
||||
StdStringCStr() {
|
||||
this.hasQualifiedName("std", "basic_string", "c_str") or
|
||||
this.hasQualifiedName("std", "basic_string", "data")
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from string itself (qualifier) to return value
|
||||
@@ -12,3 +22,126 @@ class StdStringCStr extends TaintFunction {
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` function `operator+`.
|
||||
*/
|
||||
class StdStringPlus extends TaintFunction {
|
||||
StdStringPlus() {
|
||||
this.hasQualifiedName("std", "operator+") and
|
||||
this.getUnspecifiedType() = any(StdBasicString s).getAnInstantiation()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameters to return value
|
||||
(
|
||||
input.isParameterDeref(0) or
|
||||
input.isParameterDeref(1)
|
||||
) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` functions `operator+=`, `append`, `insert` and
|
||||
* `replace`. All of these functions combine the existing string
|
||||
* with a new string (or character) from one of the arguments.
|
||||
*/
|
||||
class StdStringAppend extends TaintFunction {
|
||||
StdStringAppend() {
|
||||
this.hasQualifiedName("std", "basic_string", "operator+=") or
|
||||
this.hasQualifiedName("std", "basic_string", "append") or
|
||||
this.hasQualifiedName("std", "basic_string", "insert") or
|
||||
this.hasQualifiedName("std", "basic_string", "replace")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
* character).
|
||||
*/
|
||||
int getAStringParameter() {
|
||||
getParameter(result).getType() instanceof PointerType or
|
||||
getParameter(result).getType() instanceof ReferenceType or
|
||||
getParameter(result).getType() = getDeclaringType().getTemplateArgument(0) // i.e. `std::basic_string::CharT`
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from string and parameter to string (qualifier) and return value
|
||||
(
|
||||
input.isQualifierObject() or
|
||||
input.isParameterDeref(getAStringParameter())
|
||||
) and
|
||||
(
|
||||
output.isQualifierObject() or
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `std::string.assign`.
|
||||
*/
|
||||
class StdStringAssign extends TaintFunction {
|
||||
StdStringAssign() { this.hasQualifiedName("std", "basic_string", "assign") }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
* character).
|
||||
*/
|
||||
int getAStringParameter() {
|
||||
getParameter(result).getType() instanceof PointerType or
|
||||
getParameter(result).getType() instanceof ReferenceType or
|
||||
getParameter(result).getType() = getDeclaringType().getTemplateArgument(0) // i.e. `std::basic_string::CharT`
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to string itself (qualifier) and return value
|
||||
input.isParameterDeref(getAStringParameter()) and
|
||||
(
|
||||
output.isQualifierObject() or
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `std::string.copy`.
|
||||
*/
|
||||
class StdStringCopy extends TaintFunction {
|
||||
StdStringCopy() { this.hasQualifiedName("std", "basic_string", "copy") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// copy(dest, num, pos)
|
||||
input.isQualifierObject() and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `std::string.substr`.
|
||||
*/
|
||||
class StdStringSubstr extends TaintFunction {
|
||||
StdStringSubstr() { this.hasQualifiedName("std", "basic_string", "substr") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// substr(pos, num)
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `std::string.swap`.
|
||||
*/
|
||||
class StdStringSwap extends TaintFunction {
|
||||
StdStringSwap() { this.hasQualifiedName("std", "basic_string", "swap") }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// str1.swap(str2)
|
||||
input.isQualifierObject() and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
@@ -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. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-359/PrivateCleartextWrite.ql
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-120/MemoryUnsafeFunctionScan.ql
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,310 +0,0 @@
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class charT> struct char_traits;
|
||||
|
||||
typedef size_t streamsize;
|
||||
|
||||
struct ptrdiff_t;
|
||||
|
||||
template <class iterator_category,
|
||||
class value_type,
|
||||
class difference_type = ptrdiff_t,
|
||||
class pointer_type = value_type*,
|
||||
class reference_type = value_type&>
|
||||
struct iterator {
|
||||
iterator &operator++();
|
||||
iterator operator++(int);
|
||||
bool operator==(iterator other) const;
|
||||
bool operator!=(iterator other) const;
|
||||
reference_type operator*() const;
|
||||
};
|
||||
|
||||
struct input_iterator_tag {};
|
||||
struct forward_iterator_tag : public input_iterator_tag {};
|
||||
struct bidirectional_iterator_tag : public forward_iterator_tag {};
|
||||
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
|
||||
|
||||
template <class T> class allocator {
|
||||
public:
|
||||
allocator() throw();
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
|
||||
class basic_string {
|
||||
public:
|
||||
explicit basic_string(const Allocator& a = Allocator());
|
||||
basic_string(const charT* s, const Allocator& a = Allocator());
|
||||
|
||||
const charT* c_str() const;
|
||||
|
||||
typedef std::iterator<random_access_iterator_tag, charT> iterator;
|
||||
typedef std::iterator<random_access_iterator_tag, const charT> const_iterator;
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
const_iterator cbegin() const;
|
||||
const_iterator cend() const;
|
||||
};
|
||||
|
||||
typedef basic_string<char> string;
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
basic_istream<charT,traits>& operator>>(int& n);
|
||||
};
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
typedef charT char_type;
|
||||
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
|
||||
|
||||
basic_ostream<charT, traits>& operator<<(int n);
|
||||
};
|
||||
|
||||
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
|
||||
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
|
||||
|
||||
template<class charT, class traits = char_traits<charT>>
|
||||
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
|
||||
public:
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
|
||||
class basic_stringstream : public basic_iostream<charT, traits> {
|
||||
public:
|
||||
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
|
||||
|
||||
basic_string<charT, traits, Allocator> str() const;
|
||||
};
|
||||
|
||||
using stringstream = basic_stringstream<char>;
|
||||
}
|
||||
|
||||
char *source();
|
||||
void sink(const char *s) {};
|
||||
void sink(const std::string &s) {};
|
||||
void sink(const std::stringstream &s) {};
|
||||
|
||||
void test_string()
|
||||
{
|
||||
char *a = source();
|
||||
std::string b("123");
|
||||
std::string c(source());
|
||||
|
||||
sink(a); // tainted
|
||||
sink(b);
|
||||
sink(c); // tainted
|
||||
sink(b.c_str());
|
||||
sink(c.c_str()); // tainted
|
||||
}
|
||||
|
||||
void test_stringstream()
|
||||
{
|
||||
std::stringstream ss1, ss2, ss3, ss4, ss5;
|
||||
std::string t(source());
|
||||
|
||||
ss1 << "1234";
|
||||
ss2 << source();
|
||||
ss3 << "123" << source();
|
||||
ss4 << source() << "456";
|
||||
ss5 << t;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // tainted [NOT DETECTED]
|
||||
sink(ss3); // tainted [NOT DETECTED]
|
||||
sink(ss4); // tainted [NOT DETECTED]
|
||||
sink(ss5); // tainted [NOT DETECTED]
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // tainted [NOT DETECTED]
|
||||
sink(ss3.str()); // tainted [NOT DETECTED]
|
||||
sink(ss4.str()); // tainted [NOT DETECTED]
|
||||
sink(ss5.str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_stringstream_int(int source)
|
||||
{
|
||||
std::stringstream ss1, ss2;
|
||||
|
||||
ss1 << 1234;
|
||||
ss2 << source;
|
||||
|
||||
sink(ss1);
|
||||
sink(ss2); // tainted [NOT DETECTED]
|
||||
sink(ss1.str());
|
||||
sink(ss2.str()); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
char *user_input() {
|
||||
return source();
|
||||
}
|
||||
|
||||
void sink(const char *filename, const char *mode);
|
||||
|
||||
void test_strings2()
|
||||
{
|
||||
string path1 = user_input();
|
||||
sink(path1.c_str(), "r"); // tainted
|
||||
|
||||
string path2;
|
||||
path2 = user_input();
|
||||
sink(path2.c_str(), "r"); // tainted
|
||||
|
||||
string path3(user_input());
|
||||
sink(path3.c_str(), "r"); // tainted
|
||||
}
|
||||
|
||||
void test_string3()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string4()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
// convert back std::string -> char *
|
||||
cs = ss.c_str();
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string_constructors_assignments()
|
||||
{
|
||||
{
|
||||
std::string s1("hello");
|
||||
std::string s2 = "hello";
|
||||
std::string s3;
|
||||
s3 = "hello";
|
||||
|
||||
sink(s1);
|
||||
sink(s2);
|
||||
sink(s3);
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1(source());
|
||||
std::string s2 = source();
|
||||
std::string s3;
|
||||
s3 = source();
|
||||
|
||||
sink(s1); // tainted
|
||||
sink(s2); // tainted
|
||||
sink(s3); // tainted
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1;
|
||||
std::string s2 = s1;
|
||||
std::string s3;
|
||||
s3 = s1;
|
||||
|
||||
sink(s1);
|
||||
sink(s2);
|
||||
sink(s3);
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1 = std::string(source());
|
||||
std::string s2;
|
||||
s2 = std::string(source());
|
||||
|
||||
sink(s1); // tainted
|
||||
sink(s2); // tainted
|
||||
}
|
||||
}
|
||||
|
||||
void sink(char) {}
|
||||
|
||||
void test_range_based_for_loop_string() {
|
||||
std::string s(source());
|
||||
for(char c : s) {
|
||||
sink(c); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
for(std::string::iterator it = s.begin(); it != s.end(); ++it) {
|
||||
sink(*it); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
for(char& c : s) {
|
||||
sink(c); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
const std::string const_s(source());
|
||||
for(const char& c : const_s) {
|
||||
sink(c); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
namespace std {
|
||||
template <class T>
|
||||
class vector {
|
||||
private:
|
||||
void *data_;
|
||||
public:
|
||||
vector(int size);
|
||||
|
||||
T& operator[](int idx);
|
||||
const T& operator[](int idx) const;
|
||||
|
||||
typedef std::iterator<random_access_iterator_tag, T> iterator;
|
||||
typedef std::iterator<random_access_iterator_tag, const T> const_iterator;
|
||||
|
||||
iterator begin() noexcept;
|
||||
iterator end() noexcept;
|
||||
|
||||
const_iterator begin() const noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
};
|
||||
}
|
||||
|
||||
void sink(int);
|
||||
|
||||
void test_range_based_for_loop_vector(int source1) {
|
||||
// Tainting the vector by allocating a tainted length. This doesn't represent
|
||||
// how a vector would typically get tainted, but it allows this test to avoid
|
||||
// being concerned with std::vector modeling.
|
||||
std::vector<int> v(source1);
|
||||
|
||||
for(int x : v) {
|
||||
sink(x); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
for(std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
|
||||
sink(*it); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
for(int& x : v) {
|
||||
sink(x); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
const std::vector<int> const_v(source1);
|
||||
for(const int& x : const_v) {
|
||||
sink(x); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
}
|
||||
169
cpp/ql/test/library-tests/dataflow/taint-tests/stl.h
Normal file
169
cpp/ql/test/library-tests/dataflow/taint-tests/stl.h
Normal file
@@ -0,0 +1,169 @@
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
// --- string ---
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class charT> struct char_traits;
|
||||
|
||||
typedef size_t streamsize;
|
||||
|
||||
struct ptrdiff_t;
|
||||
|
||||
template <class iterator_category,
|
||||
class value_type,
|
||||
class difference_type = ptrdiff_t,
|
||||
class pointer_type = value_type*,
|
||||
class reference_type = value_type&>
|
||||
struct iterator {
|
||||
iterator &operator++();
|
||||
iterator operator++(int);
|
||||
bool operator==(iterator other) const;
|
||||
bool operator!=(iterator other) const;
|
||||
reference_type operator*() const;
|
||||
};
|
||||
|
||||
struct input_iterator_tag {};
|
||||
struct forward_iterator_tag : public input_iterator_tag {};
|
||||
struct bidirectional_iterator_tag : public forward_iterator_tag {};
|
||||
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
|
||||
|
||||
template <class T> class allocator {
|
||||
public:
|
||||
allocator() throw();
|
||||
typedef size_t size_type;
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
|
||||
class basic_string {
|
||||
public:
|
||||
typedef typename Allocator::size_type size_type;
|
||||
static const size_type npos = -1;
|
||||
|
||||
explicit basic_string(const Allocator& a = Allocator());
|
||||
basic_string(const charT* s, const Allocator& a = Allocator());
|
||||
|
||||
const charT* c_str() const;
|
||||
charT* data() noexcept;
|
||||
size_t length() const;
|
||||
|
||||
typedef std::iterator<random_access_iterator_tag, charT> iterator;
|
||||
typedef std::iterator<random_access_iterator_tag, const charT> const_iterator;
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
const_iterator cbegin() const;
|
||||
const_iterator cend() const;
|
||||
|
||||
template<class T> basic_string& operator+=(const T& t);
|
||||
basic_string& operator+=(const charT* s);
|
||||
basic_string& append(const basic_string& str);
|
||||
basic_string& append(const charT* s);
|
||||
basic_string& append(size_type n, charT c);
|
||||
basic_string& assign(const basic_string& str);
|
||||
basic_string& assign(size_type n, charT c);
|
||||
basic_string& insert(size_type pos, const basic_string& str);
|
||||
basic_string& insert(size_type pos, size_type n, charT c);
|
||||
basic_string& replace(size_type pos1, size_type n1, const basic_string& str);
|
||||
basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c);
|
||||
size_type copy(charT* s, size_type n, size_type pos = 0) const;
|
||||
void clear() noexcept;
|
||||
basic_string substr(size_type pos = 0, size_type n = npos) const;
|
||||
void swap(basic_string& s) noexcept/*(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value)*/;
|
||||
};
|
||||
|
||||
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> operator+(const basic_string<charT, traits, Allocator>& lhs, const basic_string<charT, traits, Allocator>& rhs);
|
||||
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> operator+(const basic_string<charT, traits, Allocator>& lhs, const charT* rhs);
|
||||
|
||||
typedef basic_string<char> string;
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
basic_istream<charT,traits>& operator>>(int& n);
|
||||
};
|
||||
|
||||
template <class charT, class traits = char_traits<charT> >
|
||||
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||
public:
|
||||
typedef charT char_type;
|
||||
basic_ostream<charT,traits>& write(const char_type* s, streamsize n);
|
||||
|
||||
basic_ostream<charT, traits>& operator<<(int n);
|
||||
};
|
||||
|
||||
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
|
||||
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
|
||||
|
||||
template<class charT, class traits = char_traits<charT>>
|
||||
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
|
||||
public:
|
||||
};
|
||||
|
||||
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
|
||||
class basic_stringstream : public basic_iostream<charT, traits> {
|
||||
public:
|
||||
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
|
||||
|
||||
basic_string<charT, traits, Allocator> str() const;
|
||||
};
|
||||
|
||||
using stringstream = basic_stringstream<char>;
|
||||
}
|
||||
|
||||
// --- vector ---
|
||||
|
||||
namespace std {
|
||||
template<class T, class Allocator = allocator<T>>
|
||||
class vector {
|
||||
public:
|
||||
using value_type = T;
|
||||
using reference = value_type&;
|
||||
using const_reference = const value_type&;
|
||||
using size_type = unsigned int;
|
||||
using iterator = std::iterator<random_access_iterator_tag, T>;
|
||||
using const_iterator = std::iterator<random_access_iterator_tag, const T>;
|
||||
|
||||
vector() noexcept(noexcept(Allocator())) : vector(Allocator()) { }
|
||||
explicit vector(const Allocator&) noexcept;
|
||||
explicit vector(size_type n, const Allocator& = Allocator());
|
||||
vector(size_type n, const T& value, const Allocator& = Allocator());
|
||||
~vector();
|
||||
|
||||
vector& operator=(const vector& x);
|
||||
vector& operator=(vector&& x) noexcept/*(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value)*/;
|
||||
|
||||
iterator begin() noexcept;
|
||||
const_iterator begin() const noexcept;
|
||||
iterator end() noexcept;
|
||||
const_iterator end() const noexcept;
|
||||
|
||||
size_type size() const noexcept;
|
||||
|
||||
reference operator[](size_type n);
|
||||
const_reference operator[](size_type n) const;
|
||||
const_reference at(size_type n) const;
|
||||
reference at(size_type n);
|
||||
reference front();
|
||||
const_reference front() const;
|
||||
reference back();
|
||||
const_reference back() const;
|
||||
|
||||
T* data() noexcept;
|
||||
const T* data() const noexcept;
|
||||
|
||||
void push_back(const T& x);
|
||||
void push_back(T&& x);
|
||||
|
||||
iterator insert(const_iterator position, const T& x);
|
||||
iterator insert(const_iterator position, T&& x);
|
||||
iterator insert(const_iterator position, size_type n, const T& x);
|
||||
|
||||
void swap(vector&) noexcept/*(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value)*/;
|
||||
|
||||
void clear() noexcept;
|
||||
};
|
||||
}
|
||||
323
cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
Normal file
323
cpp/ql/test/library-tests/dataflow/taint-tests/string.cpp
Normal file
@@ -0,0 +1,323 @@
|
||||
|
||||
#include "stl.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
char *source();
|
||||
|
||||
namespace ns_char
|
||||
{
|
||||
char source();
|
||||
}
|
||||
|
||||
char *user_input() {
|
||||
return source();
|
||||
}
|
||||
|
||||
void sink(const char *s) {};
|
||||
void sink(const std::string &s) {};
|
||||
void sink(const char *filename, const char *mode);
|
||||
void sink(char) {}
|
||||
|
||||
void test_string()
|
||||
{
|
||||
char *a = source();
|
||||
std::string b("123");
|
||||
std::string c(source());
|
||||
|
||||
sink(a); // tainted
|
||||
sink(b);
|
||||
sink(c); // tainted
|
||||
sink(b.c_str());
|
||||
sink(c.c_str()); // tainted
|
||||
}
|
||||
|
||||
void test_strings2()
|
||||
{
|
||||
string path1 = user_input();
|
||||
sink(path1.c_str(), "r"); // tainted
|
||||
|
||||
string path2;
|
||||
path2 = user_input();
|
||||
sink(path2.c_str(), "r"); // tainted
|
||||
|
||||
string path3(user_input());
|
||||
sink(path3.c_str(), "r"); // tainted
|
||||
}
|
||||
|
||||
void test_string3()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string4()
|
||||
{
|
||||
const char *cs = source();
|
||||
|
||||
// convert char * -> std::string
|
||||
std::string ss(cs);
|
||||
|
||||
// convert back std::string -> char *
|
||||
cs = ss.c_str();
|
||||
|
||||
sink(cs); // tainted
|
||||
sink(ss); // tainted
|
||||
}
|
||||
|
||||
void test_string_constructors_assignments()
|
||||
{
|
||||
{
|
||||
std::string s1("hello");
|
||||
std::string s2 = "hello";
|
||||
std::string s3;
|
||||
s3 = "hello";
|
||||
|
||||
sink(s1);
|
||||
sink(s2);
|
||||
sink(s3);
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1(source());
|
||||
std::string s2 = source();
|
||||
std::string s3;
|
||||
s3 = source();
|
||||
|
||||
sink(s1); // tainted
|
||||
sink(s2); // tainted
|
||||
sink(s3); // tainted
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1;
|
||||
std::string s2 = s1;
|
||||
std::string s3;
|
||||
s3 = s1;
|
||||
|
||||
sink(s1);
|
||||
sink(s2);
|
||||
sink(s3);
|
||||
}
|
||||
|
||||
{
|
||||
std::string s1 = std::string(source());
|
||||
std::string s2;
|
||||
s2 = std::string(source());
|
||||
|
||||
sink(s1); // tainted
|
||||
sink(s2); // tainted
|
||||
}
|
||||
}
|
||||
|
||||
void test_range_based_for_loop_string() {
|
||||
std::string s(source());
|
||||
for(char c : s) {
|
||||
sink(c); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
for(std::string::iterator it = s.begin(); it != s.end(); ++it) {
|
||||
sink(*it); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
for(char& c : s) {
|
||||
sink(c); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
const std::string const_s(source());
|
||||
for(const char& c : const_s) {
|
||||
sink(c); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
}
|
||||
|
||||
void test_string_append() {
|
||||
{
|
||||
std::string s1("hello");
|
||||
std::string s2(source());
|
||||
|
||||
sink(s1 + s1);
|
||||
sink(s1 + s2); // tainted
|
||||
sink(s2 + s1); // tainted
|
||||
sink(s2 + s2); // tainted
|
||||
|
||||
sink(s1 + " world");
|
||||
sink(s1 + source()); // tainted
|
||||
}
|
||||
|
||||
{
|
||||
std::string s3("abc");
|
||||
std::string s4(source());
|
||||
std::string s5, s6, s7, s8, s9;
|
||||
|
||||
s5 = s3 + s4;
|
||||
sink(s5); // tainted
|
||||
|
||||
s6 = s3;
|
||||
s6 += s4;
|
||||
sink(s6); // tainted
|
||||
|
||||
s7 = s3;
|
||||
s7 += source();
|
||||
s7 += " ";
|
||||
sink(s7); // tainted
|
||||
|
||||
s8 = s3;
|
||||
s8.append(s4);
|
||||
sink(s8); // tainted
|
||||
|
||||
s9 = s3;
|
||||
s9.append(source());
|
||||
s9.append(" ");
|
||||
sink(s9); // tainted
|
||||
}
|
||||
|
||||
{
|
||||
std::string s10("abc");
|
||||
char c = ns_char::source();
|
||||
|
||||
s10.append(1, c);
|
||||
sink(s10); // tainted
|
||||
}
|
||||
}
|
||||
|
||||
void test_string_assign() {
|
||||
std::string s1("hello");
|
||||
std::string s2(source());
|
||||
char c = ns_char::source();
|
||||
std::string s3, s4, s5;
|
||||
std::string s6(source());
|
||||
|
||||
sink(s3.assign(s1));
|
||||
sink(s3);
|
||||
|
||||
sink(s4.assign(s2)); // tainted
|
||||
sink(s4); // tainted
|
||||
|
||||
sink(s5.assign(10, c)); // tainted
|
||||
sink(s5); // tainted
|
||||
|
||||
sink(s6.assign(s1));
|
||||
sink(s6); // [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
void test_string_insert() {
|
||||
std::string s1("hello");
|
||||
std::string s2(source());
|
||||
char c = ns_char::source();
|
||||
std::string s3, s4, s5, s6;
|
||||
|
||||
s3 = s1;
|
||||
sink(s3.insert(0, s1));
|
||||
sink(s3);
|
||||
|
||||
s4 = s2;
|
||||
sink(s4.insert(0, s1)); // tainted
|
||||
sink(s4); // tainted
|
||||
|
||||
s5 = s1;
|
||||
sink(s5.insert(0, s2)); // tainted
|
||||
sink(s5); // tainted
|
||||
|
||||
s6 = s1;
|
||||
sink(s6.insert(0, 10, c)); // tainted
|
||||
sink(s6); // tainted
|
||||
}
|
||||
|
||||
void test_string_replace() {
|
||||
std::string s1("hello");
|
||||
std::string s2(source());
|
||||
char c = ns_char::source();
|
||||
std::string s3, s4, s5, s6;
|
||||
|
||||
s3 = s1;
|
||||
sink(s3.replace(1, 2, s1));
|
||||
sink(s3);
|
||||
|
||||
s4 = s2;
|
||||
sink(s4.replace(1, 2, s1)); // tainted
|
||||
sink(s4); // tainted
|
||||
|
||||
s5 = s1;
|
||||
sink(s5.replace(1, 2, s2)); // tainted
|
||||
sink(s5); // tainted
|
||||
|
||||
s6 = s1;
|
||||
sink(s6.replace(1, 2, 10, c)); // tainted
|
||||
sink(s6); // tainted
|
||||
}
|
||||
|
||||
void test_string_copy() {
|
||||
char b1[1024] = {0};
|
||||
char b2[1024] = {0};
|
||||
std::string s1("hello");
|
||||
std::string s2(source());
|
||||
|
||||
s1.copy(b1, s1.length(), 0);
|
||||
sink(b1);
|
||||
|
||||
s2.copy(b2, s1.length(), 0);
|
||||
sink(b2); // tainted
|
||||
}
|
||||
|
||||
void test_string_swap() {
|
||||
std::string s1("hello");
|
||||
std::string s2(source());
|
||||
std::string s3("world");
|
||||
std::string s4(source());
|
||||
|
||||
sink(s1);
|
||||
sink(s2); // tainted
|
||||
sink(s3);
|
||||
sink(s4); // tainted
|
||||
|
||||
s1.swap(s2);
|
||||
s4.swap(s3);
|
||||
|
||||
sink(s1); // tainted
|
||||
sink(s2); // [FALSE POSITIVE]
|
||||
sink(s3); // tainted
|
||||
sink(s4); // [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
void test_string_clear() {
|
||||
std::string s1(source());
|
||||
std::string s2(source());
|
||||
std::string s3(source());
|
||||
|
||||
sink(s1); // tainted
|
||||
sink(s2); // tainted
|
||||
sink(s3); // tainted
|
||||
|
||||
s1.clear();
|
||||
s2 = "";
|
||||
s3 = s3;
|
||||
|
||||
sink(s1); // [FALSE POSITIVE]
|
||||
sink(s2);
|
||||
sink(s3); // tainted
|
||||
}
|
||||
|
||||
void test_string_data()
|
||||
{
|
||||
std::string a("123");
|
||||
std::string b(source());
|
||||
|
||||
sink(a.data());
|
||||
sink(b.data()); // tainted
|
||||
sink(a.length());
|
||||
sink(b.length());
|
||||
}
|
||||
|
||||
void test_string_substr()
|
||||
{
|
||||
std::string a("123");
|
||||
std::string b(source());
|
||||
|
||||
sink(a.substr(0, a.length()));
|
||||
sink(b.substr(0, b.length())); // tainted
|
||||
}
|
||||
@@ -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]
|
||||
}
|
||||
@@ -32,27 +32,65 @@
|
||||
| movableclass.cpp:55:8:55:9 | s2 | movableclass.cpp:52:23:52:28 | call to source |
|
||||
| movableclass.cpp:64:8:64:9 | s2 | movableclass.cpp:23:55:23:60 | call to source |
|
||||
| movableclass.cpp:65:11:65:11 | call to operator= | movableclass.cpp:65:13:65:18 | call to source |
|
||||
| stl.cpp:101:7:101:7 | a | stl.cpp:97:12:97:17 | call to source |
|
||||
| stl.cpp:103:7:103:7 | c | stl.cpp:99:16:99:21 | call to source |
|
||||
| stl.cpp:105:9:105:13 | call to c_str | stl.cpp:99:16:99:21 | call to source |
|
||||
| stl.cpp:155:13:155:17 | call to c_str | stl.cpp:147:10:147:15 | call to source |
|
||||
| stl.cpp:159:13:159:17 | call to c_str | stl.cpp:147:10:147:15 | call to source |
|
||||
| stl.cpp:162:13:162:17 | call to c_str | stl.cpp:147:10:147:15 | call to source |
|
||||
| stl.cpp:172:7:172:8 | cs | stl.cpp:167:19:167:24 | call to source |
|
||||
| stl.cpp:173:7:173:8 | ss | stl.cpp:167:19:167:24 | call to source |
|
||||
| stl.cpp:186:7:186:8 | cs | stl.cpp:178:19:178:24 | call to source |
|
||||
| stl.cpp:187:7:187:8 | ss | stl.cpp:178:19:178:24 | call to source |
|
||||
| stl.cpp:209:8:209:9 | s1 | stl.cpp:204:18:204:23 | call to source |
|
||||
| stl.cpp:210:8:210:9 | s2 | stl.cpp:205:20:205:25 | call to source |
|
||||
| stl.cpp:211:8:211:9 | s3 | stl.cpp:207:8:207:13 | call to source |
|
||||
| stl.cpp:230:8:230:9 | s1 | stl.cpp:226:32:226:37 | call to source |
|
||||
| stl.cpp:231:8:231:9 | s2 | stl.cpp:228:20:228:25 | call to source |
|
||||
| stl.cpp:240:8:240:8 | c | stl.cpp:238:16:238:21 | call to source |
|
||||
| stl.cpp:248:8:248:8 | c | stl.cpp:238:16:238:21 | call to source |
|
||||
| stl.cpp:253:8:253:8 | c | stl.cpp:251:28:251:33 | call to source |
|
||||
| stl.cpp:295:8:295:8 | x | stl.cpp:288:43:288:49 | source1 |
|
||||
| stl.cpp:303:8:303:8 | x | stl.cpp:288:43:288:49 | source1 |
|
||||
| stl.cpp:308:8:308:8 | x | stl.cpp:288:43:288:49 | source1 |
|
||||
| string.cpp:28:7:28:7 | a | string.cpp:24:12:24:17 | call to source |
|
||||
| string.cpp:30:7:30:7 | c | string.cpp:26:16:26:21 | call to source |
|
||||
| string.cpp:32:9:32:13 | call to c_str | string.cpp:26:16:26:21 | call to source |
|
||||
| string.cpp:38:13:38:17 | call to c_str | string.cpp:14:10:14:15 | call to source |
|
||||
| string.cpp:42:13:42:17 | call to c_str | string.cpp:14:10:14:15 | call to source |
|
||||
| string.cpp:45:13:45:17 | call to c_str | string.cpp:14:10:14:15 | call to source |
|
||||
| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:24 | call to source |
|
||||
| string.cpp:56:7:56:8 | ss | string.cpp:50:19:50:24 | call to source |
|
||||
| string.cpp:69:7:69:8 | cs | string.cpp:61:19:61:24 | call to source |
|
||||
| string.cpp:70:7:70:8 | ss | string.cpp:61:19:61:24 | call to source |
|
||||
| string.cpp:92:8:92:9 | s1 | string.cpp:87:18:87:23 | call to source |
|
||||
| string.cpp:93:8:93:9 | s2 | string.cpp:88:20:88:25 | call to source |
|
||||
| string.cpp:94:8:94:9 | s3 | string.cpp:90:8:90:13 | call to source |
|
||||
| string.cpp:113:8:113:9 | s1 | string.cpp:109:32:109:37 | call to source |
|
||||
| string.cpp:114:8:114:9 | s2 | string.cpp:111:20:111:25 | call to source |
|
||||
| string.cpp:121:8:121:8 | c | string.cpp:119:16:119:21 | call to source |
|
||||
| string.cpp:129:8:129:8 | c | string.cpp:119:16:119:21 | call to source |
|
||||
| string.cpp:134:8:134:8 | c | string.cpp:132:28:132:33 | call to source |
|
||||
| string.cpp:144:11:144:11 | call to operator+ | string.cpp:141:18:141:23 | call to source |
|
||||
| string.cpp:145:11:145:11 | call to operator+ | string.cpp:141:18:141:23 | call to source |
|
||||
| string.cpp:146:11:146:11 | call to operator+ | string.cpp:141:18:141:23 | call to source |
|
||||
| string.cpp:149:11:149:11 | call to operator+ | string.cpp:149:13:149:18 | call to source |
|
||||
| string.cpp:158:8:158:9 | s5 | string.cpp:154:18:154:23 | call to source |
|
||||
| string.cpp:162:8:162:9 | s6 | string.cpp:154:18:154:23 | call to source |
|
||||
| string.cpp:167:8:167:9 | s7 | string.cpp:165:9:165:14 | call to source |
|
||||
| string.cpp:171:8:171:9 | s8 | string.cpp:154:18:154:23 | call to source |
|
||||
| string.cpp:176:8:176:9 | s9 | string.cpp:174:13:174:18 | call to source |
|
||||
| string.cpp:184:8:184:10 | s10 | string.cpp:181:12:181:26 | call to source |
|
||||
| string.cpp:198:10:198:15 | call to assign | string.cpp:190:17:190:22 | call to source |
|
||||
| string.cpp:199:7:199:8 | s4 | string.cpp:190:17:190:22 | call to source |
|
||||
| string.cpp:201:10:201:15 | call to assign | string.cpp:191:11:191:25 | call to source |
|
||||
| string.cpp:202:7:202:8 | s5 | string.cpp:191:11:191:25 | call to source |
|
||||
| string.cpp:205:7:205:8 | s6 | string.cpp:193:17:193:22 | call to source |
|
||||
| string.cpp:219:10:219:15 | call to insert | string.cpp:210:17:210:22 | call to source |
|
||||
| string.cpp:220:7:220:8 | s4 | string.cpp:210:17:210:22 | call to source |
|
||||
| string.cpp:223:10:223:15 | call to insert | string.cpp:210:17:210:22 | call to source |
|
||||
| string.cpp:224:7:224:8 | s5 | string.cpp:210:17:210:22 | call to source |
|
||||
| string.cpp:227:10:227:15 | call to insert | string.cpp:211:11:211:25 | call to source |
|
||||
| string.cpp:228:7:228:8 | s6 | string.cpp:211:11:211:25 | call to source |
|
||||
| string.cpp:242:10:242:16 | call to replace | string.cpp:233:17:233:22 | call to source |
|
||||
| string.cpp:243:7:243:8 | s4 | string.cpp:233:17:233:22 | call to source |
|
||||
| string.cpp:246:10:246:16 | call to replace | string.cpp:233:17:233:22 | call to source |
|
||||
| string.cpp:247:7:247:8 | s5 | string.cpp:233:17:233:22 | call to source |
|
||||
| string.cpp:250:10:250:16 | call to replace | string.cpp:234:11:234:25 | call to source |
|
||||
| string.cpp:251:7:251:8 | s6 | string.cpp:234:11:234:25 | call to source |
|
||||
| string.cpp:264:7:264:8 | b2 | string.cpp:258:17:258:22 | call to source |
|
||||
| string.cpp:274:7:274:8 | s2 | string.cpp:269:17:269:22 | call to source |
|
||||
| string.cpp:276:7:276:8 | s4 | string.cpp:271:17:271:22 | call to source |
|
||||
| string.cpp:281:7:281:8 | s1 | string.cpp:269:17:269:22 | call to source |
|
||||
| string.cpp:282:7:282:8 | s2 | string.cpp:269:17:269:22 | call to source |
|
||||
| string.cpp:283:7:283:8 | s3 | string.cpp:271:17:271:22 | call to source |
|
||||
| string.cpp:284:7:284:8 | s4 | string.cpp:271:17:271:22 | call to source |
|
||||
| string.cpp:292:7:292:8 | s1 | string.cpp:288:17:288:22 | call to source |
|
||||
| string.cpp:293:7:293:8 | s2 | string.cpp:289:17:289:22 | call to source |
|
||||
| string.cpp:294:7:294:8 | s3 | string.cpp:290:17:290:22 | call to source |
|
||||
| string.cpp:300:7:300:8 | s1 | string.cpp:288:17:288:22 | call to source |
|
||||
| string.cpp:302:7:302:8 | s3 | string.cpp:290:17:290:22 | call to source |
|
||||
| string.cpp:311:9:311:12 | call to data | string.cpp:308:16:308:21 | call to source |
|
||||
| string.cpp:322:9:322:14 | call to substr | string.cpp:319:16:319:21 | call to source |
|
||||
| structlikeclass.cpp:35:8:35:9 | s1 | structlikeclass.cpp:29:22:29:27 | call to source |
|
||||
| structlikeclass.cpp:36:8:36:9 | s2 | structlikeclass.cpp:30:24:30:29 | call to source |
|
||||
| structlikeclass.cpp:37:8:37:9 | s3 | structlikeclass.cpp:29:22:29:27 | call to source |
|
||||
@@ -159,3 +197,21 @@
|
||||
| taint.cpp:470:7:470:7 | x | taint.cpp:462:6:462:11 | call to source |
|
||||
| taint.cpp:471:7:471:7 | y | taint.cpp:462:6:462:11 | call to source |
|
||||
| taint.cpp:485:7:485:10 | line | taint.cpp:480:26:480:32 | source1 |
|
||||
| vector.cpp:20:8:20:8 | x | vector.cpp:16:43:16:49 | source1 |
|
||||
| vector.cpp:28:8:28:8 | x | vector.cpp:16:43:16:49 | source1 |
|
||||
| vector.cpp:33:8:33:8 | x | vector.cpp:16:43:16:49 | source1 |
|
||||
| vector.cpp:70:7:70:8 | v5 | vector.cpp:69:15:69:20 | call to source |
|
||||
| vector.cpp:71:10:71:14 | call to front | vector.cpp:69:15:69:20 | call to source |
|
||||
| vector.cpp:72:10:72:13 | call to back | vector.cpp:69:15:69:20 | call to source |
|
||||
| vector.cpp:109:7:109:8 | v1 | vector.cpp:106:15:106:20 | call to source |
|
||||
| vector.cpp:112:7:112:8 | v4 | vector.cpp:107:15:107:20 | call to source |
|
||||
| vector.cpp:117:7:117:8 | v1 | vector.cpp:106:15:106:20 | call to source |
|
||||
| vector.cpp:118:7:118:8 | v2 | vector.cpp:106:15:106:20 | call to source |
|
||||
| vector.cpp:119:7:119:8 | v3 | vector.cpp:107:15:107:20 | call to source |
|
||||
| vector.cpp:120:7:120:8 | v4 | vector.cpp:107:15:107:20 | call to source |
|
||||
| vector.cpp:130:7:130:8 | v1 | vector.cpp:126:15:126:20 | call to source |
|
||||
| vector.cpp:131:7:131:8 | v2 | vector.cpp:127:15:127:20 | call to source |
|
||||
| vector.cpp:132:7:132:8 | v3 | vector.cpp:128:15:128:20 | call to source |
|
||||
| vector.cpp:139:7:139:8 | v1 | vector.cpp:126:15:126:20 | call to source |
|
||||
| vector.cpp:140:7:140:8 | v2 | vector.cpp:127:15:127:20 | call to source |
|
||||
| vector.cpp:141:7:141:8 | v3 | vector.cpp:128:15:128:20 | call to source |
|
||||
|
||||
@@ -30,26 +30,64 @@
|
||||
| movableclass.cpp:55:8:55:9 | movableclass.cpp:52:23:52:28 | AST only |
|
||||
| movableclass.cpp:64:8:64:9 | movableclass.cpp:23:55:23:60 | AST only |
|
||||
| movableclass.cpp:65:11:65:11 | movableclass.cpp:65:13:65:18 | AST only |
|
||||
| stl.cpp:103:7:103:7 | stl.cpp:99:16:99:21 | AST only |
|
||||
| stl.cpp:105:9:105:13 | stl.cpp:99:16:99:21 | AST only |
|
||||
| stl.cpp:155:13:155:17 | stl.cpp:147:10:147:15 | AST only |
|
||||
| stl.cpp:159:13:159:17 | stl.cpp:147:10:147:15 | AST only |
|
||||
| stl.cpp:162:13:162:17 | stl.cpp:147:10:147:15 | AST only |
|
||||
| stl.cpp:172:7:172:8 | stl.cpp:167:19:167:26 | IR only |
|
||||
| stl.cpp:173:7:173:8 | stl.cpp:167:19:167:24 | AST only |
|
||||
| stl.cpp:186:7:186:8 | stl.cpp:178:19:178:24 | AST only |
|
||||
| stl.cpp:187:7:187:8 | stl.cpp:178:19:178:24 | AST only |
|
||||
| stl.cpp:209:8:209:9 | stl.cpp:204:18:204:23 | AST only |
|
||||
| stl.cpp:210:8:210:9 | stl.cpp:205:20:205:25 | AST only |
|
||||
| stl.cpp:211:8:211:9 | stl.cpp:207:8:207:13 | AST only |
|
||||
| stl.cpp:230:8:230:9 | stl.cpp:226:32:226:37 | AST only |
|
||||
| stl.cpp:231:8:231:9 | stl.cpp:228:20:228:25 | AST only |
|
||||
| stl.cpp:240:8:240:8 | stl.cpp:238:16:238:21 | AST only |
|
||||
| stl.cpp:248:8:248:8 | stl.cpp:238:16:238:21 | AST only |
|
||||
| stl.cpp:253:8:253:8 | stl.cpp:251:28:251:33 | AST only |
|
||||
| stl.cpp:295:8:295:8 | stl.cpp:288:43:288:49 | AST only |
|
||||
| stl.cpp:303:8:303:8 | stl.cpp:288:43:288:49 | AST only |
|
||||
| stl.cpp:308:8:308:8 | stl.cpp:288:43:288:49 | AST only |
|
||||
| string.cpp:30:7:30:7 | string.cpp:26:16:26:21 | AST only |
|
||||
| string.cpp:32:9:32:13 | string.cpp:26:16:26:21 | AST only |
|
||||
| string.cpp:38:13:38:17 | string.cpp:14:10:14:15 | AST only |
|
||||
| string.cpp:42:13:42:17 | string.cpp:14:10:14:15 | AST only |
|
||||
| string.cpp:45:13:45:17 | string.cpp:14:10:14:15 | AST only |
|
||||
| string.cpp:55:7:55:8 | string.cpp:50:19:50:26 | IR only |
|
||||
| string.cpp:56:7:56:8 | string.cpp:50:19:50:24 | AST only |
|
||||
| string.cpp:69:7:69:8 | string.cpp:61:19:61:24 | AST only |
|
||||
| string.cpp:70:7:70:8 | string.cpp:61:19:61:24 | AST only |
|
||||
| string.cpp:92:8:92:9 | string.cpp:87:18:87:23 | AST only |
|
||||
| string.cpp:93:8:93:9 | string.cpp:88:20:88:25 | AST only |
|
||||
| string.cpp:94:8:94:9 | string.cpp:90:8:90:13 | AST only |
|
||||
| string.cpp:113:8:113:9 | string.cpp:109:32:109:37 | AST only |
|
||||
| string.cpp:114:8:114:9 | string.cpp:111:20:111:25 | AST only |
|
||||
| string.cpp:121:8:121:8 | string.cpp:119:16:119:21 | AST only |
|
||||
| string.cpp:129:8:129:8 | string.cpp:119:16:119:21 | AST only |
|
||||
| string.cpp:134:8:134:8 | string.cpp:132:28:132:33 | AST only |
|
||||
| string.cpp:144:11:144:11 | string.cpp:141:18:141:23 | AST only |
|
||||
| string.cpp:145:11:145:11 | string.cpp:141:18:141:23 | AST only |
|
||||
| string.cpp:146:11:146:11 | string.cpp:141:18:141:23 | AST only |
|
||||
| string.cpp:149:11:149:11 | string.cpp:149:13:149:18 | AST only |
|
||||
| string.cpp:158:8:158:9 | string.cpp:154:18:154:23 | AST only |
|
||||
| string.cpp:162:8:162:9 | string.cpp:154:18:154:23 | AST only |
|
||||
| string.cpp:167:8:167:9 | string.cpp:165:9:165:14 | AST only |
|
||||
| string.cpp:171:8:171:9 | string.cpp:154:18:154:23 | AST only |
|
||||
| string.cpp:176:8:176:9 | string.cpp:174:13:174:18 | AST only |
|
||||
| string.cpp:184:8:184:10 | string.cpp:181:12:181:26 | AST only |
|
||||
| string.cpp:198:10:198:15 | string.cpp:190:17:190:22 | AST only |
|
||||
| string.cpp:199:7:199:8 | string.cpp:190:17:190:22 | AST only |
|
||||
| string.cpp:201:10:201:15 | string.cpp:191:11:191:25 | AST only |
|
||||
| string.cpp:202:7:202:8 | string.cpp:191:11:191:25 | AST only |
|
||||
| string.cpp:205:7:205:8 | string.cpp:193:17:193:22 | AST only |
|
||||
| string.cpp:219:10:219:15 | string.cpp:210:17:210:22 | AST only |
|
||||
| string.cpp:220:7:220:8 | string.cpp:210:17:210:22 | AST only |
|
||||
| string.cpp:223:10:223:15 | string.cpp:210:17:210:22 | AST only |
|
||||
| string.cpp:224:7:224:8 | string.cpp:210:17:210:22 | AST only |
|
||||
| string.cpp:227:10:227:15 | string.cpp:211:11:211:25 | AST only |
|
||||
| string.cpp:228:7:228:8 | string.cpp:211:11:211:25 | AST only |
|
||||
| string.cpp:242:10:242:16 | string.cpp:233:17:233:22 | AST only |
|
||||
| string.cpp:243:7:243:8 | string.cpp:233:17:233:22 | AST only |
|
||||
| string.cpp:246:10:246:16 | string.cpp:233:17:233:22 | AST only |
|
||||
| string.cpp:247:7:247:8 | string.cpp:233:17:233:22 | AST only |
|
||||
| string.cpp:250:10:250:16 | string.cpp:234:11:234:25 | AST only |
|
||||
| string.cpp:251:7:251:8 | string.cpp:234:11:234:25 | AST only |
|
||||
| string.cpp:264:7:264:8 | string.cpp:258:17:258:22 | AST only |
|
||||
| string.cpp:274:7:274:8 | string.cpp:269:17:269:22 | AST only |
|
||||
| string.cpp:276:7:276:8 | string.cpp:271:17:271:22 | AST only |
|
||||
| string.cpp:281:7:281:8 | string.cpp:269:17:269:22 | AST only |
|
||||
| string.cpp:282:7:282:8 | string.cpp:269:17:269:22 | AST only |
|
||||
| string.cpp:283:7:283:8 | string.cpp:271:17:271:22 | AST only |
|
||||
| string.cpp:284:7:284:8 | string.cpp:271:17:271:22 | AST only |
|
||||
| string.cpp:292:7:292:8 | string.cpp:288:17:288:22 | AST only |
|
||||
| string.cpp:293:7:293:8 | string.cpp:289:17:289:22 | AST only |
|
||||
| string.cpp:294:7:294:8 | string.cpp:290:17:290:22 | AST only |
|
||||
| string.cpp:300:7:300:8 | string.cpp:288:17:288:22 | AST only |
|
||||
| string.cpp:302:7:302:8 | string.cpp:290:17:290:22 | AST only |
|
||||
| string.cpp:311:9:311:12 | string.cpp:308:16:308:21 | AST only |
|
||||
| string.cpp:322:9:322:14 | string.cpp:319:16:319:21 | AST only |
|
||||
| structlikeclass.cpp:35:8:35:9 | structlikeclass.cpp:29:22:29:27 | AST only |
|
||||
| structlikeclass.cpp:36:8:36:9 | structlikeclass.cpp:30:24:30:29 | AST only |
|
||||
| structlikeclass.cpp:37:8:37:9 | structlikeclass.cpp:29:22:29:27 | AST only |
|
||||
@@ -94,3 +132,21 @@
|
||||
| taint.cpp:446:7:446:7 | taint.cpp:445:14:445:28 | AST only |
|
||||
| taint.cpp:447:9:447:17 | taint.cpp:445:14:445:28 | AST only |
|
||||
| taint.cpp:471:7:471:7 | taint.cpp:462:6:462:11 | AST only |
|
||||
| vector.cpp:20:8:20:8 | vector.cpp:16:43:16:49 | AST only |
|
||||
| vector.cpp:28:8:28:8 | vector.cpp:16:43:16:49 | AST only |
|
||||
| vector.cpp:33:8:33:8 | vector.cpp:16:43:16:49 | AST only |
|
||||
| vector.cpp:70:7:70:8 | vector.cpp:69:15:69:20 | AST only |
|
||||
| vector.cpp:71:10:71:14 | vector.cpp:69:15:69:20 | AST only |
|
||||
| vector.cpp:72:10:72:13 | vector.cpp:69:15:69:20 | AST only |
|
||||
| vector.cpp:109:7:109:8 | vector.cpp:106:15:106:20 | AST only |
|
||||
| vector.cpp:112:7:112:8 | vector.cpp:107:15:107:20 | AST only |
|
||||
| vector.cpp:117:7:117:8 | vector.cpp:106:15:106:20 | AST only |
|
||||
| vector.cpp:118:7:118:8 | vector.cpp:106:15:106:20 | AST only |
|
||||
| vector.cpp:119:7:119:8 | vector.cpp:107:15:107:20 | AST only |
|
||||
| vector.cpp:120:7:120:8 | vector.cpp:107:15:107:20 | AST only |
|
||||
| vector.cpp:130:7:130:8 | vector.cpp:126:15:126:20 | AST only |
|
||||
| vector.cpp:131:7:131:8 | vector.cpp:127:15:127:20 | AST only |
|
||||
| vector.cpp:132:7:132:8 | vector.cpp:128:15:128:20 | AST only |
|
||||
| vector.cpp:139:7:139:8 | vector.cpp:126:15:126:20 | AST only |
|
||||
| vector.cpp:140:7:140:8 | vector.cpp:127:15:127:20 | AST only |
|
||||
| vector.cpp:141:7:141:8 | vector.cpp:128:15:128:20 | AST only |
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
| format.cpp:157:7:157:22 | (int)... | format.cpp:147:12:147:25 | call to source |
|
||||
| format.cpp:157:7:157:22 | access to array | format.cpp:147:12:147:25 | call to source |
|
||||
| format.cpp:158:7:158:27 | ... + ... | format.cpp:148:16:148:30 | call to source |
|
||||
| stl.cpp:101:7:101:7 | (const char *)... | stl.cpp:97:12:97:17 | call to source |
|
||||
| stl.cpp:101:7:101:7 | a | stl.cpp:97:12:97:17 | call to source |
|
||||
| stl.cpp:172:7:172:8 | cs | stl.cpp:167:19:167:24 | call to source |
|
||||
| stl.cpp:172:7:172:8 | cs | stl.cpp:167:19:167:26 | (const char *)... |
|
||||
| string.cpp:28:7:28:7 | (const char *)... | string.cpp:24:12:24:17 | call to source |
|
||||
| string.cpp:28:7:28:7 | a | string.cpp:24:12:24:17 | call to source |
|
||||
| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:24 | call to source |
|
||||
| string.cpp:55:7:55:8 | cs | string.cpp:50:19:50:26 | (const char *)... |
|
||||
| structlikeclass.cpp:38:8:38:9 | s4 | structlikeclass.cpp:33:8:33:13 | call to source |
|
||||
| structlikeclass.cpp:61:8:61:9 | s2 | structlikeclass.cpp:58:24:58:29 | call to source |
|
||||
| structlikeclass.cpp:62:8:62:20 | ... = ... | structlikeclass.cpp:62:13:62:18 | call to source |
|
||||
|
||||
143
cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
Normal file
143
cpp/ql/test/library-tests/dataflow/taint-tests/vector.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
|
||||
#include "stl.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int source();
|
||||
|
||||
namespace ns_char
|
||||
{
|
||||
char source();
|
||||
}
|
||||
|
||||
void sink(int);
|
||||
void sink(std::vector<int> &);
|
||||
|
||||
void test_range_based_for_loop_vector(int source1) {
|
||||
std::vector<int> v(100, source1);
|
||||
|
||||
for(int x : v) {
|
||||
sink(x); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
for(std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
|
||||
sink(*it); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
for(int& x : v) {
|
||||
sink(x); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
|
||||
const std::vector<int> const_v(100, source1);
|
||||
for(const int& x : const_v) {
|
||||
sink(x); // tainted [NOT DETECTED by IR]
|
||||
}
|
||||
}
|
||||
|
||||
void test_element_taint(int x) {
|
||||
std::vector<int> v1(10), v2(10), v3(10), v4(10), v5(10), v6(10), v7(10), v8(10), v9(10);
|
||||
|
||||
v1[0] = 0;
|
||||
v1[1] = 0;
|
||||
v1[x] = 0;
|
||||
v1.push_back(1);
|
||||
sink(v1);
|
||||
sink(v1[0]);
|
||||
sink(v1[1]);
|
||||
sink(v1[x]);
|
||||
sink(v1.front());
|
||||
sink(v1.back());
|
||||
|
||||
v2[0] = source();
|
||||
sink(v2); // tainted [NOT DETECTED]
|
||||
sink(v2[0]); // tainted [NOT DETECTED]
|
||||
sink(v2[1]);
|
||||
sink(v2[x]); // potentially tainted
|
||||
|
||||
v3 = v2;
|
||||
sink(v3); // tainted [NOT DETECTED]
|
||||
sink(v3[0]); // tainted [NOT DETECTED]
|
||||
sink(v3[1]);
|
||||
sink(v3[x]); // potentially tainted
|
||||
|
||||
v4[x] = source();
|
||||
sink(v4); // tainted [NOT DETECTED]
|
||||
sink(v4[0]); // potentially tainted
|
||||
sink(v4[1]); // potentially tainted
|
||||
sink(v4[x]); // tainted [NOT DETECTED]
|
||||
|
||||
v5.push_back(source());
|
||||
sink(v5); // tainted
|
||||
sink(v5.front()); // [FALSE POSITIVE]
|
||||
sink(v5.back()); // tainted
|
||||
|
||||
v6.data()[2] = source();
|
||||
sink(v6); // tainted [NOT DETECTED]
|
||||
sink(v6.data()[2]); // tainted [NOT DETECTED]
|
||||
|
||||
{
|
||||
const std::vector<int> &v7c = v7; // (workaround because our iterators don't convert to const_iterator)
|
||||
std::vector<int>::const_iterator it = v7c.begin();
|
||||
v7.insert(it, source());
|
||||
}
|
||||
sink(v7); // tainted [NOT DETECTED]
|
||||
sink(v7.front()); // tainted [NOT DETECTED]
|
||||
sink(v7.back());
|
||||
|
||||
{
|
||||
const std::vector<int> &v8c = v8;
|
||||
std::vector<int>::const_iterator it = v8c.begin();
|
||||
v8.insert(it, 10, ns_char::source());
|
||||
}
|
||||
sink(v8); // tainted [NOT DETECTED]
|
||||
sink(v8.front()); // tainted [NOT DETECTED]
|
||||
sink(v8.back());
|
||||
|
||||
v9.at(x) = source();
|
||||
sink(v9); // tainted [NOT DETECTED]
|
||||
sink(v9.at(0)); // potentially tainted
|
||||
sink(v9.at(1)); // potentially tainted
|
||||
sink(v9.at(x)); // tainted [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_vector_swap() {
|
||||
std::vector<int> v1(10), v2(10), v3(10), v4(10);
|
||||
|
||||
v1.push_back(source());
|
||||
v4.push_back(source());
|
||||
|
||||
sink(v1); // tainted
|
||||
sink(v2);
|
||||
sink(v3);
|
||||
sink(v4); // tainted
|
||||
|
||||
v1.swap(v2);
|
||||
v3.swap(v4);
|
||||
|
||||
sink(v1); // [FALSE POSITIVE]
|
||||
sink(v2); // tainted
|
||||
sink(v3); // tainted
|
||||
sink(v4); // [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
void test_vector_clear() {
|
||||
std::vector<int> v1(10), v2(10), v3(10), v4(10);
|
||||
|
||||
v1.push_back(source());
|
||||
v2.push_back(source());
|
||||
v3.push_back(source());
|
||||
|
||||
sink(v1); // tainted
|
||||
sink(v2); // tainted
|
||||
sink(v3); // tainted
|
||||
sink(v4);
|
||||
|
||||
v1.clear();
|
||||
v2 = v2;
|
||||
v3 = v4;
|
||||
|
||||
sink(v1); // [FALSE POSITIVE]
|
||||
sink(v2); // tainted
|
||||
sink(v3); // [FALSE POSITIVE]
|
||||
sink(v4);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
from VariableAccess expr
|
||||
select expr, lowerBound(expr)
|
||||
select expr, lowerBound(expr).toString()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
from VariableAccess expr
|
||||
select expr, upperBound(expr)
|
||||
select expr, upperBound(expr).toString()
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
@@ -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. |
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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). |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
3
csharp/.gitignore
vendored
3
csharp/.gitignore
vendored
@@ -10,4 +10,5 @@ csharp.log
|
||||
**/bin/Release
|
||||
*.tlog
|
||||
.vs
|
||||
*.user
|
||||
*.user
|
||||
.vscode/launch.json
|
||||
@@ -17,7 +17,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
|
||||
|
||||
protected abstract ArrayTypeSyntax TypeSyntax { get; }
|
||||
|
||||
public abstract InitializerExpressionSyntax Initializer { get; }
|
||||
public abstract InitializerExpressionSyntax Initializer { get; }
|
||||
|
||||
protected override void PopulateExpression(TextWriter trapFile)
|
||||
{
|
||||
@@ -48,7 +48,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
|
||||
ExprKind.INT_LITERAL,
|
||||
this,
|
||||
child,
|
||||
false,
|
||||
true,
|
||||
size.ToString());
|
||||
|
||||
new Expression(info);
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
else
|
||||
Expression.Create(Context, expr, this, 0);
|
||||
|
||||
Context.NumberOfLines(trapFile, symbol, this);
|
||||
Context.NumberOfLines(trapFile, BodyDeclaringSymbol, this);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
{
|
||||
trapFile.Write('<');
|
||||
// Encode the nullability of the type arguments in the label.
|
||||
// Type arguments with different nullability can result in
|
||||
// Type arguments with different nullability can result in
|
||||
// a constructed method with different nullability of its parameters and return type,
|
||||
// so we need to create a distinct database entity for it.
|
||||
trapFile.BuildList(",", m.symbol.GetAnnotatedTypeArguments(), (ta, tb0) => { AddSignatureTypeToId(m.Context, tb0, m.symbol, ta.Symbol); trapFile.Write((int)ta.Nullability); });
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
|
||||
public override string Name => symbol.GetName();
|
||||
|
||||
protected override IMethodSymbol BodyDeclaringSymbol => symbol.PartialImplementationPart ?? symbol;
|
||||
|
||||
public IMethodSymbol SourceDeclaration
|
||||
{
|
||||
get
|
||||
|
||||
@@ -95,14 +95,15 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
Context.BindComments(this, FullLocation);
|
||||
}
|
||||
|
||||
protected virtual T BodyDeclaringSymbol => symbol;
|
||||
|
||||
public BlockSyntax Block
|
||||
{
|
||||
get
|
||||
{
|
||||
return symbol.
|
||||
return BodyDeclaringSymbol.
|
||||
DeclaringSyntaxReferences.
|
||||
Select(r => r.GetSyntax()).
|
||||
SelectMany(n => n.ChildNodes()).
|
||||
SelectMany(r => r.GetSyntax().ChildNodes()).
|
||||
OfType<BlockSyntax>().
|
||||
FirstOrDefault();
|
||||
}
|
||||
@@ -112,7 +113,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
{
|
||||
get
|
||||
{
|
||||
return symbol.
|
||||
return BodyDeclaringSymbol.
|
||||
DeclaringSyntaxReferences.
|
||||
SelectMany(r => r.GetSyntax().ChildNodes()).
|
||||
OfType<ArrowExpressionClauseSyntax>().
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Semmle.Extraction.CommentProcessing
|
||||
|
||||
class LocationComparer : IComparer<Location>
|
||||
{
|
||||
public int Compare(Location l1, Location l2) => CommentProcessor.Compare(l1, l2);
|
||||
public int Compare(Location? l1, Location? l2) => CommentProcessor.Compare(l1, l2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -44,8 +44,12 @@ namespace Semmle.Extraction.CommentProcessing
|
||||
/// <param name="l1">First location</param>
|
||||
/// <param name="l2">Second location</param>
|
||||
/// <returns><0 if l1 before l2, >0 if l1 after l2, else 0.</returns>
|
||||
static int Compare(Location l1, Location l2)
|
||||
static int Compare(Location? l1, Location? l2)
|
||||
{
|
||||
if (object.ReferenceEquals(l1, l2)) return 0;
|
||||
if (l1 == null) return -1;
|
||||
if (l2 == null) return 1;
|
||||
|
||||
int diff = l1.SourceTree == l2.SourceTree ? 0 : l1.SourceTree.FilePath.CompareTo(l2.SourceTree.FilePath);
|
||||
if (diff != 0) return diff;
|
||||
diff = l1.SourceSpan.Start - l2.SourceSpan.Start;
|
||||
@@ -243,7 +247,7 @@ namespace Semmle.Extraction.CommentProcessing
|
||||
/// Process comments up until nextElement.
|
||||
/// Group comments into blocks, and associate blocks with elements.
|
||||
/// </summary>
|
||||
///
|
||||
///
|
||||
/// <param name="commentEnumerator">Enumerator for all comments in the program.</param>
|
||||
/// <param name="nextElement">The next element in the list.</param>
|
||||
/// <param name="elementStack">A stack of nested program elements.</param>
|
||||
@@ -261,7 +265,7 @@ namespace Semmle.Extraction.CommentProcessing
|
||||
// Iterate comments until the commentEnumerator has gone past nextElement
|
||||
while (nextElement == null || Compare(commentEnumerator.Current.Value.Location, nextElement.Value.Key) < 0)
|
||||
{
|
||||
if(block is null)
|
||||
if (block is null)
|
||||
block = new CommentBlock(commentEnumerator.Current.Value);
|
||||
|
||||
if (!block.CombinesWith(commentEnumerator.Current.Value))
|
||||
@@ -284,7 +288,7 @@ namespace Semmle.Extraction.CommentProcessing
|
||||
}
|
||||
}
|
||||
|
||||
if(!(block is null))
|
||||
if (!(block is null))
|
||||
GenerateBindings(block, elementStack, nextElement, cb);
|
||||
|
||||
return true;
|
||||
|
||||
28
csharp/ql/src/printAst.ql
Normal file
28
csharp/ql/src/printAst.ql
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @name Print AST
|
||||
* @description Outputs a representation of a file's Abstract Syntax Tree. This
|
||||
* query is used by the VS Code extension.
|
||||
* @id csharp/print-ast
|
||||
* @kind graph
|
||||
* @tags ide-contextual-queries/print-ast
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.PrintAst
|
||||
import definitions
|
||||
|
||||
/**
|
||||
* The source file to generate an AST from.
|
||||
*/
|
||||
external string selectedSourceFile();
|
||||
|
||||
class PrintAstConfigurationOverride extends PrintAstConfiguration {
|
||||
/**
|
||||
* Holds if the location matches the selected file in the VS Code extension and
|
||||
* the element is `fromSource`.
|
||||
*/
|
||||
override predicate shouldPrint(Element e, Location l) {
|
||||
super.shouldPrint(e, l) and
|
||||
l.getFile() = getEncodedFile(selectedSourceFile())
|
||||
}
|
||||
}
|
||||
@@ -93,4 +93,6 @@ class Attribute extends TopLevelExprParent, @attribute {
|
||||
result = "[" + name + "(...)]"
|
||||
)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Attribute" }
|
||||
}
|
||||
|
||||
@@ -284,6 +284,8 @@ class Method extends Callable, Virtualizable, Attributable, @method {
|
||||
override Parameter getRawParameter(int i) {
|
||||
if this.isStatic() then result = this.getParameter(i) else result = this.getParameter(i - 1)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Method" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -302,6 +304,8 @@ class ExtensionMethod extends Method {
|
||||
|
||||
/** Gets the type being extended by this method. */
|
||||
Type getExtendedType() { result = getParameter(0).getType() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ExtensionMethod" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,6 +375,8 @@ class StaticConstructor extends Constructor {
|
||||
StaticConstructor() { this.isStatic() }
|
||||
|
||||
override string getUndecoratedName() { result = ".cctor" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "StaticConstructor" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -385,6 +391,8 @@ class StaticConstructor extends Constructor {
|
||||
*/
|
||||
class InstanceConstructor extends Constructor {
|
||||
InstanceConstructor() { not this.isStatic() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "InstanceConstructor" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,6 +421,8 @@ class Destructor extends DotNet::Destructor, Callable, Member, Attributable, @de
|
||||
override Location getALocation() { destructor_location(this, result) }
|
||||
|
||||
override string toString() { result = Callable.super.toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Destructor" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -471,6 +481,8 @@ class PlusOperator extends UnaryOperator {
|
||||
PlusOperator() { this.getName() = "+" }
|
||||
|
||||
override string getFunctionName() { result = "op_UnaryPlus" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PlusOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -486,6 +498,8 @@ class MinusOperator extends UnaryOperator {
|
||||
MinusOperator() { this.getName() = "-" }
|
||||
|
||||
override string getFunctionName() { result = "op_UnaryNegation" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MinusOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -501,6 +515,8 @@ class NotOperator extends UnaryOperator {
|
||||
NotOperator() { this.getName() = "!" }
|
||||
|
||||
override string getFunctionName() { result = "op_LogicalNot" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NotOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -516,6 +532,8 @@ class ComplementOperator extends UnaryOperator {
|
||||
ComplementOperator() { this.getName() = "~" }
|
||||
|
||||
override string getFunctionName() { result = "op_OnesComplement" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ComplementOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -531,6 +549,8 @@ class IncrementOperator extends UnaryOperator {
|
||||
IncrementOperator() { this.getName() = "++" }
|
||||
|
||||
override string getFunctionName() { result = "op_Increment" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "IncrementOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -546,6 +566,8 @@ class DecrementOperator extends UnaryOperator {
|
||||
DecrementOperator() { this.getName() = "--" }
|
||||
|
||||
override string getFunctionName() { result = "op_Decrement" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DecrementOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -561,6 +583,8 @@ class FalseOperator extends UnaryOperator {
|
||||
FalseOperator() { this.getName() = "false" }
|
||||
|
||||
override string getFunctionName() { result = "op_False" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FalseOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -576,6 +600,8 @@ class TrueOperator extends UnaryOperator {
|
||||
TrueOperator() { this.getName() = "true" }
|
||||
|
||||
override string getFunctionName() { result = "op_True" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TrueOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -608,6 +634,8 @@ class AddOperator extends BinaryOperator {
|
||||
AddOperator() { this.getName() = "+" }
|
||||
|
||||
override string getFunctionName() { result = "op_Addition" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AddOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -623,6 +651,8 @@ class SubOperator extends BinaryOperator {
|
||||
SubOperator() { this.getName() = "-" }
|
||||
|
||||
override string getFunctionName() { result = "op_Subtraction" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SubOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -638,6 +668,8 @@ class MulOperator extends BinaryOperator {
|
||||
MulOperator() { this.getName() = "*" }
|
||||
|
||||
override string getFunctionName() { result = "op_Multiply" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MulOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -653,6 +685,8 @@ class DivOperator extends BinaryOperator {
|
||||
DivOperator() { this.getName() = "/" }
|
||||
|
||||
override string getFunctionName() { result = "op_Division" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DivOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -668,6 +702,8 @@ class RemOperator extends BinaryOperator {
|
||||
RemOperator() { this.getName() = "%" }
|
||||
|
||||
override string getFunctionName() { result = "op_Modulus" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "RemOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -683,6 +719,8 @@ class AndOperator extends BinaryOperator {
|
||||
AndOperator() { this.getName() = "&" }
|
||||
|
||||
override string getFunctionName() { result = "op_BitwiseAnd" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AndOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -698,6 +736,8 @@ class OrOperator extends BinaryOperator {
|
||||
OrOperator() { this.getName() = "|" }
|
||||
|
||||
override string getFunctionName() { result = "op_BitwiseOr" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "OrOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -713,6 +753,8 @@ class XorOperator extends BinaryOperator {
|
||||
XorOperator() { this.getName() = "^" }
|
||||
|
||||
override string getFunctionName() { result = "op_ExclusiveOr" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "XorOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -728,6 +770,8 @@ class LShiftOperator extends BinaryOperator {
|
||||
LShiftOperator() { this.getName() = "<<" }
|
||||
|
||||
override string getFunctionName() { result = "op_LeftShift" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LShiftOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -743,6 +787,8 @@ class RShiftOperator extends BinaryOperator {
|
||||
RShiftOperator() { this.getName() = ">>" }
|
||||
|
||||
override string getFunctionName() { result = "op_RightShift" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "RShiftOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -758,6 +804,8 @@ class EQOperator extends BinaryOperator {
|
||||
EQOperator() { this.getName() = "==" }
|
||||
|
||||
override string getFunctionName() { result = "op_Equality" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EQOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -773,6 +821,8 @@ class NEOperator extends BinaryOperator {
|
||||
NEOperator() { this.getName() = "!=" }
|
||||
|
||||
override string getFunctionName() { result = "op_Inequality" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NEOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -788,6 +838,8 @@ class LTOperator extends BinaryOperator {
|
||||
LTOperator() { this.getName() = "<" }
|
||||
|
||||
override string getFunctionName() { result = "op_LessThan" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LTOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -803,6 +855,8 @@ class GTOperator extends BinaryOperator {
|
||||
GTOperator() { this.getName() = ">" }
|
||||
|
||||
override string getFunctionName() { result = "op_GreaterThan" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GTOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -818,6 +872,8 @@ class LEOperator extends BinaryOperator {
|
||||
LEOperator() { this.getName() = "<=" }
|
||||
|
||||
override string getFunctionName() { result = "op_LessThanOrEqual" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LEOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -833,6 +889,8 @@ class GEOperator extends BinaryOperator {
|
||||
GEOperator() { this.getName() = ">=" }
|
||||
|
||||
override string getFunctionName() { result = "op_GreaterThanOrEqual" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GEOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -870,6 +928,8 @@ class ImplicitConversionOperator extends ConversionOperator {
|
||||
ImplicitConversionOperator() { this.getName() = "implicit conversion" }
|
||||
|
||||
override string getFunctionName() { result = "op_Implicit" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ImplicitConversionOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -885,6 +945,8 @@ class ExplicitConversionOperator extends ConversionOperator {
|
||||
ExplicitConversionOperator() { this.getName() = "explicit conversion" }
|
||||
|
||||
override string getFunctionName() { result = "op_Explicit" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ExplicitConversionOperator" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -923,4 +985,6 @@ class LocalFunction extends Callable, Modifiable, @local_function {
|
||||
override Location getALocation() { result = getStatement().getALocation() }
|
||||
|
||||
override Parameter getRawParameter(int i) { result = getParameter(i) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalFunction" }
|
||||
}
|
||||
|
||||
@@ -48,4 +48,17 @@ class Element extends DotNet::Element, @element {
|
||||
* other children (zero-based).
|
||||
*/
|
||||
int getIndex() { exists(Element parent | parent.getChild(result) = this) }
|
||||
|
||||
/**
|
||||
* Gets the name of a primary CodeQL class to which this element belongs.
|
||||
*
|
||||
* For most elements, this is simply the most precise syntactic category to
|
||||
* which they belong; for example, `AddExpr` is a primary class, but
|
||||
* `BinaryOperation` is not.
|
||||
*
|
||||
* This predicate always has a result. If no primary class can be
|
||||
* determined, the result is `"???"`. If multiple primary classes match,
|
||||
* this predicate can have multiple results.
|
||||
*/
|
||||
string getAPrimaryQlClass() { result = "???" }
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ class Event extends DeclarationWithAccessors, @event {
|
||||
}
|
||||
|
||||
override Location getALocation() { event_location(this, result) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Event" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,6 +109,8 @@ class EventAccessor extends Accessor, @event_accessor {
|
||||
*/
|
||||
class AddEventAccessor extends EventAccessor, @add_event_accessor {
|
||||
override string getName() { result = "add" + "_" + getDeclaration().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AddEventAccessor" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,4 +128,6 @@ class AddEventAccessor extends EventAccessor, @add_event_accessor {
|
||||
*/
|
||||
class RemoveEventAccessor extends EventAccessor, @remove_event_accessor {
|
||||
override string getName() { result = "remove" + "_" + getDeclaration().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "RemoveEventAccessor" }
|
||||
}
|
||||
|
||||
@@ -183,6 +183,8 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter {
|
||||
|
||||
/** Gets the generic that defines this type parameter. */
|
||||
UnboundGeneric getGeneric() { type_parameters(this, _, result, _) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TypeParameter" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -259,7 +259,7 @@ private module Gvn {
|
||||
private newtype TGvnType =
|
||||
TLeafGvnType(LeafType t) or
|
||||
TMethodTypeParameterGvnType(int i) { i = any(MethodTypeParameter p).getIndex() } or
|
||||
TConstructedGvnType(ConstructedGvnTypeList l)
|
||||
TConstructedGvnType(ConstructedGvnTypeList l) { l.isFullyConstructed() }
|
||||
|
||||
private newtype TConstructedGvnTypeList =
|
||||
TConstructedGvnTypeNil(Unification::CompoundTypeKind k) or
|
||||
@@ -334,6 +334,10 @@ private module Gvn {
|
||||
)
|
||||
}
|
||||
|
||||
predicate isFullyConstructed() {
|
||||
this.getKind().getNumberOfTypeParameters() - 1 = this.length()
|
||||
}
|
||||
|
||||
private GvnType getArg(int i) {
|
||||
exists(GvnType head, ConstructedGvnTypeList tail |
|
||||
this = TConstructedGvnTypeCons(head, tail)
|
||||
@@ -345,47 +349,71 @@ private module Gvn {
|
||||
)
|
||||
}
|
||||
|
||||
private Unification::GenericType getConstructedGenericDeclaringTypeAt(int i) {
|
||||
i = 0 and
|
||||
result = this.getKind().getConstructedSourceDeclaration()
|
||||
or
|
||||
result = this.getConstructedGenericDeclaringTypeAt(i - 1).getGenericDeclaringType()
|
||||
}
|
||||
|
||||
private predicate isDeclaringTypeAt(int i) {
|
||||
exists(this.getConstructedGenericDeclaringTypeAt(i - 1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this constructed type, restricted
|
||||
* to the prefix `t` of the underlying source declaration type.
|
||||
*
|
||||
* The `toString()` calculation needs to be split up into prefixes, in
|
||||
* order to apply the type arguments correctly. For example, a source
|
||||
* declaration type `A<>.B.C<,>` applied to types `int, string, bool`
|
||||
* needs to be printed as `A<int>.B.C<string,bool>`.
|
||||
* Gets the `j`th `toString()` part of the `i`th nested component of this
|
||||
* constructed type, if any. The nested components are sorted in reverse
|
||||
* order, while the individual parts are sorted in normal order.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
private string toStringConstructed(Unification::GenericType t) {
|
||||
t = this.getKind().getConstructedSourceDeclaration().getGenericDeclaringType*() and
|
||||
exists(int offset, int children, string name, string nameArgs |
|
||||
offset = t.getNumberOfDeclaringArguments() and
|
||||
children = t.getNumberOfArgumentsSelf() and
|
||||
name = Unification::getNameNested(t) and
|
||||
if children = 0
|
||||
then nameArgs = name
|
||||
else
|
||||
exists(string offsetArgs |
|
||||
offsetArgs =
|
||||
concat(int i |
|
||||
i in [offset .. offset + children - 1]
|
||||
|
|
||||
this.getArg(i).toString(), "," order by i
|
||||
) and
|
||||
nameArgs = name.prefix(name.length() - children - 1) + "<" + offsetArgs + ">"
|
||||
private string toStringConstructedPart(int i, int j) {
|
||||
this.isFullyConstructed() and
|
||||
exists(Unification::GenericType t |
|
||||
t = this.getConstructedGenericDeclaringTypeAt(i) and
|
||||
exists(int offset, int children, string name |
|
||||
offset = t.getNumberOfDeclaringArguments() and
|
||||
children = t.getNumberOfArgumentsSelf() and
|
||||
name = Unification::getNameNested(t) and
|
||||
if children = 0
|
||||
then
|
||||
j = 0 and result = name
|
||||
or
|
||||
this.isDeclaringTypeAt(i) and j = 1 and result = "."
|
||||
else (
|
||||
j = 0 and result = name.prefix(name.length() - children - 1) + "<"
|
||||
or
|
||||
j in [1 .. 2 * children - 1] and
|
||||
if j % 2 = 0
|
||||
then result = ","
|
||||
else result = this.getArg((j + 1) / 2 + offset - 1).toString()
|
||||
or
|
||||
j = 2 * children and
|
||||
result = ">"
|
||||
or
|
||||
this.isDeclaringTypeAt(i) and
|
||||
j = 2 * children + 1 and
|
||||
result = "."
|
||||
)
|
||||
|
|
||||
offset = 0 and result = nameArgs
|
||||
or
|
||||
result = this.toStringConstructed(t.getGenericDeclaringType()) + "." + nameArgs
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
string toString() {
|
||||
this.isFullyConstructed() and
|
||||
exists(Unification::CompoundTypeKind k | k = this.getKind() |
|
||||
result = k.toStringBuiltin(this.getArg(0).toString())
|
||||
or
|
||||
result = this.toStringConstructed(k.getConstructedSourceDeclaration())
|
||||
result =
|
||||
strictconcat(int i, int j |
|
||||
exists(Unification::GenericType t, int children |
|
||||
t = this.getConstructedGenericDeclaringTypeAt(i) and
|
||||
children = t.getNumberOfArgumentsSelf() and
|
||||
if children = 0 then j = 0 else j in [0 .. 2 * children]
|
||||
)
|
||||
|
|
||||
this.toStringConstructedPart(i, j) order by i desc, j
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -207,4 +207,6 @@ class NamespaceDeclaration extends Element, @namespace_declaration {
|
||||
override Location getALocation() { namespace_declaration_location(this, result) }
|
||||
|
||||
override string toString() { result = "namespace ... { ... }" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NamespaceDeclaration" }
|
||||
}
|
||||
|
||||
20
csharp/ql/src/semmle/code/csharp/PrintAst.ql
Normal file
20
csharp/ql/src/semmle/code/csharp/PrintAst.ql
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Print AST
|
||||
* @description Outputs a representation of the Abstract Syntax Tree.
|
||||
* @id csharp/print-ast
|
||||
* @kind graph
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import PrintAst
|
||||
|
||||
/**
|
||||
* Temporarily tweak this class or make a copy to control which functions are
|
||||
* printed.
|
||||
*/
|
||||
class PrintAstConfigurationOverride extends PrintAstConfiguration {
|
||||
/**
|
||||
* TWEAK THIS PREDICATE AS NEEDED.
|
||||
*/
|
||||
override predicate shouldPrint(Element e, Location l) { super.shouldPrint(e, l) }
|
||||
}
|
||||
644
csharp/ql/src/semmle/code/csharp/PrintAst.qll
Normal file
644
csharp/ql/src/semmle/code/csharp/PrintAst.qll
Normal file
@@ -0,0 +1,644 @@
|
||||
/**
|
||||
* Provides queries to pretty-print a C# AST as a graph.
|
||||
*
|
||||
* By default, this will print the AST for all elements in the database. To change this behavior,
|
||||
* extend `PrintAstConfiguration` and override `shouldPrint` to hold for only the elements
|
||||
* you wish to view the AST for.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
private newtype TPrintAstConfiguration = MkPrintAstConfiguration()
|
||||
|
||||
/**
|
||||
* The query can extend this class to control which elements are printed.
|
||||
*/
|
||||
class PrintAstConfiguration extends TPrintAstConfiguration {
|
||||
/**
|
||||
* Gets a textual representation of this `PrintAstConfiguration`.
|
||||
*/
|
||||
string toString() { result = "PrintAstConfiguration" }
|
||||
|
||||
/**
|
||||
* Controls whether the `Element` should be considered for AST printing.
|
||||
* By default it checks whether the `Element` `e` belongs to `Location` `l`.
|
||||
*/
|
||||
predicate shouldPrint(Element e, Location l) { e.fromSource() and l = e.getLocation() }
|
||||
}
|
||||
|
||||
private predicate shouldPrint(Element e, Location l) {
|
||||
exists(PrintAstConfiguration config | config.shouldPrint(e, l))
|
||||
}
|
||||
|
||||
private predicate isImplicitExpression(ControlFlowElement element) {
|
||||
element.(Expr).isImplicit() and not exists(element.getAChild())
|
||||
}
|
||||
|
||||
private predicate isFilteredCompilerGenerated(Declaration d) {
|
||||
d.isCompilerGenerated() and
|
||||
not d instanceof Accessor
|
||||
}
|
||||
|
||||
private predicate isNotNeeded(Element e) {
|
||||
isFilteredCompilerGenerated(e)
|
||||
or
|
||||
e instanceof ConstructedGeneric
|
||||
or
|
||||
e instanceof AnonymousClass
|
||||
or
|
||||
isNotNeeded(e.(Declaration).getDeclaringType())
|
||||
or
|
||||
isNotNeeded(e.(Parameter).getDeclaringElement())
|
||||
or
|
||||
isNotNeeded(e.(Attribute).getTarget())
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the canonical QL class(es) for entity `el`
|
||||
*/
|
||||
private string getQlClass(Element el) {
|
||||
result = "[" + concat(el.getAPrimaryQlClass(), ",") + "] "
|
||||
// Alternative implementation -- do not delete. It is useful for QL class discovery.
|
||||
// result = "["+ concat(el.getAQlClass(), ",") + "] "
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Element`, such as a `namespace` and a `partial class`, might have multiple locations.
|
||||
* The locations are ordered by line, column, and then the first one is selected.
|
||||
*/
|
||||
private Location getRepresentativeLocation(Element ast) {
|
||||
result =
|
||||
min(Location loc |
|
||||
shouldPrint(ast, loc)
|
||||
|
|
||||
loc order by loc.getStartLine(), loc.getStartColumn(), loc.getEndLine(), loc.getEndColumn()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate locationSortKeys(Element ast, string file, int line, int column) {
|
||||
exists(Location loc |
|
||||
loc = getRepresentativeLocation(ast) and
|
||||
file = loc.getFile().toString() and
|
||||
line = loc.getStartLine() and
|
||||
column = loc.getStartColumn()
|
||||
)
|
||||
or
|
||||
not exists(getRepresentativeLocation(ast)) and
|
||||
file = "" and
|
||||
line = 0 and
|
||||
column = 0
|
||||
}
|
||||
|
||||
private predicate hasInterestingBaseTypes(ValueOrRefType type) {
|
||||
exists(getAnInterestingBaseType(type))
|
||||
}
|
||||
|
||||
private ValueOrRefType getAnInterestingBaseType(ValueOrRefType type) {
|
||||
not type instanceof TupleType and
|
||||
not type instanceof ArrayType and
|
||||
not type instanceof NullableType and
|
||||
result = type.getABaseType() and
|
||||
isInterestingBaseType(result)
|
||||
}
|
||||
|
||||
private predicate isInterestingBaseType(ValueOrRefType base) {
|
||||
not base instanceof ObjectType and
|
||||
not base.getQualifiedName() = "System.ValueType" and
|
||||
not base.getQualifiedName() = "System.Delegate" and
|
||||
not base.getQualifiedName() = "System.MulticastDelegate" and
|
||||
not base.getQualifiedName() = "System.Enum"
|
||||
}
|
||||
|
||||
/**
|
||||
* Printed AST nodes are mostly `Element`s of the underlying AST.
|
||||
* There are extra AST nodes generated for parameters of `Parameterizable`s,
|
||||
* attributes of `Attributable`s, type parameters of `UnboundGeneric` and
|
||||
* base types of `ValueOrRefType` declarations. These extra nodes are used
|
||||
* as containers to organize the tree a bit better.
|
||||
*/
|
||||
private newtype TPrintAstNode =
|
||||
TElementNode(Element element) { shouldPrint(element, _) } or
|
||||
TParametersNode(Parameterizable parameterizable) {
|
||||
shouldPrint(parameterizable, _) and
|
||||
parameterizable.getNumberOfParameters() > 0 and
|
||||
not isNotNeeded(parameterizable)
|
||||
} or
|
||||
TAttributesNode(Attributable attributable) {
|
||||
shouldPrint(attributable, _) and
|
||||
exists(attributable.getAnAttribute()) and
|
||||
not isNotNeeded(attributable)
|
||||
} or
|
||||
TTypeParametersNode(UnboundGeneric unboundGeneric) {
|
||||
shouldPrint(unboundGeneric, _) and
|
||||
unboundGeneric.getNumberOfTypeParameters() > 0 and
|
||||
not isNotNeeded(unboundGeneric)
|
||||
} or
|
||||
TBaseTypesNode(ValueOrRefType type) {
|
||||
shouldPrint(type, _) and
|
||||
hasInterestingBaseTypes(type) and
|
||||
not isNotNeeded(type)
|
||||
} or
|
||||
TBaseTypeNode(ValueOrRefType derived, ValueOrRefType base) {
|
||||
shouldPrint(derived, _) and
|
||||
base = getAnInterestingBaseType(derived) and
|
||||
not isNotNeeded(derived)
|
||||
}
|
||||
|
||||
/**
|
||||
* A node in the output tree.
|
||||
*/
|
||||
class PrintAstNode extends TPrintAstNode {
|
||||
/**
|
||||
* Gets a textual representation of this node in the PrintAst output tree.
|
||||
*/
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets the child node at index `childIndex`. Child indices must be unique,
|
||||
* but need not be contiguous.
|
||||
*/
|
||||
PrintAstNode getChild(int childIndex) { none() }
|
||||
|
||||
/**
|
||||
* Gets a child of this node.
|
||||
*/
|
||||
final PrintAstNode getAChild() { result = getChild(_) }
|
||||
|
||||
/**
|
||||
* Gets the parent of this node, if any.
|
||||
*/
|
||||
final PrintAstNode getParent() { result.getAChild() = this }
|
||||
|
||||
/**
|
||||
* Gets the location of this node in the source code.
|
||||
*/
|
||||
Location getLocation() { none() }
|
||||
|
||||
/**
|
||||
* Gets the value of the property of this node, where the name of the property
|
||||
* is `key`.
|
||||
*/
|
||||
string getProperty(string key) {
|
||||
key = "semmle.label" and
|
||||
result = toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the label for the edge from this node to the specified child. By
|
||||
* default, this is just the index of the child, but subclasses can override
|
||||
* this.
|
||||
*/
|
||||
string getChildEdgeLabel(int childIndex) {
|
||||
exists(getChild(childIndex)) and
|
||||
result = childIndex.toString()
|
||||
}
|
||||
}
|
||||
|
||||
/** A top-level AST node. */
|
||||
class TopLevelPrintAstNode extends PrintAstNode {
|
||||
TopLevelPrintAstNode() { not exists(this.getParent()) }
|
||||
|
||||
private int getOrder() {
|
||||
this =
|
||||
rank[result](TopLevelPrintAstNode n, Location l |
|
||||
l = n.getLocation()
|
||||
|
|
||||
n
|
||||
order by
|
||||
l.getFile().getRelativePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(),
|
||||
l.getEndColumn()
|
||||
)
|
||||
}
|
||||
|
||||
override string getProperty(string key) {
|
||||
result = super.getProperty(key)
|
||||
or
|
||||
key = "semmle.order" and
|
||||
result = this.getOrder().toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing an AST node with an underlying `Element`.
|
||||
*/
|
||||
abstract class ElementNode extends PrintAstNode, TElementNode {
|
||||
Element element;
|
||||
|
||||
ElementNode() { this = TElementNode(element) }
|
||||
|
||||
override string toString() { result = getQlClass(element) + element.toString() }
|
||||
|
||||
override Location getLocation() { result = getRepresentativeLocation(element) }
|
||||
|
||||
/**
|
||||
* Gets the `Element` represented by this node.
|
||||
*/
|
||||
final Element getElement() { result = element }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `ControlFlowElement` (`Expr` or `Stmt`).
|
||||
*/
|
||||
class ControlFlowElementNode extends ElementNode {
|
||||
ControlFlowElement controlFlowElement;
|
||||
|
||||
ControlFlowElementNode() {
|
||||
controlFlowElement = element and
|
||||
// Removing implicit expressions
|
||||
not isImplicitExpression(element) and
|
||||
// Removing extra nodes that are generated for an `AssignOperation`
|
||||
not exists(AssignOperation ao |
|
||||
ao.hasExpandedAssignment() and
|
||||
(
|
||||
ao.getExpandedAssignment() = controlFlowElement or
|
||||
ao.getExpandedAssignment().getRValue() = controlFlowElement or
|
||||
ao.getExpandedAssignment().getRValue().(BinaryOperation).getLeftOperand() =
|
||||
controlFlowElement.getParent*() or
|
||||
ao.getExpandedAssignment().getRValue().(OperatorCall).getChild(0) =
|
||||
controlFlowElement.getParent*()
|
||||
)
|
||||
) and
|
||||
not isNotNeeded(element.getParent+())
|
||||
}
|
||||
|
||||
override ElementNode getChild(int childIndex) {
|
||||
result.getElement() = controlFlowElement.getChild(childIndex)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `LocalFunctionStmt`.
|
||||
* Each `LocalFunction` is displayed below its corresponding `LocalFunctionStmt`.
|
||||
*/
|
||||
final class LocalFunctionStmtNode extends ControlFlowElementNode {
|
||||
LocalFunctionStmt stmt;
|
||||
|
||||
LocalFunctionStmtNode() { stmt = element }
|
||||
|
||||
override CallableNode getChild(int childIndex) {
|
||||
childIndex = 0 and
|
||||
result.getElement() = stmt.getLocalFunction()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `Callable`, such as method declaration.
|
||||
*/
|
||||
final class CallableNode extends ElementNode {
|
||||
Callable callable;
|
||||
|
||||
CallableNode() {
|
||||
callable = element and
|
||||
not isNotNeeded(callable)
|
||||
}
|
||||
|
||||
override PrintAstNode getChild(int childIndex) {
|
||||
childIndex = 0 and
|
||||
result.(AttributesNode).getAttributable() = callable
|
||||
or
|
||||
childIndex = 1 and
|
||||
result.(TypeParametersNode).getUnboundGeneric() = callable
|
||||
or
|
||||
childIndex = 2 and
|
||||
result.(ParametersNode).getParameterizable() = callable
|
||||
or
|
||||
childIndex = 3 and
|
||||
result.(ElementNode).getElement() = callable.(Constructor).getInitializer()
|
||||
or
|
||||
childIndex = 4 and
|
||||
result.(ElementNode).getElement() = callable.getBody()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `DeclarationWithAccessors`, such as property declaration.
|
||||
*/
|
||||
final class DeclarationWithAccessorsNode extends ElementNode {
|
||||
DeclarationWithAccessors declaration;
|
||||
|
||||
DeclarationWithAccessorsNode() {
|
||||
declaration = element and
|
||||
not isNotNeeded(declaration.getDeclaringType())
|
||||
}
|
||||
|
||||
override PrintAstNode getChild(int childIndex) {
|
||||
childIndex = 0 and
|
||||
result.(AttributesNode).getAttributable() = declaration
|
||||
or
|
||||
childIndex = 1 and
|
||||
result.(ParametersNode).getParameterizable() = declaration
|
||||
or
|
||||
childIndex = 2 and
|
||||
result.(ElementNode).getElement() = declaration.(Property).getInitializer().getParent()
|
||||
or
|
||||
result.(ElementNode).getElement() =
|
||||
rank[childIndex - 2](Element a, string file, int line, int column |
|
||||
a = declaration.getAnAccessor() and
|
||||
locationSortKeys(a, file, line, column)
|
||||
|
|
||||
a order by file, line, column
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `Field` declaration.
|
||||
*/
|
||||
final class FieldNode extends ElementNode {
|
||||
Field field;
|
||||
|
||||
FieldNode() {
|
||||
field = element and
|
||||
not field.getDeclaringType() instanceof TupleType and
|
||||
not isNotNeeded(field.getDeclaringType())
|
||||
}
|
||||
|
||||
override PrintAstNode getChild(int childIndex) {
|
||||
childIndex = 0 and
|
||||
result.(AttributesNode).getAttributable() = field
|
||||
or
|
||||
childIndex = 1 and
|
||||
field.hasInitializer() and
|
||||
(
|
||||
if field.getDeclaringType() instanceof Enum
|
||||
then result.(ElementNode).getElement() = field.getInitializer()
|
||||
else result.(ElementNode).getElement() = field.getInitializer().getParent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `Parameter` declaration.
|
||||
*/
|
||||
final class ParameterNode extends ElementNode {
|
||||
Parameter param;
|
||||
|
||||
ParameterNode() {
|
||||
param = element and
|
||||
not isNotNeeded(param.getDeclaringElement())
|
||||
}
|
||||
|
||||
override Location getLocation() {
|
||||
not param.hasExtensionMethodModifier() and result = super.getLocation()
|
||||
or
|
||||
// for extension method first parameters, we're choosing the shorter location of the two
|
||||
param.hasExtensionMethodModifier() and
|
||||
result =
|
||||
min(Location loc |
|
||||
shouldPrint(param, loc) and
|
||||
loc.getStartLine() = loc.getEndLine()
|
||||
|
|
||||
loc order by loc.getEndColumn() - loc.getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
override PrintAstNode getChild(int childIndex) {
|
||||
childIndex = 0 and
|
||||
result.(AttributesNode).getAttributable() = param
|
||||
or
|
||||
childIndex = 1 and
|
||||
result.(ElementNode).getElement() = param.getDefaultValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing an `Attribute`.
|
||||
*/
|
||||
final class AttributeNode extends ElementNode {
|
||||
Attribute attr;
|
||||
|
||||
AttributeNode() {
|
||||
attr = element and
|
||||
not isNotNeeded(attr.getTarget())
|
||||
}
|
||||
|
||||
override ElementNode getChild(int childIndex) { result.getElement() = attr.getChild(childIndex) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `TypeParameter`.
|
||||
*/
|
||||
final class TypeParameterNode extends ElementNode {
|
||||
TypeParameter typeParameter;
|
||||
|
||||
TypeParameterNode() {
|
||||
typeParameter = element and
|
||||
not isNotNeeded(typeParameter.getDeclaringGeneric())
|
||||
}
|
||||
|
||||
override ElementNode getChild(int childIndex) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `ValueOrRefType`.
|
||||
*/
|
||||
final class TypeNode extends ElementNode {
|
||||
ValueOrRefType type;
|
||||
|
||||
TypeNode() {
|
||||
type = element and
|
||||
not type instanceof TupleType and
|
||||
not type instanceof ArrayType and
|
||||
not type instanceof NullableType and
|
||||
not isNotNeeded(type)
|
||||
}
|
||||
|
||||
override PrintAstNode getChild(int childIndex) {
|
||||
childIndex = 0 and
|
||||
result.(AttributesNode).getAttributable() = type
|
||||
or
|
||||
childIndex = 1 and
|
||||
result.(TypeParametersNode).getUnboundGeneric() = type
|
||||
or
|
||||
childIndex = 2 and
|
||||
result.(ParametersNode).getParameterizable() = type
|
||||
or
|
||||
childIndex = 3 and
|
||||
result.(BaseTypesNode).getValueOrRefType() = type
|
||||
or
|
||||
result.(ElementNode).getElement() =
|
||||
rank[childIndex - 3](Member m, string file, int line, int column |
|
||||
m = type.getAMember() and
|
||||
locationSortKeys(m, file, line, column)
|
||||
|
|
||||
m order by file, line, column
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a `NamespaceDeclaration`.
|
||||
*/
|
||||
final class NamespaceNode extends ElementNode {
|
||||
NamespaceDeclaration namespace;
|
||||
|
||||
NamespaceNode() { namespace = element }
|
||||
|
||||
override PrintAstNode getChild(int childIndex) {
|
||||
result.(ElementNode).getElement() =
|
||||
rank[childIndex](Element a, string file, int line, int column |
|
||||
(a = namespace.getAChildNamespaceDeclaration() or a = namespace.getATypeDeclaration()) and
|
||||
locationSortKeys(a, file, line, column)
|
||||
|
|
||||
a order by file, line, column
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the parameters of a `Parameterizable`.
|
||||
* Only rendered if there's at least one parameter and if the
|
||||
* `Parameterizable` is not compiler generated or is of type
|
||||
* `Accessor`.
|
||||
*/
|
||||
final class ParametersNode extends PrintAstNode, TParametersNode {
|
||||
Parameterizable parameterizable;
|
||||
|
||||
ParametersNode() { this = TParametersNode(parameterizable) }
|
||||
|
||||
override string toString() { result = "(Parameters)" }
|
||||
|
||||
override Location getLocation() { none() }
|
||||
|
||||
override ParameterNode getChild(int childIndex) {
|
||||
result.getElement() = parameterizable.getParameter(childIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying `Parameterizable`
|
||||
*/
|
||||
Parameterizable getParameterizable() { result = parameterizable }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the attributes of an `Attributable`.
|
||||
* Only rendered if there's at least one attribute.
|
||||
*/
|
||||
final class AttributesNode extends PrintAstNode, TAttributesNode {
|
||||
Attributable attributable;
|
||||
|
||||
AttributesNode() { this = TAttributesNode(attributable) }
|
||||
|
||||
override string toString() { result = "(Attributes)" }
|
||||
|
||||
override Location getLocation() { none() }
|
||||
|
||||
override AttributeNode getChild(int childIndex) {
|
||||
result.getElement() =
|
||||
rank[childIndex](Attribute a, string file, int line, int column |
|
||||
a = attributable.getAnAttribute() and
|
||||
locationSortKeys(a, file, line, column)
|
||||
|
|
||||
a order by file, line, column
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying `Attributable`
|
||||
*/
|
||||
Attributable getAttributable() { result = attributable }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the type parameters of an `UnboundGeneric`.
|
||||
*/
|
||||
final class TypeParametersNode extends PrintAstNode, TTypeParametersNode {
|
||||
UnboundGeneric unboundGeneric;
|
||||
|
||||
TypeParametersNode() { this = TTypeParametersNode(unboundGeneric) }
|
||||
|
||||
override string toString() { result = "(Type parameters)" }
|
||||
|
||||
override Location getLocation() { none() }
|
||||
|
||||
override TypeParameterNode getChild(int childIndex) {
|
||||
result.getElement() = unboundGeneric.getTypeParameter(childIndex)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying `UnboundGeneric`
|
||||
*/
|
||||
UnboundGeneric getUnboundGeneric() { result = unboundGeneric }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing the base types of a `ValueOrRefType`.
|
||||
*/
|
||||
final class BaseTypesNode extends PrintAstNode, TBaseTypesNode {
|
||||
ValueOrRefType valueOrRefType;
|
||||
|
||||
BaseTypesNode() { this = TBaseTypesNode(valueOrRefType) }
|
||||
|
||||
override string toString() { result = "(Base types)" }
|
||||
|
||||
override Location getLocation() { none() }
|
||||
|
||||
override BaseTypeNode getChild(int childIndex) {
|
||||
childIndex = 0 and
|
||||
result.getBaseType() = valueOrRefType.getBaseClass() and
|
||||
result.getDerivedType() = valueOrRefType
|
||||
or
|
||||
result.getBaseType() =
|
||||
rank[childIndex](ValueOrRefType base, string name |
|
||||
base = valueOrRefType.getABaseInterface() and
|
||||
name = base.toString()
|
||||
|
|
||||
base order by name
|
||||
) and
|
||||
result.getDerivedType() = valueOrRefType
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the underlying `ValueOrRefType`
|
||||
*/
|
||||
ValueOrRefType getValueOrRefType() { result = valueOrRefType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node representing a base type reference of a `ValueOrRefType` declaration.
|
||||
*/
|
||||
final class BaseTypeNode extends PrintAstNode, TBaseTypeNode {
|
||||
ValueOrRefType derived;
|
||||
ValueOrRefType base;
|
||||
|
||||
BaseTypeNode() { this = TBaseTypeNode(derived, base) }
|
||||
|
||||
override string toString() { result = getQlClass(base) + base.toString() }
|
||||
|
||||
override Location getLocation() { result = derived.getLocation() }
|
||||
|
||||
override BaseTypeNode getChild(int childIndex) { none() }
|
||||
|
||||
/**
|
||||
* Gets the underlying derived `ValueOrRefType`
|
||||
*/
|
||||
ValueOrRefType getDerivedType() { result = derived }
|
||||
|
||||
/**
|
||||
* Gets the underlying base `ValueOrRefType`
|
||||
*/
|
||||
ValueOrRefType getBaseType() { result = base }
|
||||
}
|
||||
|
||||
/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */
|
||||
query predicate nodes(PrintAstNode node, string key, string value) { value = node.getProperty(key) }
|
||||
|
||||
/**
|
||||
* Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the
|
||||
* given `value`.
|
||||
*/
|
||||
query predicate edges(PrintAstNode source, PrintAstNode target, string key, string value) {
|
||||
exists(int childIndex |
|
||||
target = source.getChild(childIndex) and
|
||||
(
|
||||
key = "semmle.label" and value = source.getChildEdgeLabel(childIndex)
|
||||
or
|
||||
key = "semmle.order" and value = childIndex.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if property `key` of the graph has the given `value`. */
|
||||
query predicate graphProperties(string key, string value) {
|
||||
key = "semmle.graphKind" and value = "tree"
|
||||
}
|
||||
@@ -232,6 +232,8 @@ class Property extends DotNet::Property, DeclarationWithGetSetAccessors, @proper
|
||||
override Getter getGetter() { result = DeclarationWithGetSetAccessors.super.getGetter() }
|
||||
|
||||
override Setter getSetter() { result = DeclarationWithGetSetAccessors.super.getSetter() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Property" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -298,6 +300,8 @@ class Indexer extends DeclarationWithGetSetAccessors, Parameterizable, @indexer
|
||||
override Location getALocation() { indexer_location(this, result) }
|
||||
|
||||
override string toStringWithTypes() { result = getName() + "[" + parameterTypesToString() + "]" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Indexer" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -415,6 +419,8 @@ class Getter extends Accessor, @getter {
|
||||
override DeclarationWithGetSetAccessors getDeclaration() {
|
||||
result = Accessor.super.getDeclaration()
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Getter" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -464,6 +470,8 @@ class Setter extends Accessor, @setter {
|
||||
override DeclarationWithGetSetAccessors getDeclaration() {
|
||||
result = Accessor.super.getDeclaration()
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Setter" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -554,4 +562,6 @@ class IndexerProperty extends Property {
|
||||
// ```
|
||||
result.getIndexer() = i
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "IndexerProperty" }
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ class BlockStmt extends Stmt, @block_stmt {
|
||||
}
|
||||
|
||||
override string toString() { result = "{...}" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BlockStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,6 +98,8 @@ class ExprStmt extends Stmt, @expr_stmt {
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "...;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ExprStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,6 +135,8 @@ class IfStmt extends SelectionStmt, @if_stmt {
|
||||
Stmt getElse() { result = this.getChild(2) }
|
||||
|
||||
override string toString() { result = "if (...) ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "IfStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,6 +186,8 @@ class SwitchStmt extends SelectionStmt, Switch, @switch_stmt {
|
||||
|
||||
override string toString() { result = "switch (...) {...}" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SwitchStmt" }
|
||||
|
||||
/**
|
||||
* Gets the `i`th statement in the body of this `switch` statement.
|
||||
*
|
||||
@@ -284,6 +292,8 @@ class CaseStmt extends Case, @case_stmt {
|
||||
SwitchStmt getSwitchStmt() { result.getACase() = this }
|
||||
|
||||
override string toString() { result = "case ...:" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CaseStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -305,6 +315,8 @@ class ConstCase extends CaseStmt, LabeledStmt {
|
||||
override string getLabel() { result = p.getValue() }
|
||||
|
||||
override string toString() { result = CaseStmt.super.toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ConstCase" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,6 +336,8 @@ class DefaultCase extends CaseStmt, LabeledStmt {
|
||||
override string getLabel() { result = "default" }
|
||||
|
||||
override string toString() { result = "default:" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DefaultCase" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,6 +368,8 @@ class WhileStmt extends LoopStmt, @while_stmt {
|
||||
override Expr getCondition() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "while (...) ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "WhileStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,6 +386,8 @@ class DoStmt extends LoopStmt, @do_stmt {
|
||||
override Expr getCondition() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "do ... while (...);" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DoStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -440,6 +458,8 @@ class ForStmt extends LoopStmt, @for_stmt {
|
||||
Expr getUpdate(int n) { exists(int i | result = this.getChild(i) and n = i - 1 and i >= 1) }
|
||||
|
||||
override string toString() { result = "for (...;...;...) ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ForStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -562,6 +582,8 @@ class ForeachStmt extends LoopStmt, @foreach_stmt {
|
||||
Expr getIterableExpr() { result = this.getChild(1) }
|
||||
|
||||
override string toString() { result = "foreach (... ... in ...) ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ForeachStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -586,6 +608,8 @@ class JumpStmt extends Stmt, @jump_stmt { }
|
||||
*/
|
||||
class BreakStmt extends JumpStmt, @break_stmt {
|
||||
override string toString() { result = "break;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BreakStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,6 +626,8 @@ class BreakStmt extends JumpStmt, @break_stmt {
|
||||
*/
|
||||
class ContinueStmt extends JumpStmt, @continue_stmt {
|
||||
override string toString() { result = "continue;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ContinueStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -637,6 +663,8 @@ class GotoLabelStmt extends GotoStmt, @goto_stmt {
|
||||
result.getEnclosingCallable() = getEnclosingCallable() and
|
||||
result.getLabel() = getLabel()
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GotoLabelStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -662,6 +690,8 @@ class GotoCaseStmt extends GotoStmt, @goto_case_stmt {
|
||||
override string getLabel() { result = getExpr().getValue() }
|
||||
|
||||
override string toString() { result = "goto case ...;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GotoCaseStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -684,6 +714,8 @@ class GotoDefaultStmt extends GotoStmt, @goto_default_stmt {
|
||||
override string toString() { result = "goto default;" }
|
||||
|
||||
override string getLabel() { result = "default" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GotoDefaultStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -714,6 +746,8 @@ class ThrowStmt extends JumpStmt, ThrowElement, @throw_stmt {
|
||||
result = mid.getParent()
|
||||
)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ThrowStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -738,6 +772,8 @@ class ReturnStmt extends JumpStmt, @return_stmt {
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "return ...;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ReturnStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -769,6 +805,8 @@ class YieldBreakStmt extends YieldStmt {
|
||||
YieldBreakStmt() { not exists(this.getExpr()) }
|
||||
|
||||
override string toString() { result = "yield break;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "YieldBreakStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -789,6 +827,8 @@ class YieldReturnStmt extends YieldStmt {
|
||||
YieldReturnStmt() { exists(this.getExpr()) }
|
||||
|
||||
override string toString() { result = "yield return ...;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "YieldReturnStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -824,6 +864,8 @@ class TryStmt extends Stmt, @try_stmt {
|
||||
|
||||
override string toString() { result = "try {...} ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TryStmt" }
|
||||
|
||||
/** Gets the `catch` clause that handles an exception of type `ex`, if any. */
|
||||
CatchClause getAnExceptionHandler(ExceptionClass ex) { result = clauseHandlesException(ex, 0) }
|
||||
|
||||
@@ -965,6 +1007,8 @@ class SpecificCatchClause extends CatchClause {
|
||||
LocalVariableDeclExpr getVariableDeclExpr() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "catch (...) {...}" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SpecificCatchClause" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -985,6 +1029,8 @@ class GeneralCatchClause extends CatchClause {
|
||||
GeneralCatchClause() { catch_type(this, _, 2) }
|
||||
|
||||
override string toString() { result = "catch {...}" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GeneralCatchClause" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1002,6 +1048,8 @@ class CheckedStmt extends Stmt, @checked_stmt {
|
||||
BlockStmt getBlock() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "checked {...}" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CheckedStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1019,6 +1067,8 @@ class UncheckedStmt extends Stmt, @unchecked_stmt {
|
||||
BlockStmt getBlock() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "unchecked {...}" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "UncheckedStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1056,6 +1106,8 @@ class LockStmt extends Stmt, @lock_stmt {
|
||||
|
||||
/** Gets the type `T` if this statement is of the form `lock(typeof(T)) { ... }`. */
|
||||
Type getLockTypeObject() { result = getExpr().(TypeofExpr).getTypeAccess().getTarget() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LockStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1134,6 +1186,8 @@ class UsingBlockStmt extends UsingStmt, @using_block_stmt {
|
||||
Stmt getBody() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "using (...) {...}" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "UsingBlockStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1171,6 +1225,8 @@ class LocalVariableDeclStmt extends Stmt, @decl_stmt {
|
||||
LocalVariableDeclExpr getVariableDeclExpr(int n) { result = this.getChild(n) }
|
||||
|
||||
override string toString() { result = "... ...;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalVariableDeclStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1207,6 +1263,8 @@ class LocalConstantDeclStmt extends LocalVariableDeclStmt, @const_decl_stmt {
|
||||
override LocalConstantDeclExpr getVariableDeclExpr(int n) { result = this.getChild(n) }
|
||||
|
||||
override string toString() { result = "const ... ...;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalConstantDeclStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1228,6 +1286,8 @@ class UsingDeclStmt extends LocalVariableDeclStmt, UsingStmt, @using_decl_stmt {
|
||||
}
|
||||
|
||||
override Expr getAnExpr() { result = this.getAVariableDeclExpr().getInitializer() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "UsingDeclStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1241,6 +1301,8 @@ class UsingDeclStmt extends LocalVariableDeclStmt, UsingStmt, @using_decl_stmt {
|
||||
*/
|
||||
class EmptyStmt extends Stmt, @empty_stmt {
|
||||
override string toString() { result = ";" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EmptyStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1260,6 +1322,8 @@ class UnsafeStmt extends Stmt, @unsafe_stmt {
|
||||
BlockStmt getBlock() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "unsafe {...}" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "UnsafeStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1291,6 +1355,8 @@ class FixedStmt extends Stmt, @fixed_stmt {
|
||||
Stmt getBody() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "fixed(...) { ... }" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FixedStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1306,7 +1372,9 @@ class FixedStmt extends Stmt, @fixed_stmt {
|
||||
* exit: ...
|
||||
* ```
|
||||
*/
|
||||
class LabelStmt extends LabeledStmt, @label_stmt { }
|
||||
class LabelStmt extends LabeledStmt, @label_stmt {
|
||||
override string getAPrimaryQlClass() { result = "LabelStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A labeled statement.
|
||||
@@ -1356,4 +1424,6 @@ class LocalFunctionStmt extends Stmt, @local_function_stmt {
|
||||
LocalFunction getLocalFunction() { local_function_stmts(this, result) }
|
||||
|
||||
override string toString() { result = getLocalFunction().getName() + "(...)" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalFunctionStmt" }
|
||||
}
|
||||
|
||||
@@ -648,6 +648,8 @@ class Enum extends ValueType, @enum_type {
|
||||
EnumConstant getEnumConstant(string value) {
|
||||
result = this.getAnEnumConstant() and result.getValue() = value
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Enum" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -665,6 +667,8 @@ class Struct extends ValueType, @struct_type {
|
||||
|
||||
/** Holds if this `struct` has a `readonly` modifier. */
|
||||
predicate isReadonly() { hasModifier("readonly") }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Struct" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -713,7 +717,9 @@ private predicate isNonOverridden(Member m) { not m.(Virtualizable).isOverridden
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class Class extends RefType, @class_type { }
|
||||
class Class extends RefType, @class_type {
|
||||
override string getAPrimaryQlClass() { result = "Class" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A class generated by the compiler from an anonymous object creation.
|
||||
@@ -755,7 +761,9 @@ class StringType extends Class {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class Interface extends RefType, @interface_type { }
|
||||
class Interface extends RefType, @interface_type {
|
||||
override string getAPrimaryQlClass() { result = "Interface" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `delegate` type, for example
|
||||
@@ -770,6 +778,8 @@ class DelegateType extends RefType, Parameterizable, @delegate_type {
|
||||
|
||||
/** Gets the annotated return type of this delegate. */
|
||||
AnnotatedType getAnnotatedReturnType() { result.appliesTo(this) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DelegateType" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -794,6 +804,8 @@ class NullableType extends ValueType, DotNet::ConstructedGeneric, @nullable_type
|
||||
override Location getALocation() { result = getUnderlyingType().getALocation() }
|
||||
|
||||
override Type getTypeArgument(int p) { p = 0 and result = getUnderlyingType() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NullableType" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -875,6 +887,8 @@ class PointerType extends DotNet::PointerType, Type, @pointer_type {
|
||||
override Location getALocation() { result = getReferentType().getALocation() }
|
||||
|
||||
override string toString() { result = DotNet::PointerType.super.toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PointerType" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -882,6 +896,8 @@ class PointerType extends DotNet::PointerType, Type, @pointer_type {
|
||||
*/
|
||||
class DynamicType extends RefType, @dynamic_type {
|
||||
override string toStringWithTypes() { result = "dynamic" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DynamicType" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -889,6 +905,8 @@ class DynamicType extends RefType, @dynamic_type {
|
||||
*/
|
||||
class ArglistType extends Type, @arglist_type {
|
||||
override string toStringWithTypes() { result = "__arglist" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ArglistType" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -213,6 +213,10 @@ module Gvn {
|
||||
)
|
||||
}
|
||||
|
||||
predicate isFullyConstructed() {
|
||||
this.getKind().getNumberOfTypeParameters() - 1 = this.length()
|
||||
}
|
||||
|
||||
GvnType getArg(int i) {
|
||||
exists(GvnType head, ConstructedGvnTypeList tail |
|
||||
this = TConstructedGvnTypeCons(head, tail)
|
||||
@@ -224,47 +228,72 @@ module Gvn {
|
||||
)
|
||||
}
|
||||
|
||||
private GenericType getConstructedGenericDeclaringTypeAt(int i) {
|
||||
i = 0 and
|
||||
result = this.getKind().getConstructedSourceDeclaration()
|
||||
or
|
||||
result = this.getConstructedGenericDeclaringTypeAt(i - 1).getGenericDeclaringType()
|
||||
}
|
||||
|
||||
private predicate isDeclaringTypeAt(int i) {
|
||||
exists(this.getConstructedGenericDeclaringTypeAt(i - 1))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this constructed type, restricted
|
||||
* to the prefix `t` of the underlying source declaration type.
|
||||
*
|
||||
* The `toString()` calculation needs to be split up into prefixes, in
|
||||
* order to apply the type arguments correctly. For example, a source
|
||||
* declaration type `A<>.B.C<,>` applied to types `int, string, bool`
|
||||
* needs to be printed as `A<int>.B.C<string,bool>`.
|
||||
* Gets the `j`th `toString()` part of the `i`th nested component of this
|
||||
* constructed type, if any. The nested components are sorted in reverse
|
||||
* order, while the individual parts are sorted in normal order.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
private string toStringConstructed(GenericType t) {
|
||||
t = this.getKind().getConstructedSourceDeclaration().getGenericDeclaringType*() and
|
||||
exists(int offset, int children, string name, string nameArgs |
|
||||
offset = t.getNumberOfDeclaringArguments() and
|
||||
children = t.getNumberOfArgumentsSelf() and
|
||||
name = getNameNested(t) and
|
||||
if children = 0
|
||||
then nameArgs = name
|
||||
else
|
||||
exists(string offsetArgs |
|
||||
offsetArgs =
|
||||
concat(int i |
|
||||
i in [offset .. offset + children - 1]
|
||||
|
|
||||
this.getArg(i).toString(), "," order by i
|
||||
) and
|
||||
nameArgs = name.prefix(name.length() - children - 1) + "<" + offsetArgs + ">"
|
||||
private string toStringConstructedPart(int i, int j) {
|
||||
this.isFullyConstructed() and
|
||||
exists(GenericType t |
|
||||
t = this.getConstructedGenericDeclaringTypeAt(i) and
|
||||
exists(int offset, int children, string name |
|
||||
offset = t.getNumberOfDeclaringArguments() and
|
||||
children = t.getNumberOfArgumentsSelf() and
|
||||
name = getNameNested(t) and
|
||||
if children = 0
|
||||
then
|
||||
j = 0 and result = name
|
||||
or
|
||||
this.isDeclaringTypeAt(i) and j = 1 and result = "."
|
||||
else (
|
||||
j = 0 and result = name.prefix(name.length() - children - 1) + "<"
|
||||
or
|
||||
j in [1 .. 2 * children - 1] and
|
||||
if j % 2 = 0
|
||||
then result = ","
|
||||
else result = this.getArg((j + 1) / 2 + offset - 1).toString()
|
||||
or
|
||||
j = 2 * children and
|
||||
result = ">"
|
||||
or
|
||||
this.isDeclaringTypeAt(i) and
|
||||
j = 2 * children + 1 and
|
||||
result = "."
|
||||
)
|
||||
|
|
||||
offset = 0 and result = nameArgs
|
||||
or
|
||||
result = this.toStringConstructed(t.getGenericDeclaringType()) + "." + nameArgs
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
language[monotonicAggregates]
|
||||
string toString() {
|
||||
this.isFullyConstructed() and
|
||||
exists(CompoundTypeKind k | k = this.getKind() |
|
||||
result = k.toStringBuiltin(this.getArg(0).toString())
|
||||
or
|
||||
result = this.toStringConstructed(k.getConstructedSourceDeclaration())
|
||||
result =
|
||||
strictconcat(int i, int j, int offset |
|
||||
exists(GenericType t, int children |
|
||||
t = this.getConstructedGenericDeclaringTypeAt(i) and
|
||||
children = t.getNumberOfArgumentsSelf() and
|
||||
(if this.isDeclaringTypeAt(i) then offset = 1 else offset = 0) and
|
||||
if children = 0 then j in [0 .. offset] else j in [0 .. 2 * children + offset]
|
||||
)
|
||||
|
|
||||
this.toStringConstructedPart(i, j) order by i desc, j
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -482,7 +511,7 @@ module Gvn {
|
||||
newtype TGvnType =
|
||||
TLeafGvnType(LeafType t) or
|
||||
TTypeParameterGvnType() or
|
||||
TConstructedGvnType(ConstructedGvnTypeList l)
|
||||
TConstructedGvnType(ConstructedGvnTypeList l) { l.isFullyConstructed() }
|
||||
|
||||
cached
|
||||
newtype TConstructedGvnTypeList =
|
||||
|
||||
@@ -48,6 +48,8 @@ class UsingNamespaceDirective extends UsingDirective, @using_namespace_directive
|
||||
Namespace getImportedNamespace() { using_namespace_directives(this, result) }
|
||||
|
||||
override string toString() { result = "using ...;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "UsingNamespaceDirective" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,4 +63,6 @@ class UsingStaticDirective extends UsingDirective, @using_static_directive {
|
||||
ValueOrRefType getTarget() { using_static_directives(this, getTypeRef(result)) }
|
||||
|
||||
override string toString() { result = "using static ...;" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "UsingStaticDirective" }
|
||||
}
|
||||
|
||||
@@ -194,6 +194,8 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Parameter" }
|
||||
|
||||
/**
|
||||
* Gets the default value of this parameter, if any. For example, the
|
||||
* default value of `numberOfTries` is `3` in
|
||||
@@ -407,6 +409,8 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent
|
||||
override Location getALocation() { field_location(this, result) }
|
||||
|
||||
override string toString() { result = Variable.super.toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Field" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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 + ")")
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ abstract class NodeImpl extends Node {
|
||||
|
||||
/** Gets the type of this node used for type pruning. */
|
||||
cached
|
||||
DataFlowType getDataFlowType() {
|
||||
Gvn::GvnType getDataFlowType() {
|
||||
Stages::DataFlowStage::forceCachingInSameStage() and
|
||||
exists(Type t0 | result = Gvn::getGlobalValueNumber(t0) |
|
||||
t0 = getCSharpType(this.getType())
|
||||
@@ -490,14 +490,23 @@ private Type getCSharpType(DotNet::Type t) {
|
||||
result.matchesHandle(t)
|
||||
}
|
||||
|
||||
/** A GVN type that is either a `DataFlowType` or unifiable with a `DataFlowType`. */
|
||||
private class DataFlowTypeOrUnifiable extends Gvn::GvnType {
|
||||
pragma[nomagic]
|
||||
DataFlowTypeOrUnifiable() {
|
||||
this instanceof DataFlowType or
|
||||
Gvn::unifiable(any(DataFlowType t), this)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private TypeParameter getATypeParameterSubType(DataFlowType t) {
|
||||
private TypeParameter getATypeParameterSubType(DataFlowTypeOrUnifiable t) {
|
||||
not t instanceof Gvn::TypeParameterGvnType and
|
||||
exists(Type t0 | t = Gvn::getGlobalValueNumber(t0) | implicitConversionRestricted(result, t0))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private DataFlowType getANonTypeParameterSubType(DataFlowType t) {
|
||||
private Gvn::GvnType getANonTypeParameterSubType(DataFlowTypeOrUnifiable t) {
|
||||
not t instanceof Gvn::TypeParameterGvnType and
|
||||
not result instanceof Gvn::TypeParameterGvnType and
|
||||
exists(Type t1, Type t2 |
|
||||
@@ -728,12 +737,8 @@ private module Cached {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if GVNs `t1` and `t2` may have a common sub type. Neither `t1` nor
|
||||
* `t2` are allowed to be type parameters.
|
||||
*/
|
||||
cached
|
||||
predicate commonSubType(DataFlowType t1, DataFlowType t2) {
|
||||
pragma[nomagic]
|
||||
private predicate commonSubTypeGeneral(DataFlowTypeOrUnifiable t1, DataFlowType t2) {
|
||||
not t1 instanceof Gvn::TypeParameterGvnType and
|
||||
t1 = t2
|
||||
or
|
||||
@@ -742,11 +747,18 @@ private module Cached {
|
||||
getANonTypeParameterSubType(t1) = getANonTypeParameterSubType(t2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if GVNs `t1` and `t2` may have a common sub type. Neither `t1` nor
|
||||
* `t2` are allowed to be type parameters.
|
||||
*/
|
||||
cached
|
||||
predicate commonSubType(DataFlowType t1, DataFlowType t2) { commonSubTypeGeneral(t1, t2) }
|
||||
|
||||
cached
|
||||
predicate commonSubTypeUnifiableLeft(DataFlowType t1, DataFlowType t2) {
|
||||
exists(DataFlowType t |
|
||||
exists(Gvn::GvnType t |
|
||||
Gvn::unifiable(t1, t) and
|
||||
commonSubType(t, t2)
|
||||
commonSubTypeGeneral(t, t2)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2004,7 +2016,7 @@ module LibraryFlow {
|
||||
|
||||
/** Gets the type of content `c`. */
|
||||
pragma[noinline]
|
||||
private DataFlowType getContentType(Content c) {
|
||||
private Gvn::GvnType getContentType(Content c) {
|
||||
exists(Type t | result = Gvn::getGlobalValueNumber(t) |
|
||||
t = c.(FieldContent).getField().getType()
|
||||
or
|
||||
@@ -2031,7 +2043,7 @@ class LibraryCodeNode extends NodeImpl, TLibraryCodeNode {
|
||||
|
||||
override Callable getEnclosingCallableImpl() { result = callCfn.getEnclosingCallable() }
|
||||
|
||||
override DataFlowType getDataFlowType() {
|
||||
override Gvn::GvnType getDataFlowType() {
|
||||
exists(LibraryFlow::AdjustedAccessPath ap |
|
||||
state = LibraryFlow::TLibraryCodeNodeAfterReadState(ap) and
|
||||
if sinkAp.length() = 0 and state.isLastReadState() and preservesValue = true
|
||||
@@ -2194,6 +2206,20 @@ private class ReadStepConfiguration extends ControlFlowReachabilityConfiguration
|
||||
|
||||
predicate readStep = readStepImpl/3;
|
||||
|
||||
/**
|
||||
* An entity used to represent the type of data-flow node. Two nodes will have
|
||||
* the same `DataFlowType` when the underlying `Type`s are structurally equal
|
||||
* modulo type parameters and identity conversions.
|
||||
*
|
||||
* For example, `Func<T, int>` and `Func<S, int>` are mapped to the same
|
||||
* `DataFlowType`, while `Func<T, int>` and `Func<string, int>` are not, because
|
||||
* `string` is not a type parameter.
|
||||
*/
|
||||
class DataFlowType extends Gvn::GvnType {
|
||||
pragma[nomagic]
|
||||
DataFlowType() { this = any(NodeImpl n).getDataFlowType() }
|
||||
}
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
DataFlowType getNodeType(NodeImpl n) { result = n.getDataFlowType() }
|
||||
|
||||
@@ -2323,8 +2349,6 @@ class CastNode extends Node {
|
||||
|
||||
class DataFlowExpr = DotNet::Expr;
|
||||
|
||||
class DataFlowType = Gvn::GvnType;
|
||||
|
||||
/** Holds if `e` is an expression that always has the same Boolean value `val`. */
|
||||
private predicate constantBooleanExpr(Expr e, boolean val) {
|
||||
e = any(AbstractValues::BooleanValue bv | val = bv.getValue()).getAnExpr()
|
||||
@@ -2354,7 +2378,7 @@ private predicate viableConstantBooleanParamArg(
|
||||
)
|
||||
}
|
||||
|
||||
int accessPathLimit() { result = 3 }
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
/**
|
||||
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
|
||||
|
||||
@@ -222,10 +222,10 @@ class Content extends TContent {
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets the type of the object containing this content. */
|
||||
deprecated DataFlowType getContainerType() { none() }
|
||||
deprecated Gvn::GvnType getContainerType() { none() }
|
||||
|
||||
/** Gets the type of this content. */
|
||||
deprecated DataFlowType getType() { none() }
|
||||
deprecated Gvn::GvnType getType() { none() }
|
||||
}
|
||||
|
||||
/** A reference to a field. */
|
||||
@@ -241,11 +241,11 @@ class FieldContent extends Content, TFieldContent {
|
||||
|
||||
override Location getLocation() { result = f.getLocation() }
|
||||
|
||||
deprecated override DataFlowType getContainerType() {
|
||||
deprecated override Gvn::GvnType getContainerType() {
|
||||
result = Gvn::getGlobalValueNumber(f.getDeclaringType())
|
||||
}
|
||||
|
||||
deprecated override DataFlowType getType() { result = Gvn::getGlobalValueNumber(f.getType()) }
|
||||
deprecated override Gvn::GvnType getType() { result = Gvn::getGlobalValueNumber(f.getType()) }
|
||||
}
|
||||
|
||||
/** A reference to a property. */
|
||||
@@ -261,11 +261,11 @@ class PropertyContent extends Content, TPropertyContent {
|
||||
|
||||
override Location getLocation() { result = p.getLocation() }
|
||||
|
||||
deprecated override DataFlowType getContainerType() {
|
||||
deprecated override Gvn::GvnType getContainerType() {
|
||||
result = Gvn::getGlobalValueNumber(p.getDeclaringType())
|
||||
}
|
||||
|
||||
deprecated override DataFlowType getType() { result = Gvn::getGlobalValueNumber(p.getType()) }
|
||||
deprecated override Gvn::GvnType getType() { result = Gvn::getGlobalValueNumber(p.getType()) }
|
||||
}
|
||||
|
||||
/** A reference to an element in a collection. */
|
||||
|
||||
@@ -78,6 +78,8 @@ class ThisAccess extends Access, @this_access_expr {
|
||||
override Class getTarget() { result = this.getEnclosingCallable().getDeclaringType() }
|
||||
|
||||
override string toString() { result = "this access" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ThisAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,6 +98,8 @@ class BaseAccess extends Access, @base_access_expr {
|
||||
}
|
||||
|
||||
override string toString() { result = "base access" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BaseAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,6 +225,8 @@ class ParameterAccess extends LocalScopeVariableAccess, @parameter_access_expr {
|
||||
override Parameter getTarget() { expr_access(this, result) }
|
||||
|
||||
override string toString() { result = "access to parameter " + this.getTarget().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ParameterAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,6 +279,8 @@ class LocalVariableAccess extends LocalScopeVariableAccess, @local_variable_acce
|
||||
not this instanceof LocalVariableDeclExpr and
|
||||
result = "access to local variable " + this.getTarget().getName()
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalVariableAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -325,6 +333,8 @@ class FieldAccess extends AssignableMemberAccess, VariableAccess, @field_access_
|
||||
override Field getTarget() { expr_access(this, result) }
|
||||
|
||||
override string toString() { result = "access to field " + this.getTarget().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FieldAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,6 +388,8 @@ class MemberConstantAccess extends FieldAccess {
|
||||
override MemberConstant getTarget() { expr_access(this, result) }
|
||||
|
||||
override string toString() { result = "access to constant " + this.getTarget().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MemberConstantAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -636,6 +648,8 @@ library class EventAccessExpr extends Expr, @event_access_expr {
|
||||
*/
|
||||
class EventAccess extends AssignableMemberAccess, EventAccessExpr {
|
||||
override Event getTarget() { result = getEvent() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EventAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -727,6 +741,8 @@ class MethodAccess extends MemberAccess, CallableAccess {
|
||||
override Method getTarget() { expr_access(this, result) }
|
||||
|
||||
override string toString() { result = "access to method " + this.getTarget().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MethodAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -750,6 +766,8 @@ class LocalFunctionAccess extends CallableAccess {
|
||||
override LocalFunction getTarget() { expr_access(this, result) }
|
||||
|
||||
override string toString() { result = "access to local function " + this.getTarget().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalFunctionAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -787,6 +805,8 @@ class TypeAccess extends MemberAccess, @type_access_expr {
|
||||
override Type getTarget() { result = this.getType() }
|
||||
|
||||
override string toString() { result = "access to type " + this.getTarget().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TypeAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -807,6 +827,8 @@ class ArrayAccess extends ElementAccess, @array_access_expr {
|
||||
// Although an array (element) can be assigned a value, there is no
|
||||
// corresponding assignable (`ArrayAccess` does not extend `MemberAccess`)
|
||||
override Assignable getTarget() { none() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ArrayAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -840,4 +862,6 @@ class NamespaceAccess extends Access, @namespace_access_expr {
|
||||
override Namespace getTarget() { expr_access(this, result) }
|
||||
|
||||
override string toString() { result = "access to namespace " + this.getTarget().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NamespaceAccess" }
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ class UnaryArithmeticOperation extends ArithmeticOperation, UnaryOperation, @un_
|
||||
*/
|
||||
class UnaryMinusExpr extends UnaryArithmeticOperation, @minus_expr {
|
||||
override string getOperator() { result = "-" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "UnaryMinusExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,6 +36,8 @@ class UnaryMinusExpr extends UnaryArithmeticOperation, @minus_expr {
|
||||
*/
|
||||
class UnaryPlusExpr extends UnaryArithmeticOperation, @plus_expr {
|
||||
override string getOperator() { result = "+" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "UnaryPlusExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,18 +65,24 @@ class DecrementOperation extends MutatorOperation, @decr_op_expr {
|
||||
/**
|
||||
* A prefix increment operation, for example `++x`.
|
||||
*/
|
||||
class PreIncrExpr extends IncrementOperation, @pre_incr_expr { }
|
||||
class PreIncrExpr extends IncrementOperation, @pre_incr_expr {
|
||||
override string getAPrimaryQlClass() { result = "PreIncrExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A prefix decrement operation, for example `--x`.
|
||||
*/
|
||||
class PreDecrExpr extends DecrementOperation, @pre_decr_expr { }
|
||||
class PreDecrExpr extends DecrementOperation, @pre_decr_expr {
|
||||
override string getAPrimaryQlClass() { result = "PreDecrExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A postfix increment operation, for example `x++`.
|
||||
*/
|
||||
class PostIncrExpr extends IncrementOperation, @post_incr_expr {
|
||||
override string toString() { result = "..." + this.getOperator() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PostIncrExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,6 +90,8 @@ class PostIncrExpr extends IncrementOperation, @post_incr_expr {
|
||||
*/
|
||||
class PostDecrExpr extends DecrementOperation, @post_decr_expr {
|
||||
override string toString() { result = "..." + this.getOperator() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PostDecrExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,6 +109,8 @@ class BinaryArithmeticOperation extends ArithmeticOperation, BinaryOperation, @b
|
||||
*/
|
||||
class AddExpr extends BinaryArithmeticOperation, @add_expr {
|
||||
override string getOperator() { result = "+" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AddExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,6 +118,8 @@ class AddExpr extends BinaryArithmeticOperation, @add_expr {
|
||||
*/
|
||||
class SubExpr extends BinaryArithmeticOperation, @sub_expr {
|
||||
override string getOperator() { result = "-" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SubExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,6 +127,8 @@ class SubExpr extends BinaryArithmeticOperation, @sub_expr {
|
||||
*/
|
||||
class MulExpr extends BinaryArithmeticOperation, @mul_expr {
|
||||
override string getOperator() { result = "*" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MulExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +142,8 @@ class DivExpr extends BinaryArithmeticOperation, @div_expr {
|
||||
|
||||
/** Gets the denominator of this division operation. */
|
||||
Expr getDenominator() { result = getRightOperand() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DivExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,4 +151,6 @@ class DivExpr extends BinaryArithmeticOperation, @div_expr {
|
||||
*/
|
||||
class RemExpr extends BinaryArithmeticOperation, @rem_expr {
|
||||
override string getOperator() { result = "%" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "RemExpr" }
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ class LocalVariableDeclAndInitExpr extends LocalVariableDeclExpr, Assignment {
|
||||
override LocalVariableAccess getLValue() { result = Assignment.super.getLValue() }
|
||||
|
||||
override string toString() { result = LocalVariableDeclExpr.super.toString() + " = ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalVariableDeclAndInitExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,6 +54,8 @@ class AssignExpr extends Assignment, @simple_assign_expr {
|
||||
override string getOperator() { result = "=" }
|
||||
|
||||
override string toString() { result = "... = ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,6 +105,8 @@ class AssignArithmeticOperation extends AssignOperation, @assign_arith_expr { }
|
||||
*/
|
||||
class AssignAddExpr extends AssignArithmeticOperation, @assign_add_expr {
|
||||
override string getOperator() { result = "+=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignAddExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,6 +114,8 @@ class AssignAddExpr extends AssignArithmeticOperation, @assign_add_expr {
|
||||
*/
|
||||
class AssignSubExpr extends AssignArithmeticOperation, @assign_sub_expr {
|
||||
override string getOperator() { result = "-=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignSubExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,6 +123,8 @@ class AssignSubExpr extends AssignArithmeticOperation, @assign_sub_expr {
|
||||
*/
|
||||
class AssignMulExpr extends AssignArithmeticOperation, @assign_mul_expr {
|
||||
override string getOperator() { result = "*=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignMulExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,6 +132,8 @@ class AssignMulExpr extends AssignArithmeticOperation, @assign_mul_expr {
|
||||
*/
|
||||
class AssignDivExpr extends AssignArithmeticOperation, @assign_div_expr {
|
||||
override string getOperator() { result = "/=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignDivExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,6 +141,8 @@ class AssignDivExpr extends AssignArithmeticOperation, @assign_div_expr {
|
||||
*/
|
||||
class AssignRemExpr extends AssignArithmeticOperation, @assign_rem_expr {
|
||||
override string getOperator() { result = "%=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignRemExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,6 +160,8 @@ class AssignBitwiseOperation extends AssignOperation, @assign_bitwise_expr { }
|
||||
*/
|
||||
class AssignAndExpr extends AssignBitwiseOperation, @assign_and_expr {
|
||||
override string getOperator() { result = "&=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignAndExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,6 +169,8 @@ class AssignAndExpr extends AssignBitwiseOperation, @assign_and_expr {
|
||||
*/
|
||||
class AssignOrExpr extends AssignBitwiseOperation, @assign_or_expr {
|
||||
override string getOperator() { result = "|=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignOrExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,6 +178,8 @@ class AssignOrExpr extends AssignBitwiseOperation, @assign_or_expr {
|
||||
*/
|
||||
class AssignXorExpr extends AssignBitwiseOperation, @assign_xor_expr {
|
||||
override string getOperator() { result = "^=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignXorExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,6 +187,8 @@ class AssignXorExpr extends AssignBitwiseOperation, @assign_xor_expr {
|
||||
*/
|
||||
class AssignLShiftExpr extends AssignBitwiseOperation, @assign_lshift_expr {
|
||||
override string getOperator() { result = "<<=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignLShiftExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,6 +196,8 @@ class AssignLShiftExpr extends AssignBitwiseOperation, @assign_lshift_expr {
|
||||
*/
|
||||
class AssignRShiftExpr extends AssignBitwiseOperation, @assign_rshift_expr {
|
||||
override string getOperator() { result = ">>=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignRShiftExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,6 +232,8 @@ class AddOrRemoveEventExpr extends AssignOperation, @assign_event_expr {
|
||||
*/
|
||||
class AddEventExpr extends AddOrRemoveEventExpr, @add_event_expr {
|
||||
override string toString() { result = "... += ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AddEventExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,6 +255,8 @@ class AddEventExpr extends AddOrRemoveEventExpr, @add_event_expr {
|
||||
*/
|
||||
class RemoveEventExpr extends AddOrRemoveEventExpr, @remove_event_expr {
|
||||
override string toString() { result = "... -= ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "RemoveEventExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -236,4 +264,6 @@ class RemoveEventExpr extends AddOrRemoveEventExpr, @remove_event_expr {
|
||||
*/
|
||||
class AssignCoalesceExpr extends AssignOperation, @assign_coalesce_expr {
|
||||
override string toString() { result = "... ??= ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AssignCoalesceExpr" }
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ class UnaryBitwiseOperation extends BitwiseOperation, UnaryOperation, @un_bit_op
|
||||
*/
|
||||
class ComplementExpr extends UnaryBitwiseOperation, @bit_not_expr {
|
||||
override string getOperator() { result = "~" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ComplementExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,6 +42,8 @@ class BinaryBitwiseOperation extends BitwiseOperation, BinaryOperation, @bin_bit
|
||||
*/
|
||||
class LShiftExpr extends BinaryBitwiseOperation, @lshift_expr {
|
||||
override string getOperator() { result = "<<" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LShiftExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,6 +51,8 @@ class LShiftExpr extends BinaryBitwiseOperation, @lshift_expr {
|
||||
*/
|
||||
class RShiftExpr extends BinaryBitwiseOperation, @rshift_expr {
|
||||
override string getOperator() { result = ">>" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "RShiftExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,6 +60,8 @@ class RShiftExpr extends BinaryBitwiseOperation, @rshift_expr {
|
||||
*/
|
||||
class BitwiseAndExpr extends BinaryBitwiseOperation, @bit_and_expr {
|
||||
override string getOperator() { result = "&" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BitwiseAndExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,6 +69,8 @@ class BitwiseAndExpr extends BinaryBitwiseOperation, @bit_and_expr {
|
||||
*/
|
||||
class BitwiseOrExpr extends BinaryBitwiseOperation, @bit_or_expr {
|
||||
override string getOperator() { result = "|" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BitwiseOrExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,4 +78,6 @@ class BitwiseOrExpr extends BinaryBitwiseOperation, @bit_or_expr {
|
||||
*/
|
||||
class BitwiseXorExpr extends BinaryBitwiseOperation, @bit_xor_expr {
|
||||
override string getOperator() { result = "^" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BitwiseXorExpr" }
|
||||
}
|
||||
|
||||
@@ -297,6 +297,8 @@ class MethodCall extends Call, QualifiableExpr, LateBindableExpr, @method_invoca
|
||||
|
||||
override string toString() { result = "call to method " + concat(this.getTarget().getName()) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MethodCall" }
|
||||
|
||||
override Expr getRawArgument(int i) {
|
||||
if exists(getQualifier())
|
||||
then
|
||||
@@ -405,6 +407,8 @@ class ConstructorInitializer extends Call, @constructor_init_expr {
|
||||
|
||||
override string toString() { result = "call to constructor " + this.getTarget().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ConstructorInitializer" }
|
||||
|
||||
private ValueOrRefType getTargetType() {
|
||||
result = this.getTarget().getDeclaringType().getSourceDeclaration()
|
||||
}
|
||||
@@ -493,6 +497,8 @@ class OperatorCall extends Call, LateBindableExpr, @operator_invocation_expr {
|
||||
override Operator getARuntimeTarget() { result = Call.super.getARuntimeTarget() }
|
||||
|
||||
override string toString() { result = "call to operator " + this.getTarget().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "OperatorCall" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -594,6 +600,8 @@ class DelegateCall extends Call, @delegate_invocation_expr {
|
||||
Expr getDelegateExpr() { result = this.getChild(-1) }
|
||||
|
||||
override string toString() { result = "delegate call" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DelegateCall" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -638,6 +646,8 @@ class PropertyCall extends AccessorCall, PropertyAccessExpr {
|
||||
}
|
||||
|
||||
override string toString() { result = PropertyAccessExpr.super.toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PropertyCall" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -673,6 +683,8 @@ class IndexerCall extends AccessorCall, IndexerAccessExpr {
|
||||
}
|
||||
|
||||
override string toString() { result = IndexerAccessExpr.super.toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "IndexerCall" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -717,6 +729,8 @@ class EventCall extends AccessorCall, EventAccessExpr {
|
||||
}
|
||||
|
||||
override string toString() { result = EventAccessExpr.super.toString() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EventCall" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -736,4 +750,6 @@ class LocalFunctionCall extends Call, @local_function_invocation_expr {
|
||||
override LocalFunction getTarget() { expr_call(this, result) }
|
||||
|
||||
override string toString() { result = "call to local function " + getTarget().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LocalFunctionCall" }
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ class EqualityOperation extends ComparisonOperation, @equality_op_expr { }
|
||||
*/
|
||||
class EQExpr extends EqualityOperation, @eq_expr {
|
||||
override string getOperator() { result = "==" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "EQExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,6 +32,8 @@ class EQExpr extends EqualityOperation, @eq_expr {
|
||||
*/
|
||||
class NEExpr extends EqualityOperation, @ne_expr {
|
||||
override string getOperator() { result = "!=" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NEExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,6 +68,8 @@ class GTExpr extends RelationalOperation, @gt_expr {
|
||||
override Expr getGreaterOperand() { result = getLeftOperand() }
|
||||
|
||||
override Expr getLesserOperand() { result = getRightOperand() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GTExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,6 +81,8 @@ class LTExpr extends RelationalOperation, @lt_expr {
|
||||
override Expr getGreaterOperand() { result = getRightOperand() }
|
||||
|
||||
override Expr getLesserOperand() { result = getLeftOperand() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LTExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,6 +94,8 @@ class GEExpr extends RelationalOperation, @ge_expr {
|
||||
override Expr getGreaterOperand() { result = getLeftOperand() }
|
||||
|
||||
override Expr getLesserOperand() { result = getRightOperand() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GEExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,4 +107,6 @@ class LEExpr extends RelationalOperation, @le_expr {
|
||||
override Expr getGreaterOperand() { result = getRightOperand() }
|
||||
|
||||
override Expr getLesserOperand() { result = getLeftOperand() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LEExpr" }
|
||||
}
|
||||
|
||||
@@ -73,6 +73,8 @@ class ObjectInitializer extends ObjectOrCollectionInitializer, @object_init_expr
|
||||
|
||||
/** Holds if this object initializer has no member initializers. */
|
||||
predicate hasNoMemberInitializers() { not exists(this.getAMemberInitializer()) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ObjectInitializer" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,6 +96,8 @@ class MemberInitializer extends AssignExpr {
|
||||
|
||||
/** Gets the initialized member. */
|
||||
Member getInitializedMember() { result.getAnAccess() = this.getLValue() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MemberInitializer" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -142,6 +146,8 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i
|
||||
|
||||
/** Holds if this collection initializer has no element initializers. */
|
||||
predicate hasNoElementInitializers() { not exists(this.getAnElementInitializer()) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CollectionInitializer" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,6 +163,8 @@ class CollectionInitializer extends ObjectOrCollectionInitializer, @collection_i
|
||||
*/
|
||||
class ElementInitializer extends MethodCall {
|
||||
ElementInitializer() { this.getParent() instanceof CollectionInitializer }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ElementInitializer" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -204,6 +212,10 @@ class ObjectCreation extends Call, LateBindableExpr, @object_creation_expr {
|
||||
then result = this.getArgument(i)
|
||||
else result = this.getArgument(i - 1)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
result = "ObjectCreation" and not this instanceof AnonymousObjectCreation
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,6 +233,8 @@ class AnonymousObjectCreation extends ObjectCreation {
|
||||
AnonymousObjectCreation() { this.getObjectType() instanceof AnonymousClass }
|
||||
|
||||
override ObjectInitializer getInitializer() { result = this.getChild(-1) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AnonymousObjectCreation" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -255,7 +269,9 @@ class DelegateCreation extends Expr, @delegate_creation_expr {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ExplicitDelegateCreation extends DelegateCreation, @explicit_delegate_creation_expr { }
|
||||
class ExplicitDelegateCreation extends DelegateCreation, @explicit_delegate_creation_expr {
|
||||
override string getAPrimaryQlClass() { result = "ExplicitDelegateCreation" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An implicit delegate creation, for example the access to `M` on line 6 in
|
||||
@@ -270,7 +286,9 @@ class ExplicitDelegateCreation extends DelegateCreation, @explicit_delegate_crea
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ImplicitDelegateCreation extends DelegateCreation, @implicit_delegate_creation_expr { }
|
||||
class ImplicitDelegateCreation extends DelegateCreation, @implicit_delegate_creation_expr {
|
||||
override string getAPrimaryQlClass() { result = "ImplicitDelegateCreation" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An array initializer, for example `{ {0, 1}, {2, 3}, {4, 5} }` in
|
||||
@@ -322,6 +340,8 @@ class ArrayInitializer extends Expr, @array_init_expr {
|
||||
predicate hasNoElements() { not exists(this.getAnElement()) }
|
||||
|
||||
override string toString() { result = "{ ..., ... }" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ArrayInitializer" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,6 +390,8 @@ class ArrayCreation extends Expr, @array_creation_expr {
|
||||
predicate isImplicitlyTyped() { implicitly_typed_array_creation(this) }
|
||||
|
||||
override string toString() { result = "array creation of type " + this.getType().getName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ArrayCreation" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -377,6 +399,8 @@ class ArrayCreation extends Expr, @array_creation_expr {
|
||||
*/
|
||||
class Stackalloc extends ArrayCreation {
|
||||
Stackalloc() { stackalloc_array_creation(this) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "Stackalloc" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,6 +429,8 @@ class AnonymousFunctionExpr extends Expr, Callable, @anonymous_function_expr {
|
||||
*/
|
||||
class LambdaExpr extends AnonymousFunctionExpr, @lambda_expr {
|
||||
override string toString() { result = "(...) => ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LambdaExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -413,4 +439,6 @@ class LambdaExpr extends AnonymousFunctionExpr, @lambda_expr {
|
||||
*/
|
||||
class AnonymousMethodExpr extends AnonymousFunctionExpr, @anonymous_method_expr {
|
||||
override string toString() { result = "delegate(...) { ... }" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AnonymousMethodExpr" }
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ class DynamicObjectCreation extends DynamicExpr, ObjectCreation {
|
||||
override string toString() {
|
||||
result = "dynamic object creation of type " + this.getType().getName()
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DynamicObjectCreation" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,6 +68,8 @@ class DynamicObjectCreation extends DynamicExpr, ObjectCreation {
|
||||
*/
|
||||
class DynamicMethodCall extends DynamicExpr, MethodCall {
|
||||
override string toString() { result = "dynamic call to method " + getLateBoundTargetName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DynamicMethodCall" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,6 +98,8 @@ class DynamicMethodCall extends DynamicExpr, MethodCall {
|
||||
*/
|
||||
class DynamicOperatorCall extends DynamicExpr, OperatorCall {
|
||||
override string toString() { result = "dynamic call to operator " + getLateBoundTargetName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DynamicOperatorCall" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,6 +191,8 @@ class DynamicMemberAccess extends DynamicAccess, MemberAccess, AssignableAccess,
|
||||
@dynamic_member_access_expr {
|
||||
override string toString() { result = "dynamic access to member " + getLateBoundTargetName() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DynamicMemberAccess" }
|
||||
|
||||
// The target is unknown when the qualifier is a `dynamic` expression
|
||||
override DynamicMember getTarget() { none() }
|
||||
}
|
||||
@@ -338,6 +346,8 @@ class DynamicAccessorCall extends DynamicAccess {
|
||||
*/
|
||||
class DynamicElementAccess extends DynamicAccess, ElementAccess, @dynamic_element_access_expr {
|
||||
override string toString() { result = "dynamic access to element" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DynamicElementAccess" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -165,6 +165,11 @@ class LocalVariableDeclExpr extends Expr, @local_var_decl_expr {
|
||||
* for example `M(out int x)`.
|
||||
*/
|
||||
predicate isOutArgument() { expr_argument(this, 2) }
|
||||
|
||||
override string getAPrimaryQlClass() {
|
||||
result = "LocalVariableDeclExpr" and
|
||||
not this instanceof LocalVariableDeclAndInitExpr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,6 +251,8 @@ class ParenthesizedExpr extends Expr, @par_expr {
|
||||
Expr getExpr() { result = this.getChild(0) }
|
||||
|
||||
override string toString() { result = "(...)" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ParenthesizedExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,6 +263,8 @@ class CheckedExpr extends Expr, @checked_expr {
|
||||
Expr getExpr() { result = this.getChild(0) }
|
||||
|
||||
override string toString() { result = "checked (...)" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CheckedExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,6 +275,8 @@ class UncheckedExpr extends Expr, @unchecked_expr {
|
||||
Expr getExpr() { result = this.getChild(0) }
|
||||
|
||||
override string toString() { result = "unchecked (...)" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "UncheckedExpr" }
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -319,11 +330,15 @@ class PatternExpr extends Expr {
|
||||
}
|
||||
|
||||
/** A discard pattern, for example `_` in `x is (_, false)` */
|
||||
class DiscardPatternExpr extends DiscardExpr, PatternExpr { }
|
||||
class DiscardPatternExpr extends DiscardExpr, PatternExpr {
|
||||
override string getAPrimaryQlClass() { result = "DiscardPatternExpr" }
|
||||
}
|
||||
|
||||
/** A constant pattern, for example `false` in `x is (_, false)`. */
|
||||
class ConstantPatternExpr extends PatternExpr {
|
||||
ConstantPatternExpr() { this.hasValue() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ConstantPatternExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -343,7 +358,9 @@ class TypePatternExpr extends PatternExpr {
|
||||
}
|
||||
|
||||
/** A type access pattern, for example `string` in `x is string`. */
|
||||
class TypeAccessPatternExpr extends TypePatternExpr, TypeAccess { }
|
||||
class TypeAccessPatternExpr extends TypePatternExpr, TypeAccess {
|
||||
override string getAPrimaryQlClass() { result = "TypeAccessPatternExpr" }
|
||||
}
|
||||
|
||||
/** A pattern that may bind a variable, for example `string s` in `x is string s`. */
|
||||
class BindingPatternExpr extends PatternExpr {
|
||||
@@ -362,6 +379,8 @@ class BindingPatternExpr extends PatternExpr {
|
||||
/** A variable declaration pattern, for example `string s` in `x is string s`. */
|
||||
class VariablePatternExpr extends BindingPatternExpr, LocalVariableDeclExpr {
|
||||
override LocalVariableDeclExpr getVariableDeclExpr() { result = this }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "VariablePatternExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,6 +390,8 @@ class VariablePatternExpr extends BindingPatternExpr, LocalVariableDeclExpr {
|
||||
class RecursivePatternExpr extends BindingPatternExpr, @recursive_pattern_expr {
|
||||
override string toString() { result = "{ ... }" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "RecursivePatternExpr" }
|
||||
|
||||
/**
|
||||
* Gets the position patterns of this recursive pattern, if any.
|
||||
* For example, `(1, _)`.
|
||||
@@ -398,6 +419,8 @@ class PropertyPatternExpr extends Expr, @property_pattern_expr {
|
||||
|
||||
/** Gets the `n`th pattern. */
|
||||
PatternExpr getPattern(int n) { result = this.getChild(n) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PropertyPatternExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -409,6 +432,8 @@ class LabeledPatternExpr extends PatternExpr {
|
||||
|
||||
/** Gets the label of this pattern. */
|
||||
string getLabel() { exprorstmt_name(this, result) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "LabeledPatternExpr" }
|
||||
}
|
||||
|
||||
/** A positional pattern. For example, `(int x, int y)`. */
|
||||
@@ -417,6 +442,8 @@ class PositionalPatternExpr extends Expr, @positional_pattern_expr {
|
||||
|
||||
/** Gets the `n`th pattern. */
|
||||
PatternExpr getPattern(int n) { result = this.getChild(n) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PositionalPatternExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -439,6 +466,8 @@ class IsExpr extends Expr, PatternMatch, @is_expr {
|
||||
override PatternExpr getPattern() { result = this.getChild(1) }
|
||||
|
||||
override string toString() { result = "... is ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "IsExpr" }
|
||||
}
|
||||
|
||||
/** A `switch` expression or statement. */
|
||||
@@ -473,6 +502,8 @@ class SwitchExpr extends Expr, Switch, @switch_expr {
|
||||
override SwitchCaseExpr getCase(int n) { result = this.getChild(n) }
|
||||
|
||||
override SwitchCaseExpr getACase() { result = this.getCase(_) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SwitchExpr" }
|
||||
}
|
||||
|
||||
/** A `case` expression or statement. */
|
||||
@@ -510,6 +541,8 @@ class SwitchCaseExpr extends Expr, Case, @switch_case_expr {
|
||||
// should match all cases due to the type of the expression.
|
||||
this.getPattern() instanceof DiscardPatternExpr
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SwitchCaseExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -574,6 +607,8 @@ class ExplicitCast extends Cast {
|
||||
*/
|
||||
class AsExpr extends Cast, @as_expr {
|
||||
override string toString() { result = "... as ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AsExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -581,6 +616,8 @@ class AsExpr extends Cast, @as_expr {
|
||||
*/
|
||||
class CastExpr extends Cast, @cast_expr {
|
||||
override string toString() { result = "(...) ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "CastExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -594,6 +631,8 @@ class TypeofExpr extends Expr, @typeof_expr {
|
||||
TypeAccess getTypeAccess() { result = this.getChild(0) }
|
||||
|
||||
override string toString() { result = "typeof(...)" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TypeofExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -609,6 +648,8 @@ class DefaultValueExpr extends Expr, @default_expr {
|
||||
override string toString() {
|
||||
if exists(getTypeAccess()) then result = "default(...)" else result = "default"
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DefaultValueExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -624,6 +665,8 @@ class SizeofExpr extends UnaryOperation, @sizeof_expr {
|
||||
override string getOperator() { result = "sizeof(..)" }
|
||||
|
||||
override string toString() { result = "sizeof(..)" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SizeofExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -662,6 +705,8 @@ class SizeofExpr extends UnaryOperation, @sizeof_expr {
|
||||
*/
|
||||
class PointerIndirectionExpr extends UnaryOperation, @pointer_indirection_expr {
|
||||
override string getOperator() { result = "*" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PointerIndirectionExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -679,6 +724,8 @@ class PointerIndirectionExpr extends UnaryOperation, @pointer_indirection_expr {
|
||||
*/
|
||||
class AddressOfExpr extends UnaryOperation, @address_of_expr {
|
||||
override string getOperator() { result = "&" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AddressOfExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -689,6 +736,8 @@ class AwaitExpr extends Expr, @await_expr {
|
||||
Expr getExpr() { result = getChild(0) }
|
||||
|
||||
override string toString() { result = "await ..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AwaitExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -710,6 +759,8 @@ class NameOfExpr extends Expr, @nameof_expr {
|
||||
* `nameof(x.F)`.
|
||||
*/
|
||||
Access getAccess() { result = this.getChild(0) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NameOfExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -724,6 +775,8 @@ class NameOfExpr extends Expr, @nameof_expr {
|
||||
class InterpolatedStringExpr extends Expr, @interpolated_string_expr {
|
||||
override string toString() { result = "$\"...\"" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "InterpolatedStringExpr" }
|
||||
|
||||
/**
|
||||
* Gets the insert at index `i` in this interpolated string, if any. For
|
||||
* example, the insert at index `i = 1` is `name` in `$"Hello, {name}!"`.
|
||||
@@ -787,6 +840,8 @@ class ThrowExpr extends Expr, ThrowElement, @throw_expr {
|
||||
*/
|
||||
// overriden for more precise qldoc
|
||||
override Expr getExpr() { result = ThrowElement.super.getExpr() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ThrowExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -884,6 +939,8 @@ class TupleExpr extends Expr, @tuple_expr {
|
||||
|
||||
/** Holds if this tuple is a read access. */
|
||||
predicate isReadAccess() { not this = getAnAssignOrForeachChild() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TupleExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -902,6 +959,8 @@ class RefExpr extends Expr, @ref_expr {
|
||||
override string toString() { result = "ref ..." }
|
||||
|
||||
override Type getType() { result = getExpr().getType() }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "RefExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -913,6 +972,8 @@ class RefExpr extends Expr, @ref_expr {
|
||||
*/
|
||||
class DiscardExpr extends Expr, @discard_expr {
|
||||
override string toString() { result = "_" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "DiscardExpr" }
|
||||
}
|
||||
|
||||
private class UnknownExpr extends Expr, @unknown_expr {
|
||||
@@ -944,6 +1005,8 @@ class RangeExpr extends Expr, @range_expr {
|
||||
|
||||
/** Holds if this range expression has a right hand operand. */
|
||||
predicate hasEnd() { exists(this.getEnd()) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "RangeExpr" }
|
||||
}
|
||||
|
||||
/** An index expression, for example `^1` meaning "1 from the end". */
|
||||
@@ -951,6 +1014,8 @@ class IndexExpr extends Expr, @index_expr {
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "^..." }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "IndexExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -968,4 +1033,6 @@ class SuppressNullableWarningExpr extends Expr, @suppress_nullable_warning_expr
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
override string toString() { result = "...!" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SuppressNullableWarningExpr" }
|
||||
}
|
||||
|
||||
@@ -27,12 +27,16 @@ class BoolLiteral extends Literal, @bool_literal_expr {
|
||||
or
|
||||
getValue() = "false" and result = false
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BoolLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A Unicode character literal, for example `'a'`.
|
||||
*/
|
||||
class CharLiteral extends Literal, @char_literal_expr { }
|
||||
class CharLiteral extends Literal, @char_literal_expr {
|
||||
override string getAPrimaryQlClass() { result = "CharLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An integer literal. Either an `int` literal (`IntLiteral`), a `long`
|
||||
@@ -44,22 +48,30 @@ class IntegerLiteral extends DotNet::IntLiteral, Literal, @integer_literal_expr
|
||||
/**
|
||||
* An `int` literal, for example `0`.
|
||||
*/
|
||||
class IntLiteral extends IntegerLiteral, @int_literal_expr { }
|
||||
class IntLiteral extends IntegerLiteral, @int_literal_expr {
|
||||
override string getAPrimaryQlClass() { result = "IntLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `long` literal, for example `-5L`.
|
||||
*/
|
||||
class LongLiteral extends IntegerLiteral, @long_literal_expr { }
|
||||
class LongLiteral extends IntegerLiteral, @long_literal_expr {
|
||||
override string getAPrimaryQlClass() { result = "LongLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `uint` literal, for example `5U`.
|
||||
*/
|
||||
class UIntLiteral extends IntegerLiteral, @uint_literal_expr { }
|
||||
class UIntLiteral extends IntegerLiteral, @uint_literal_expr {
|
||||
override string getAPrimaryQlClass() { result = "UIntLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `ulong` literal, for example `5UL`.
|
||||
*/
|
||||
class ULongLiteral extends IntegerLiteral, @ulong_literal_expr { }
|
||||
class ULongLiteral extends IntegerLiteral, @ulong_literal_expr {
|
||||
override string getAPrimaryQlClass() { result = "ULongLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A floating point literal. Either a `float` literal (`FloatLiteral`), a
|
||||
@@ -71,26 +83,36 @@ class RealLiteral extends Literal, @real_literal_expr { }
|
||||
/**
|
||||
* A `float` literal, for example `5F`.
|
||||
*/
|
||||
class FloatLiteral extends RealLiteral, @float_literal_expr { }
|
||||
class FloatLiteral extends RealLiteral, @float_literal_expr {
|
||||
override string getAPrimaryQlClass() { result = "FloatLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `double` literal, for example `5D`.
|
||||
*/
|
||||
class DoubleLiteral extends RealLiteral, @double_literal_expr { }
|
||||
class DoubleLiteral extends RealLiteral, @double_literal_expr {
|
||||
override string getAPrimaryQlClass() { result = "DoubleLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `decimal` literal, for example `5m`.
|
||||
*/
|
||||
class DecimalLiteral extends RealLiteral, @decimal_literal_expr { }
|
||||
class DecimalLiteral extends RealLiteral, @decimal_literal_expr {
|
||||
override string getAPrimaryQlClass() { result = "DecimalLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `string` literal, for example `"Hello, World!"`.
|
||||
*/
|
||||
class StringLiteral extends DotNet::StringLiteral, Literal, @string_literal_expr {
|
||||
override string toString() { result = "\"" + getValue().replaceAll("\"", "\\\"") + "\"" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "StringLiteral" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `null` literal.
|
||||
*/
|
||||
class NullLiteral extends DotNet::NullLiteral, Literal, @null_literal_expr { }
|
||||
class NullLiteral extends DotNet::NullLiteral, Literal, @null_literal_expr {
|
||||
override string getAPrimaryQlClass() { result = "NullLiteral" }
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user