mirror of
https://github.com/github/codeql.git
synced 2026-04-21 23:14:03 +02:00
Merge branch 'master' into cpp340a
So as to get to change-notes/1.21/analysis-cpp.md
This commit is contained in:
@@ -1,19 +1,23 @@
|
||||
# Improvements to Python analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
## General improvements
|
||||
### Extractor changes
|
||||
|
||||
> Changes that affect alerts in many files or from many queries
|
||||
> For example, changes to file classification
|
||||
The extractor now parses all Python code from a single unified grammar. This means that almost all Python code will be successfully parsed, even if mutually incompatible Python code is present in the same project. This also means that Python code for any version can be correctly parsed on a worker running any other supported version of Python. For example, Python 3.7 code is parsed correctly, even if the installed version of Python is only 3.5. This will reduce the number of syntax errors found in many projects.
|
||||
|
||||
### Regular expression analysis improvements
|
||||
|
||||
The Python `re` (regular expressions) module library has a couple of constants called `MULTILINE` and `VERBOSE` which determine the parsing of regular expressions. Python 3.6 changed the implementation of these constants, which resulted in false positive results for some queries. The relevant QL libraries have been updated to support both implementations which will remove false positive results from projects that use Python 3.6 and later versions.
|
||||
|
||||
### API improvements
|
||||
|
||||
The constants `MULTILINE` and `VERBOSE` in `re` module, are now understood for Python 3.6 and upward.
|
||||
Removes false positives seen when using Python 3.6, but not when using earlier versions.
|
||||
The API has been improved to declutter the global namespace and improve discoverability and readability.
|
||||
* New predicates `ModuleObject::named(name)` and `ModuleObject.attr(name)` have been added, allowing more readable access to common objects. For example, `(any ModuleObject m | m.getName() = "sys").getAttribute("exit")` can be replaced with `ModuleObject::named("sys").attr("exit")`
|
||||
* The API for accessing builtin functions has been improved. Predicates of the form `theXXXFunction()`, such as `theLenFunction()`, have been deprecated in favour of `Object::builtin(name)`.
|
||||
* The API for accessing builtin functions has been improved. Predicates of the form `theXXXFunction()`, such as `theLenFunction()`, have been deprecated in favor of `Object::builtin(name)`.
|
||||
* A configuration based API has been added for writing data flow and taint tracking queries. This is provided as a convenience for query authors who have written data flow or taint tracking queries for other languages, so they can use a similar format of query across multiple languages.
|
||||
|
||||
## New queries
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
@@ -24,7 +28,7 @@ The API has been improved to declutter the global namespace and improve discover
|
||||
| Overly permissive file permissions (`py/overly-permissive-file`) | security, external/cwe/cwe-732 | Finds instances where a file is created with overly permissive permissions. Results are not shown on LGTM by default. |
|
||||
| Use of insecure SSL/TLS version (`py/insecure-protocol`) | security, external/cwe/cwe-327 | Finds instances where a known insecure protocol has been specified. Results are shown on LGTM by default. |
|
||||
|
||||
## Changes to existing queries
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
@@ -35,11 +39,8 @@ The API has been improved to declutter the global namespace and improve discover
|
||||
| Unused import (`py/unused-import`) | Fewer false positive results | Results where the imported module is used in a `doctest` string are no longer reported. |
|
||||
| Unused import (`py/unused-import`) | Fewer false positive results | Results where the imported module is used in a type-hint comment are no longer reported. |
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
* The extractor now parses all Python code from a single unified grammar. This means that almost all Python code will be successfully parsed, even if mutually incompatible Python code is present in the same project. This also means that Python code for any version can be correctly parsed on a worker running any other supported version of Python. For example, Python 3.7 code is parsed correctly, even if the installed version of Python is only 3.5.
|
||||
|
||||
## Changes to QL libraries
|
||||
## Changes to QL libraries
|
||||
|
||||
* Added support for the `dill` pickle library.
|
||||
* Added support for the `bottle` web framework.
|
||||
|
||||
@@ -2,25 +2,11 @@
|
||||
|
||||
# Improvements to JavaScript analysis
|
||||
|
||||
> NOTES
|
||||
>
|
||||
> Please describe your changes in terms that are suitable for
|
||||
> customers to read. These notes will have only minor tidying up
|
||||
> before they are published as part of the release notes.
|
||||
>
|
||||
> This file is written for lgtm users and should contain *only*
|
||||
> notes about changes that affect lgtm enterprise users. Add
|
||||
> any other customer-facing changes to the `studio-java.md`
|
||||
> file.
|
||||
>
|
||||
|
||||
## General improvements
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
* Parallel extraction of JavaScript files (but not TypeScript files) on LGTM is now supported. The `LGTM_THREADS` environment variable can be set to indicate how many files should be extracted in parallel. If this variable is not set, parallel extraction is disabled.
|
||||
* The extractor now offers experimental support for [E4X](https://developer.mozilla.org/en-US/docs/Archive/Web/E4X), a legacy language extension developed by Mozilla.
|
||||
* The extractor now supports additional [Flow](https://flow.org/) syntax.
|
||||
* The extractor now supports [Nullish Coalescing](https://github.com/tc39/proposal-nullish-coalescing) expressions.
|
||||
* The extractor now supports [TypeScript 3.2](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-2.html).
|
||||
* The TypeScript extractor now handles the control-flow of logical operators and destructuring assignments more accurately.
|
||||
* Parallel extraction of JavaScript files (but not TypeScript files) on LGTM is now supported. If LGTM is configured to evaluate queries using multiple threads, then JavaScript files are also extracted using multiple threads.
|
||||
* Experimental support for [E4X](https://developer.mozilla.org/en-US/docs/Archive/Web/E4X), a legacy language extension developed by Mozilla, is available.
|
||||
* Additional [Flow](https://flow.org/) syntax is now supported.
|
||||
* [Nullish Coalescing](https://github.com/tc39/proposal-nullish-coalescing) expressions are now supported.
|
||||
* [TypeScript 3.2](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-2.html) is now supported.
|
||||
* The TypeScript extractor now handles the control flow of logical operators and destructuring assignments more accurately.
|
||||
|
||||
@@ -13,4 +13,4 @@ Java,"Java 6 to 11 [2]_.","javac (OpenJDK and Oracle JDK)
|
||||
Eclipse compiler for Java (ECJ) batch compiler",``.java``
|
||||
JavaScript,ECMAScript 2018 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhm``, ``.xhtml``, ``.vue``, ``.json`` [3]_."
|
||||
Python,"2.7, 3.5, 3.6, 3.7",Not applicable,``.py``
|
||||
TypeScript [4]_.,"2.6, 2.7, 2.8, 2.9, 3.0, 3.1",Standard TypeScript compiler,"``.ts``, ``.tsx``"
|
||||
TypeScript [4]_.,"2.6, 2.7, 2.8, 2.9, 3.0, 3.1, 3.2",Standard TypeScript compiler,"``.ts``, ``.tsx``"
|
||||
|
||||
|
22
change-notes/1.21/analysis-cpp.md
Normal file
22
change-notes/1.21/analysis-cpp.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Improvements to C/C++ analysis
|
||||
|
||||
## General improvements
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
|-----------------------------|-----------|--------------------------------------------------------------------|
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Mismatching new/free or malloc/delete (`cpp/new-free-mismatch`) | Fewer false positive results | Fixed an issue where functions were being identified as allocation functions inappropriately. Also affects `cpp/new-array-delete-mismatch` and `cpp/new-delete-array-mismatch`. |
|
||||
| Overflow in uncontrolled allocation size (`cpp/uncontrolled-allocation-size`) | More correct results | This query has been reworked so that it can find a wider variety of results. |
|
||||
| Memory may not be freed (`cpp/memory-may-not-be-freed`) | More correct results | Support added for more Microsoft-specific allocation functions, including `LocalAlloc`, `GlobalAlloc`, `HeapAlloc` and `CoTaskMemAlloc`. |
|
||||
| Memory is never freed (`cpp/memory-never-freed`) | More correct results | Support added for more Microsoft-specific allocation functions, including `LocalAlloc`, `GlobalAlloc`, `HeapAlloc` and `CoTaskMemAlloc`. |
|
||||
| Resource not released in destructor (`cpp/resource-not-released-in-destructor`) | Fewer false positive results | Resource allocation and deallocation functions are now determined more accurately. |
|
||||
| Comparison result is always the same | Fewer false positive results | The range analysis library is now more conservative about floating point values being possibly `NaN` |
|
||||
| Wrong type of arguments to formatting function (`cpp/wrong-type-format-argument`) | More correct results and fewer false positive results | This query now more accurately identifies wide and non-wide string/character format arguments on different platforms. Platform detection has also been made more accurate for the purposes of this query. |
|
||||
|
||||
## Changes to QL libraries
|
||||
17
change-notes/1.21/analysis-csharp.md
Normal file
17
change-notes/1.21/analysis-csharp.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Improvements to C# analysis
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|------------------------------|------------------------|-----------------------------------|
|
||||
|
||||
|
||||
## Changes to code extraction
|
||||
|
||||
* Named attribute arguments are now extracted.
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* The class `Attribute` has two new predicates: `getConstructorArgument()` and `getNamedArgument()`. The first predicate returns arguments to the underlying constructor call and the latter returns named arguments for initializing fields and properties.
|
||||
|
||||
## Changes to autobuilder
|
||||
@@ -17,8 +17,15 @@
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||
| Expression has no effect | Fewer false-positive results | This rule now treats uses of `Object.defineProperty` more conservatively. |
|
||||
| Useless assignment to property | Fewer false-positive results | This rule now ignore reads of additional getters. |
|
||||
| Arbitrary file write during zip extraction ("Zip Slip") | More results | This rule now considers more libraries, including tar as well as zip. |
|
||||
| Client-side URL redirect | More results and fewer false-positive results | This rule now recognizes additional uses of the document URL. This rule now treats URLs as safe in more cases where the hostname cannot be tampered with. |
|
||||
| Double escaping or unescaping | More results | This rule now considers the flow of regular expressions literals. |
|
||||
| Expression has no effect | Fewer false-positive results | This rule now treats uses of `Object.defineProperty` more conservatively. |
|
||||
| Incomplete string escaping or encoding | More results | This rule now considers the flow of regular expressions literals. |
|
||||
| Replacement of a substring with itself | More results | This rule now considers the flow of regular expressions literals. |
|
||||
| Server-side URL redirect | Fewer false-positive results | This rule now treats URLs as safe in more cases where the hostname cannot be tampered with. |
|
||||
| Useless assignment to property | Fewer false-positive results | This rule now ignore reads of additional getters. |
|
||||
|
||||
## Changes to QL libraries
|
||||
|
||||
* `RegExpLiteral` is now a `DataFlow::SourceNode`.
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
@name Call to memory access function may overflow buffer (CWE-119)
|
||||
+ semmlecode-cpp-queries/Critical/OverflowStatic.ql: /CWE/CWE-119
|
||||
@name Static array access may cause overflow (CWE-119)
|
||||
# + semmlecode-cpp-queries/Critical/OverflowDestination.ql: /CWE/CWE-119
|
||||
# ^ disabled due to timeout issue
|
||||
+ semmlecode-cpp-queries/Critical/OverflowDestination.ql: /CWE/CWE-119
|
||||
@name Copy function using source size (CWE-119)
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Memory Management/SuspiciousCallToStrncat.ql: /CWE/CWE-119
|
||||
@name Potentially unsafe call to strncat (CWE-119)
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql: /CWE/CWE-119
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.SSA
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
|
||||
/**
|
||||
* Holds if `alloc` is a use of `malloc` or `new`. `kind` is
|
||||
@@ -46,7 +47,10 @@ predicate allocExprOrIndirect(Expr alloc, string kind) {
|
||||
alloc.(FunctionCall).getTarget() = rtn.getEnclosingFunction() and
|
||||
(
|
||||
allocExprOrIndirect(rtn.getExpr(), kind) or
|
||||
allocReaches0(rtn.getExpr(), _, kind)
|
||||
exists(Expr e |
|
||||
allocExprOrIndirect(e, kind) and
|
||||
DataFlow::localFlow(DataFlow::exprNode(e), DataFlow::exprNode(rtn.getExpr()))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
* readability
|
||||
*/
|
||||
import cpp
|
||||
private import semmle.code.cpp.commons.Exclusions
|
||||
private import semmle.code.cpp.rangeanalysis.PointlessComparison
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
import UnsignedGEZero
|
||||
@@ -31,6 +32,7 @@ from
|
||||
where
|
||||
not cmp.isInMacroExpansion() and
|
||||
not cmp.isFromTemplateInstantiation(_) and
|
||||
not functionContainsDisabledCode(cmp.getEnclosingFunction()) and
|
||||
reachablePointlessComparison(cmp, left, right, value, ss) and
|
||||
|
||||
// a comparison between an enum and zero is always valid because whether
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
* external/cwe/cwe-561
|
||||
*/
|
||||
import cpp
|
||||
private import semmle.code.cpp.commons.Exclusions
|
||||
|
||||
class PureExprInVoidContext extends ExprInVoidContext {
|
||||
PureExprInVoidContext() { this.isPure() }
|
||||
@@ -23,71 +24,29 @@ predicate accessInInitOfForStmt(Expr e) {
|
||||
s.getExpr() = e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the preprocessor branch `pbd` is on line `pbdStartLine` in file `file`.
|
||||
*/
|
||||
predicate pbdLocation(PreprocessorBranchDirective pbd, string file, int pbdStartLine) {
|
||||
pbd.getLocation().hasLocationInfo(file, pbdStartLine, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the body of the function `f` is on lines `fBlockStartLine` to `fBlockEndLine` in file `file`.
|
||||
*/
|
||||
predicate functionLocation(Function f, string file, int fBlockStartLine, int fBlockEndLine) {
|
||||
f.getBlock().getLocation().hasLocationInfo(file, fBlockStartLine, _, fBlockEndLine, _)
|
||||
}
|
||||
/**
|
||||
* Holds if the function `f`, or a function called by it, contains
|
||||
* code excluded by the preprocessor.
|
||||
*/
|
||||
predicate containsDisabledCode(Function f) {
|
||||
// `f` contains a preprocessor branch that was not taken
|
||||
exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int fBlockStartLine, int fBlockEndLine |
|
||||
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
|
||||
pbdLocation(pbd, file, pbdStartLine) and
|
||||
pbdStartLine <= fBlockEndLine and
|
||||
pbdStartLine >= fBlockStartLine and
|
||||
(
|
||||
pbd.(PreprocessorBranch).wasNotTaken() or
|
||||
|
||||
// an else either was not taken, or it's corresponding branch
|
||||
// was not taken.
|
||||
pbd instanceof PreprocessorElse
|
||||
)
|
||||
) or
|
||||
|
||||
predicate functionContainsDisabledCodeRecursive(Function f) {
|
||||
functionContainsDisabledCode(f) or
|
||||
// recurse into function calls
|
||||
exists(FunctionCall fc |
|
||||
fc.getEnclosingFunction() = f and
|
||||
containsDisabledCode(fc.getTarget())
|
||||
functionContainsDisabledCodeRecursive(fc.getTarget())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Holds if the function `f`, or a function called by it, is inside a
|
||||
* preprocessor branch that may have code in another arm
|
||||
*/
|
||||
predicate definedInIfDef(Function f) {
|
||||
exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int pbdEndLine, int fBlockStartLine, int fBlockEndLine |
|
||||
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
|
||||
pbdLocation(pbd, file, pbdStartLine) and
|
||||
pbdLocation(pbd.getNext(), file, pbdEndLine) and
|
||||
pbdStartLine <= fBlockStartLine and
|
||||
pbdEndLine >= fBlockEndLine and
|
||||
// pbd is a preprocessor branch where multiple branches exist
|
||||
(
|
||||
pbd.getNext() instanceof PreprocessorElse or
|
||||
pbd instanceof PreprocessorElse or
|
||||
pbd.getNext() instanceof PreprocessorElif or
|
||||
pbd instanceof PreprocessorElif
|
||||
)
|
||||
) or
|
||||
|
||||
predicate functionDefinedInIfDefRecursive(Function f) {
|
||||
functionDefinedInIfDef(f) or
|
||||
// recurse into function calls
|
||||
exists(FunctionCall fc |
|
||||
fc.getEnclosingFunction() = f and
|
||||
definedInIfDef(fc.getTarget())
|
||||
functionDefinedInIfDefRecursive(fc.getTarget())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -121,8 +80,8 @@ where // EQExprs are covered by CompareWhereAssignMeant.ql
|
||||
not parent instanceof PureExprInVoidContext and
|
||||
not peivc.getEnclosingFunction().isCompilerGenerated() and
|
||||
not peivc.getType() instanceof UnknownType and
|
||||
not containsDisabledCode(peivc.(FunctionCall).getTarget()) and
|
||||
not definedInIfDef(peivc.(FunctionCall).getTarget()) and
|
||||
not functionContainsDisabledCodeRecursive(peivc.(FunctionCall).getTarget()) and
|
||||
not functionDefinedInIfDefRecursive(peivc.(FunctionCall).getTarget()) and
|
||||
if peivc instanceof FunctionCall then
|
||||
exists(Function target |
|
||||
target = peivc.(FunctionCall).getTarget() and
|
||||
|
||||
42
cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.qhelp
Normal file
42
cpp/ql/src/Likely Bugs/Memory Management/AllocaInLoop.qhelp
Normal file
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p> The <code>alloca</code> macro allocates memory by expanding the current stack frame. Invoking
|
||||
<code>alloca</code> within a loop may lead to a stack overflow because the memory is not released
|
||||
until the function returns.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Consider invoking <code>alloca</code> once outside the loop, or using <code>malloc</code> or
|
||||
<code>new</code> to allocate memory on the heap if the allocation must be done inside the loop.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The variable <code>path</code> is allocated inside a loop with <code>alloca</code>. Consequently,
|
||||
storage for all copies of the path is present in the stack frame until the end of the function.
|
||||
</p>
|
||||
|
||||
<sample src="AllocaInLoopBad.cpp" />
|
||||
|
||||
<p>In the revised example, <code>path</code> is allocated with <code>malloc</code> and freed at the
|
||||
end of the loop.
|
||||
</p>
|
||||
|
||||
<sample src="AllocaInLoopGood.cpp" />
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>Linux Programmer's Manual: <a href="http://man7.org/linux/man-pages/man3/alloca.3.html">ALLOCA(3)</a>.</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,8 @@
|
||||
char *dir_path;
|
||||
char **dir_entries;
|
||||
int count;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
char *path = (char*)alloca(strlen(dir_path) + strlen(dir_entry[i]) + 2);
|
||||
// use path
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
char *dir_path;
|
||||
char **dir_entries;
|
||||
int count;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
char *path = (char*)malloc(strlen(dir_path) + strlen(dir_entry[i]) + 2);
|
||||
// use path
|
||||
free(path);
|
||||
}
|
||||
@@ -56,7 +56,7 @@ where
|
||||
or
|
||||
// The data flow library doesn't support conversions, so here we check that
|
||||
// the address escapes into some expression `pointerToLocal`, which flows
|
||||
// in a one or more steps to a returned expression.
|
||||
// in one or more steps to a returned expression.
|
||||
exists(Expr pointerToLocal |
|
||||
variableAddressEscapesTree(va, pointerToLocal.getFullyConverted()) and
|
||||
not hasNontrivialConversion(pointerToLocal) and
|
||||
|
||||
@@ -14,15 +14,20 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
|
||||
from Expr source, Expr tainted, BinaryArithmeticOperation oper,
|
||||
SizeofOperator sizeof, string taintCause
|
||||
where tainted(source, tainted)
|
||||
and oper.getAnOperand() = tainted
|
||||
and oper.getOperator() = "*"
|
||||
and oper.getAnOperand() = sizeof
|
||||
and oper != tainted
|
||||
and sizeof.getValue().toInt() > 1
|
||||
and isUserInput(source, taintCause)
|
||||
select
|
||||
oper, "This allocation size is derived from $@ and might overflow",
|
||||
source, "user input (" + taintCause + ")"
|
||||
predicate taintedAllocSize(Expr e, Expr source, string taintCause) {
|
||||
(
|
||||
isAllocationExpr(e) or
|
||||
any(MulExpr me | me.getAChild() instanceof SizeofOperator) = e
|
||||
) and
|
||||
exists(Expr tainted |
|
||||
tainted = e.getAChild() and
|
||||
tainted.getType().getUnspecifiedType() instanceof IntegralType and
|
||||
isUserInput(source, taintCause) and
|
||||
tainted(source, tainted)
|
||||
)
|
||||
}
|
||||
|
||||
from Expr e, Expr source, string taintCause
|
||||
where taintedAllocSize(e, source, taintCause)
|
||||
select e, "This allocation size is derived from $@ and might overflow", source,
|
||||
"user input (" + taintCause + ")"
|
||||
|
||||
@@ -16,9 +16,14 @@ abstract class InsecureCryptoSpec extends Locatable {
|
||||
abstract string description();
|
||||
}
|
||||
|
||||
Function getAnInsecureFunction() {
|
||||
result.getName().regexpMatch(algorithmBlacklistRegex()) and
|
||||
exists(result.getACallToThisFunction())
|
||||
}
|
||||
|
||||
class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall {
|
||||
InsecureFunctionCall() {
|
||||
this.getTarget().getName().regexpMatch(algorithmBlacklistRegex())
|
||||
this.getTarget() = getAnInsecureFunction()
|
||||
}
|
||||
|
||||
override string description() { result = "function call" }
|
||||
@@ -27,9 +32,14 @@ class InsecureFunctionCall extends InsecureCryptoSpec, FunctionCall {
|
||||
override Location getLocation() { result = FunctionCall.super.getLocation() }
|
||||
}
|
||||
|
||||
Macro getAnInsecureMacro() {
|
||||
result.getName().regexpMatch(algorithmBlacklistRegex()) and
|
||||
exists(result.getAnInvocation())
|
||||
}
|
||||
|
||||
class InsecureMacroSpec extends InsecureCryptoSpec, MacroInvocation {
|
||||
InsecureMacroSpec() {
|
||||
this.getMacro().getName().regexpMatch(algorithmBlacklistRegex())
|
||||
this.getMacro() = getAnInsecureMacro()
|
||||
}
|
||||
|
||||
override string description() { result = "macro invocation" }
|
||||
|
||||
@@ -68,6 +68,9 @@ some are after the final <code>#endif</code>. All three of these things must be
|
||||
<li>
|
||||
<a href="http://www.cplusplus.com/forum/articles/10627/">Headers and Includes: Why and How</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html">The Multiple-Include Optimization</a>
|
||||
</li>
|
||||
|
||||
|
||||
</references>
|
||||
|
||||
@@ -88,6 +88,7 @@ string extraDetail(HeaderFile hf, SomePreprocessorDirective detail1, SomePreproc
|
||||
* Header file `hf` uses a macro called `macroName`.
|
||||
*/
|
||||
predicate usesMacro(HeaderFile hf, string macroName) {
|
||||
not hf instanceof IncludeGuardedHeader and // restrict to cases the query looks at
|
||||
exists(MacroAccess ma |
|
||||
ma.getFile() = hf and
|
||||
ma.getMacro().getName() = macroName
|
||||
|
||||
@@ -21,7 +21,7 @@ predicate acquireExpr(Expr acquire, string kind) {
|
||||
exists(FunctionCall fc, Function f, string name |
|
||||
fc = acquire and
|
||||
f = fc.getTarget() and
|
||||
name = f.getName() and
|
||||
name = f.getQualifiedName() and
|
||||
(
|
||||
(
|
||||
name = "fopen" and
|
||||
@@ -47,7 +47,7 @@ predicate releaseExpr(Expr release, Expr resource, string kind) {
|
||||
exists(FunctionCall fc, Function f, string name |
|
||||
fc = release and
|
||||
f = fc.getTarget() and
|
||||
name = f.getName() and
|
||||
name = f.getQualifiedName() and
|
||||
(
|
||||
(
|
||||
name = "fclose" and
|
||||
@@ -267,7 +267,7 @@ predicate automaticallyReleased(Assignment acquire)
|
||||
{
|
||||
// sub-types of the Qt type QObject are released by their parent (if they have one)
|
||||
exists(NewExpr alloc |
|
||||
alloc.getType() = qtObject() and
|
||||
alloc.getAllocatedType() = qtObject() and
|
||||
acquire.getRValue() = alloc and
|
||||
alloc.getInitializer() = qtParentConstructor().getACallToThisFunction()
|
||||
)
|
||||
|
||||
@@ -302,7 +302,13 @@ class File extends Container, @file {
|
||||
predicate compiledAsMicrosoft() {
|
||||
exists(Compilation c |
|
||||
c.getAFileCompiled() = this and
|
||||
c.getAnArgument() = "--microsoft"
|
||||
(
|
||||
c.getAnArgument() = "--microsoft" or
|
||||
c.getAnArgument().toLowerCase().replaceAll("\\", "/").matches("%/cl.exe")
|
||||
)
|
||||
) or exists(File parent |
|
||||
parent.compiledAsMicrosoft() and
|
||||
parent.getAnIncludedFile() = this
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,16 @@ predicate allocationFunction(Function f)
|
||||
name = "MmAllocateNodePagesForMdlEx" or
|
||||
name = "MmMapLockedPagesWithReservedMapping" or
|
||||
name = "MmMapLockedPages" or
|
||||
name = "MmMapLockedPagesSpecifyCache"
|
||||
name = "MmMapLockedPagesSpecifyCache" or
|
||||
name = "LocalAlloc" or
|
||||
name = "LocalReAlloc" or
|
||||
name = "GlobalAlloc" or
|
||||
name = "GlobalReAlloc" or
|
||||
name = "HeapAlloc" or
|
||||
name = "HeapReAlloc" or
|
||||
name = "VirtualAlloc" or
|
||||
name = "CoTaskMemAlloc" or
|
||||
name = "CoTaskMemRealloc"
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -81,7 +90,17 @@ predicate freeFunction(Function f, int argNum)
|
||||
(name = "MmFreeMappingAddress" and argNum = 0) or
|
||||
(name = "MmFreePagesFromMdl" and argNum = 0) or
|
||||
(name = "MmUnmapReservedMapping" and argNum = 0) or
|
||||
(name = "MmUnmapLockedPages" and argNum = 0)
|
||||
(name = "MmUnmapLockedPages" and argNum = 0) or
|
||||
(name = "LocalFree" and argNum = 0) or
|
||||
(name = "GlobalFree" and argNum = 0) or
|
||||
(name = "HeapFree" and argNum = 2) or
|
||||
(name = "VirtualFree" and argNum = 0) or
|
||||
(name = "CoTaskMemFree" and argNum = 0) or
|
||||
(name = "SysFreeString" and argNum = 0) or
|
||||
(name = "LocalReAlloc" and argNum = 0) or
|
||||
(name = "GlobalReAlloc" and argNum = 0) or
|
||||
(name = "HeapReAlloc" and argNum = 2) or
|
||||
(name = "CoTaskMemRealloc" and argNum = 0)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
60
cpp/ql/src/semmle/code/cpp/commons/Exclusions.qll
Normal file
60
cpp/ql/src/semmle/code/cpp/commons/Exclusions.qll
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Common predicates used to exclude results from a query based on heuristics.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* Holds if the preprocessor branch `pbd` is on line `pbdStartLine` in file `file`.
|
||||
*/
|
||||
private predicate pbdLocation(PreprocessorBranchDirective pbd, string file, int pbdStartLine) {
|
||||
pbd.getLocation().hasLocationInfo(file, pbdStartLine, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the body of the function `f` is on lines `fBlockStartLine` to `fBlockEndLine` in file `file`.
|
||||
*/
|
||||
private predicate functionLocation(Function f, string file, int fBlockStartLine, int fBlockEndLine) {
|
||||
f.getBlock().getLocation().hasLocationInfo(file, fBlockStartLine, _, fBlockEndLine, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function `f` is inside a preprocessor branch that may have code in another arm.
|
||||
*/
|
||||
predicate functionDefinedInIfDef(Function f) {
|
||||
exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int pbdEndLine, int fBlockStartLine,
|
||||
int fBlockEndLine |
|
||||
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
|
||||
pbdLocation(pbd, file, pbdStartLine) and
|
||||
pbdLocation(pbd.getNext(), file, pbdEndLine) and
|
||||
pbdStartLine <= fBlockStartLine and
|
||||
pbdEndLine >= fBlockEndLine and
|
||||
// pbd is a preprocessor branch where multiple branches exist
|
||||
(
|
||||
pbd.getNext() instanceof PreprocessorElse or
|
||||
pbd instanceof PreprocessorElse or
|
||||
pbd.getNext() instanceof PreprocessorElif or
|
||||
pbd instanceof PreprocessorElif
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the function `f` contains code excluded by the preprocessor.
|
||||
*/
|
||||
predicate functionContainsDisabledCode(Function f) {
|
||||
// `f` contains a preprocessor branch that was not taken
|
||||
exists(PreprocessorBranchDirective pbd, string file, int pbdStartLine, int fBlockStartLine, int fBlockEndLine |
|
||||
functionLocation(f, file, fBlockStartLine, fBlockEndLine) and
|
||||
pbdLocation(pbd, file, pbdStartLine) and
|
||||
pbdStartLine <= fBlockEndLine and
|
||||
pbdStartLine >= fBlockStartLine and
|
||||
(
|
||||
pbd.(PreprocessorBranch).wasNotTaken() or
|
||||
|
||||
// an else either was not taken, or it's corresponding branch
|
||||
// was not taken.
|
||||
pbd instanceof PreprocessorElse
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -32,19 +32,19 @@ class AttributeFormattingFunction extends FormattingFunction {
|
||||
* A standard function such as `vprintf` that has a format parameter
|
||||
* and a variable argument list of type `va_arg`.
|
||||
*/
|
||||
predicate primitiveVariadicFormatter(TopLevelFunction f, int formatParamIndex, boolean wide) {
|
||||
predicate primitiveVariadicFormatter(TopLevelFunction f, int formatParamIndex) {
|
||||
f.getName().regexpMatch("_?_?va?[fs]?n?w?printf(_s)?(_p)?(_l)?")
|
||||
and (
|
||||
if f.getName().matches("%\\_l")
|
||||
then formatParamIndex = f.getNumberOfParameters() - 3
|
||||
else formatParamIndex = f.getNumberOfParameters() - 2
|
||||
) and if f.getName().matches("%w%") then wide = true else wide = false
|
||||
)
|
||||
}
|
||||
|
||||
private
|
||||
predicate callsVariadicFormatter(Function f, int formatParamIndex, boolean wide) {
|
||||
predicate callsVariadicFormatter(Function f, int formatParamIndex) {
|
||||
exists(FunctionCall fc, int i |
|
||||
variadicFormatter(fc.getTarget(), i, wide)
|
||||
variadicFormatter(fc.getTarget(), i)
|
||||
and fc.getEnclosingFunction() = f
|
||||
and fc.getArgument(i) = f.getParameter(formatParamIndex).getAnAccess()
|
||||
)
|
||||
@@ -54,11 +54,11 @@ predicate callsVariadicFormatter(Function f, int formatParamIndex, boolean wide)
|
||||
* Holds if `f` is a function such as `vprintf` that has a format parameter
|
||||
* (at `formatParamIndex`) and a variable argument list of type `va_arg`.
|
||||
*/
|
||||
predicate variadicFormatter(Function f, int formatParamIndex, boolean wide) {
|
||||
primitiveVariadicFormatter(f, formatParamIndex, wide)
|
||||
predicate variadicFormatter(Function f, int formatParamIndex) {
|
||||
primitiveVariadicFormatter(f, formatParamIndex)
|
||||
or (
|
||||
not f.isVarargs()
|
||||
and callsVariadicFormatter(f, formatParamIndex, wide)
|
||||
and callsVariadicFormatter(f, formatParamIndex)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -68,12 +68,10 @@ predicate variadicFormatter(Function f, int formatParamIndex, boolean wide) {
|
||||
*/
|
||||
class UserDefinedFormattingFunction extends FormattingFunction {
|
||||
UserDefinedFormattingFunction() {
|
||||
isVarargs() and callsVariadicFormatter(this, _, _)
|
||||
isVarargs() and callsVariadicFormatter(this, _)
|
||||
}
|
||||
|
||||
override int getFormatParameterIndex() { callsVariadicFormatter(this, result, _) }
|
||||
|
||||
override predicate isWideCharDefault() { callsVariadicFormatter(this, _, true) }
|
||||
override int getFormatParameterIndex() { callsVariadicFormatter(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,11 +254,11 @@ class FormatLiteral extends Literal {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the format string, with '%%' replaced by '_' (to avoid processing
|
||||
* '%%' as a format specifier).
|
||||
* Gets the format string, with '%%' and '%@' replaced by '_' (to avoid processing
|
||||
* them as format specifiers).
|
||||
*/
|
||||
string getFormat() {
|
||||
result = this.getValue().replaceAll("%%", "_")
|
||||
result = this.getValue().replaceAll("%%", "_").replaceAll("%@", "_")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -674,8 +672,8 @@ class FormatLiteral extends Literal {
|
||||
/**
|
||||
* Gets the char type required by the nth conversion specifier.
|
||||
* - in the base case this is the default for the formatting function
|
||||
* (e.g. `char` for `printf`, `wchar_t` for `wprintf`).
|
||||
* - the `%S` format character reverses wideness.
|
||||
* (e.g. `char` for `printf`, `char` or `wchar_t` for `wprintf`).
|
||||
* - the `%C` format character reverses wideness.
|
||||
* - the size prefixes 'l'/'w' and 'h' override the type character
|
||||
* to wide or single-byte characters respectively.
|
||||
*/
|
||||
@@ -721,8 +719,8 @@ class FormatLiteral extends Literal {
|
||||
/**
|
||||
* Gets the string type required by the nth conversion specifier.
|
||||
* - in the base case this is the default for the formatting function
|
||||
* (e.g. `char` for `printf`, `wchar_t` for `wprintf`).
|
||||
* - the `%S` format character reverses wideness.
|
||||
* (e.g. `char *` for `printf`, `char *` or `wchar_t *` for `wprintf`).
|
||||
* - the `%S` format character reverses wideness on some platforms.
|
||||
* - the size prefixes 'l'/'w' and 'h' override the type character
|
||||
* to wide or single-byte characters respectively.
|
||||
*/
|
||||
|
||||
@@ -57,6 +57,14 @@ class LambdaExpression extends Expr, @lambdaexpr {
|
||||
Operator getLambdaFunction() {
|
||||
result = getType().(Closure).getLambdaFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the initializer that initializes the captured variables in the closure, if any.
|
||||
* A lambda that does not capture any variables will not have an initializer.
|
||||
*/
|
||||
Expr getInitializer() {
|
||||
result = getChild(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,6 @@ private import semmle.code.cpp.ir.internal.TempVariableTag
|
||||
|
||||
class TempVariableTag extends TTempVariableTag {
|
||||
string toString() {
|
||||
result = "Tag"
|
||||
result = getTempVariableTagId(this)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ class InstructionTag extends TInstructionTag {
|
||||
string getInstructionTagId(TInstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and result = "Only" or // Single instruction (not including implicit Load)
|
||||
tag = InitializerVariableAddressTag() and result = "InitVarAddr" or
|
||||
tag = InitializerLoadStringTag() and result = "InitLoadStr" or
|
||||
tag = InitializerStoreTag() and result = "InitStore" or
|
||||
tag = InitializerUninitializedTag() and result = "InitUninit" or
|
||||
tag = ZeroPadStringConstantTag() and result = "ZeroPadConst" or
|
||||
|
||||
@@ -268,6 +268,9 @@ newtype TTranslatedElement =
|
||||
) or
|
||||
exists(ThrowExpr throw |
|
||||
throw.getExpr().getFullyConverted() = expr
|
||||
) or
|
||||
exists(LambdaExpression lambda |
|
||||
lambda.getInitializer().getFullyConverted() = expr
|
||||
)
|
||||
)
|
||||
} or
|
||||
|
||||
@@ -2608,3 +2608,122 @@ class TranslatedConditionDeclExpr extends TranslatedNonConstantExpr {
|
||||
result = getTranslatedExpr(expr.getVariableAccess().getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a lambda expression. This initializes a temporary variable whose type is that of the lambda,
|
||||
* using the initializer list that represents the captures of the lambda.
|
||||
*/
|
||||
class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationContext {
|
||||
override LambdaExpression expr;
|
||||
|
||||
override final Instruction getFirstInstruction() {
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override final TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getInitialization()
|
||||
}
|
||||
|
||||
override Instruction getResult() {
|
||||
result = getInstruction(LoadTag())
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
(
|
||||
tag = InitializerVariableAddressTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getInstruction(InitializerStoreTag())
|
||||
) or
|
||||
(
|
||||
tag = InitializerStoreTag() and
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
result = getInitialization().getFirstInstruction() or
|
||||
not hasInitializer() and result = getInstruction(LoadTag())
|
||||
)
|
||||
) or
|
||||
(
|
||||
tag = LoadTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getInitialization() and
|
||||
result = getInstruction(LoadTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType,
|
||||
boolean isGLValue) {
|
||||
(
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getResultType() and
|
||||
isGLValue = true
|
||||
) or
|
||||
(
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Uninitialized and
|
||||
resultType = getResultType() and
|
||||
isGLValue = false
|
||||
) or
|
||||
(
|
||||
tag = LoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getResultType() and
|
||||
isGLValue = false
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
(
|
||||
tag = InitializerStoreTag() and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
) or
|
||||
(
|
||||
tag = LoadTag() and
|
||||
(
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
) or
|
||||
(
|
||||
operandTag instanceof LoadOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
(
|
||||
tag = InitializerVariableAddressTag() or
|
||||
tag = InitializerStoreTag()
|
||||
) and
|
||||
result = getTempVariable(LambdaTempVar())
|
||||
}
|
||||
|
||||
override predicate hasTempVariable(TempVariableTag tag, Type type) {
|
||||
tag = LambdaTempVar() and
|
||||
type = getResultType()
|
||||
}
|
||||
|
||||
override final Instruction getTargetAddress() {
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
override final Type getTargetType() {
|
||||
result = getResultType()
|
||||
}
|
||||
|
||||
private predicate hasInitializer() {
|
||||
exists(getInitialization())
|
||||
}
|
||||
|
||||
private TranslatedInitialization getInitialization() {
|
||||
result = getTranslatedInitialization(expr.getChild(0).getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,7 @@ abstract class InitializationContext extends TranslatedElement {
|
||||
* Represents the IR translation of any initialization, whether from an
|
||||
* initializer list or from a direct initializer.
|
||||
*/
|
||||
abstract class TranslatedInitialization extends TranslatedElement,
|
||||
TTranslatedInitialization {
|
||||
abstract class TranslatedInitialization extends TranslatedElement, TTranslatedInitialization {
|
||||
Expr expr;
|
||||
|
||||
TranslatedInitialization() {
|
||||
@@ -78,10 +77,10 @@ abstract class TranslatedInitialization extends TranslatedElement,
|
||||
/**
|
||||
* Represents the IR translation of an initialization from an initializer list.
|
||||
*/
|
||||
abstract class TranslatedListInitialization extends TranslatedInitialization,
|
||||
InitializationContext {
|
||||
abstract class TranslatedListInitialization extends TranslatedInitialization, InitializationContext {
|
||||
override Instruction getFirstInstruction() {
|
||||
result = getChild(0).getFirstInstruction()
|
||||
result = getChild(0).getFirstInstruction() or
|
||||
not exists(getChild(0)) and result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
@@ -117,8 +116,7 @@ abstract class TranslatedListInitialization extends TranslatedInitialization,
|
||||
* Represents the IR translation of an initialization of a class object from an
|
||||
* initializer list.
|
||||
*/
|
||||
class TranslatedClassListInitialization extends
|
||||
TranslatedListInitialization
|
||||
class TranslatedClassListInitialization extends TranslatedListInitialization
|
||||
{
|
||||
override ClassAggregateLiteral expr;
|
||||
|
||||
@@ -135,8 +133,7 @@ class TranslatedClassListInitialization extends
|
||||
* Represents the IR translation of an initialization of an array from an
|
||||
* initializer list.
|
||||
*/
|
||||
class TranslatedArrayListInitialization extends
|
||||
TranslatedListInitialization {
|
||||
class TranslatedArrayListInitialization extends TranslatedListInitialization {
|
||||
override ArrayAggregateLiteral expr;
|
||||
|
||||
override TranslatedElement getChild(int id) {
|
||||
@@ -175,23 +172,20 @@ abstract class TranslatedDirectInitialization extends TranslatedInitialization {
|
||||
* expression, where the initialization is performed via bitwise copy (as
|
||||
* opposed to a constructor).
|
||||
*/
|
||||
class TranslatedSimpleDirectInitialization extends
|
||||
TranslatedDirectInitialization {
|
||||
class TranslatedSimpleDirectInitialization extends TranslatedDirectInitialization {
|
||||
TranslatedSimpleDirectInitialization() {
|
||||
not expr instanceof ConstructorCall and
|
||||
not expr instanceof StringLiteral
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getContext().getTargetType() and
|
||||
isGLValue = false
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = InitializerStoreTag() and
|
||||
result = getParent().getChildSuccessor(this) and
|
||||
kind instanceof GotoEdge
|
||||
@@ -201,8 +195,7 @@ class TranslatedSimpleDirectInitialization extends
|
||||
child = getInitializer() and result = getInstruction(InitializerStoreTag())
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = InitializerStoreTag() and
|
||||
(
|
||||
(
|
||||
@@ -221,12 +214,10 @@ class TranslatedSimpleDirectInitialization extends
|
||||
* Represents the IR translation of an initialization of an array from a string
|
||||
* literal.
|
||||
*/
|
||||
class TranslatedStringLiteralInitialization extends
|
||||
TranslatedDirectInitialization {
|
||||
class TranslatedStringLiteralInitialization extends TranslatedDirectInitialization {
|
||||
override StringLiteral expr;
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
(
|
||||
// Load the string literal to make it a prvalue of type `char[len]`
|
||||
tag = InitializerLoadStringTag() and
|
||||
@@ -280,8 +271,7 @@ class TranslatedStringLiteralInitialization extends
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
(
|
||||
@@ -321,8 +311,7 @@ class TranslatedStringLiteralInitialization extends
|
||||
child = getInitializer() and result = getInstruction(InitializerLoadStringTag())
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
(
|
||||
tag = InitializerLoadStringTag() and
|
||||
(
|
||||
@@ -422,17 +411,14 @@ class TranslatedStringLiteralInitialization extends
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedConstructorInitialization extends
|
||||
TranslatedDirectInitialization, StructorCallContext {
|
||||
class TranslatedConstructorInitialization extends TranslatedDirectInitialization, StructorCallContext {
|
||||
override ConstructorCall expr;
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -440,8 +426,7 @@ class TranslatedConstructorInitialization extends
|
||||
child = getInitializer() and result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -454,13 +439,11 @@ class TranslatedConstructorInitialization extends
|
||||
* Gets the `TranslatedFieldInitialization` for field `field` within initializer
|
||||
* list `initList`.
|
||||
*/
|
||||
TranslatedFieldInitialization getTranslatedFieldInitialization(
|
||||
ClassAggregateLiteral initList, Field field) {
|
||||
TranslatedFieldInitialization getTranslatedFieldInitialization(ClassAggregateLiteral initList, Field field) {
|
||||
result.getAST() = initList and result.getField() = field
|
||||
}
|
||||
|
||||
TranslatedFieldInitialization getTranslatedConstructorFieldInitialization(
|
||||
ConstructorFieldInit init) {
|
||||
TranslatedFieldInitialization getTranslatedConstructorFieldInitialization(ConstructorFieldInit init) {
|
||||
result.getAST() = init
|
||||
}
|
||||
|
||||
@@ -496,16 +479,14 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
|
||||
result = field.getInitializationOrder()
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
tag = getFieldAddressTag() and
|
||||
opcode instanceof Opcode::FieldAddress and
|
||||
resultType = field.getType().getUnspecifiedType() and
|
||||
isGLValue = true
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = getFieldAddressTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getParent().(InitializationContext).getTargetAddress()
|
||||
@@ -528,9 +509,8 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
|
||||
* Represents the IR translation of the initialization of a field from an
|
||||
* explicit element in an initializer list.
|
||||
*/
|
||||
class TranslatedExplicitFieldInitialization extends
|
||||
TranslatedFieldInitialization, InitializationContext,
|
||||
TTranslatedExplicitFieldInitialization {
|
||||
class TranslatedExplicitFieldInitialization extends TranslatedFieldInitialization, InitializationContext,
|
||||
TTranslatedExplicitFieldInitialization {
|
||||
Expr expr;
|
||||
|
||||
TranslatedExplicitFieldInitialization() {
|
||||
@@ -545,8 +525,7 @@ class TranslatedExplicitFieldInitialization extends
|
||||
result = field.getType().getUnspecifiedType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = getFieldAddressTag() and
|
||||
result = getInitialization().getFirstInstruction() and
|
||||
kind instanceof GotoEdge
|
||||
@@ -576,14 +555,12 @@ private string getZeroValue(Type type) {
|
||||
* Represents the IR translation of the initialization of a field without a
|
||||
* corresponding element in the initializer list.
|
||||
*/
|
||||
class TranslatedFieldValueInitialization extends
|
||||
TranslatedFieldInitialization, TTranslatedFieldValueInitialization {
|
||||
class TranslatedFieldValueInitialization extends TranslatedFieldInitialization, TTranslatedFieldValueInitialization {
|
||||
TranslatedFieldValueInitialization() {
|
||||
this = TTranslatedFieldValueInitialization(ast, field)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType, isGLValue) or
|
||||
(
|
||||
tag = getFieldDefaultValueTag() and
|
||||
@@ -599,8 +576,7 @@ class TranslatedFieldValueInitialization extends
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
(
|
||||
@@ -623,8 +599,7 @@ class TranslatedFieldValueInitialization extends
|
||||
result = getZeroValue(field.getType().getUnspecifiedType())
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
result = TranslatedFieldInitialization.super.getInstructionOperand(tag, operandTag) or
|
||||
(
|
||||
tag = getFieldDefaultValueStoreTag() and
|
||||
@@ -681,8 +656,7 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
result = getInstruction(getElementIndexTag())
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
(
|
||||
tag = getElementIndexTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
@@ -697,15 +671,13 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = getElementIndexTag() and
|
||||
result = getInstruction(getElementAddressTag()) and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = getElementAddressTag() and
|
||||
(
|
||||
(
|
||||
@@ -764,8 +736,7 @@ class TranslatedExplicitElementInitialization extends TranslatedElementInitializ
|
||||
result = getElementType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind) or
|
||||
(
|
||||
tag = getElementAddressTag() and
|
||||
@@ -806,8 +777,7 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
|
||||
elementCount)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag,
|
||||
Type resultType, boolean isGLValue) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
TranslatedElementInitialization.super.hasInstruction(opcode, tag, resultType, isGLValue) or
|
||||
(
|
||||
tag = getElementDefaultValueTag() and
|
||||
@@ -823,8 +793,7 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag,
|
||||
EdgeKind kind) {
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
result = TranslatedElementInitialization.super.getInstructionSuccessor(tag, kind) or
|
||||
(
|
||||
kind instanceof GotoEdge and
|
||||
@@ -862,8 +831,7 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati
|
||||
result = elementCount * getElementType().getSize()
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
result = TranslatedElementInitialization.super.getInstructionOperand(tag, operandTag) or
|
||||
(
|
||||
tag = getElementDefaultValueStoreTag() and
|
||||
@@ -943,8 +911,7 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
|
||||
result = getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType,
|
||||
boolean isGLValue) {
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::ConvertToBase and
|
||||
resultType = call.getTarget().getDeclaringType().getUnspecifiedType() and
|
||||
@@ -961,15 +928,13 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
|
||||
result = getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override final Instruction getInstructionOperand(InstructionTag tag,
|
||||
OperandTag operandTag) {
|
||||
override final Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getTranslatedFunction(getFunction()).getInitializeThisInstruction()
|
||||
}
|
||||
|
||||
override final predicate getInstructionInheritance(InstructionTag tag,
|
||||
Class baseClass, Class derivedClass) {
|
||||
override final predicate getInstructionInheritance(InstructionTag tag, Class baseClass, Class derivedClass) {
|
||||
tag = OnlyInstructionTag() and
|
||||
baseClass = call.getTarget().getDeclaringType().getUnspecifiedType() and
|
||||
derivedClass = getFunction().getDeclaringType().getUnspecifiedType()
|
||||
@@ -980,7 +945,7 @@ abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStru
|
||||
* Represents a call to a delegating or base class constructor from within a constructor.
|
||||
*/
|
||||
abstract class TranslatedConstructorCallFromConstructor extends TranslatedStructorCallFromStructor,
|
||||
TTranslatedConstructorBaseInit {
|
||||
TTranslatedConstructorBaseInit {
|
||||
TranslatedConstructorCallFromConstructor() {
|
||||
this = TTranslatedConstructorBaseInit(call)
|
||||
}
|
||||
@@ -1004,8 +969,7 @@ class TranslatedConstructorDelegationInit extends TranslatedConstructorCallFromC
|
||||
result = getStructorCall().getFirstInstruction()
|
||||
}
|
||||
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType,
|
||||
boolean isGLValue) {
|
||||
override final predicate hasInstruction(Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue) {
|
||||
none()
|
||||
}
|
||||
|
||||
@@ -1022,8 +986,7 @@ class TranslatedConstructorDelegationInit extends TranslatedConstructorCallFromC
|
||||
* Represents the IR translation of a call to a base class constructor from within a
|
||||
* derived class constructor
|
||||
*/
|
||||
class TranslatedConstructorBaseInit extends TranslatedConstructorCallFromConstructor,
|
||||
TranslatedBaseStructorCall {
|
||||
class TranslatedConstructorBaseInit extends TranslatedConstructorCallFromConstructor, TranslatedBaseStructorCall {
|
||||
TranslatedConstructorBaseInit() {
|
||||
not call instanceof ConstructorDelegationInit
|
||||
}
|
||||
@@ -1041,8 +1004,7 @@ TranslatedDestructorBaseDestruction getTranslatedDestructorBaseDestruction(Destr
|
||||
* Represents the IR translation of a call to a base class destructor from within a
|
||||
* derived class destructor.
|
||||
*/
|
||||
class TranslatedDestructorBaseDestruction extends TranslatedBaseStructorCall,
|
||||
TTranslatedDestructorBaseDestruction {
|
||||
class TranslatedDestructorBaseDestruction extends TranslatedBaseStructorCall, TTranslatedDestructorBaseDestruction {
|
||||
TranslatedDestructorBaseDestruction() {
|
||||
this = TTranslatedDestructorBaseDestruction(call)
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ import cpp
|
||||
newtype TTempVariableTag =
|
||||
ConditionValueTempVar() or
|
||||
ReturnValueTempVar() or
|
||||
ThrowTempVar()
|
||||
ThrowTempVar() or
|
||||
LambdaTempVar()
|
||||
|
||||
string getTempVariableTagId(TTempVariableTag tag) {
|
||||
tag = ConditionValueTempVar() and result = "CondVal" or
|
||||
tag = ReturnValueTempVar() and result = "Ret" or
|
||||
tag = ThrowTempVar() and result = "Throw"
|
||||
tag = ThrowTempVar() and result = "Throw" or
|
||||
tag = LambdaTempVar() and result = "Lambda"
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ private Type stripTopLevelSpecifiersOnly(Type t) {
|
||||
*/
|
||||
Type getAFormatterWideType() {
|
||||
exists(FormattingFunction ff |
|
||||
result = stripTopLevelSpecifiersOnly(ff.getDefaultCharType()) and
|
||||
result = stripTopLevelSpecifiersOnly(ff.getFormatCharType()) and
|
||||
result.getSize() != 1
|
||||
)
|
||||
}
|
||||
@@ -46,6 +46,14 @@ abstract class FormattingFunction extends Function {
|
||||
/** Gets the position at which the format parameter occurs. */
|
||||
abstract int getFormatParameterIndex();
|
||||
|
||||
/**
|
||||
* Holds if this `FormattingFunction` is in a context that supports
|
||||
* Microsoft rules and extensions.
|
||||
*/
|
||||
predicate isMicrosoft() {
|
||||
getFile().compiledAsMicrosoft()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the default meaning of `%s` is a `wchar_t *`, rather than
|
||||
* a `char *` (either way, `%S` will have the opposite meaning).
|
||||
@@ -55,11 +63,10 @@ abstract class FormattingFunction extends Function {
|
||||
deprecated predicate isWideCharDefault() { none() }
|
||||
|
||||
/**
|
||||
* Gets the default character type expected for `%s` by this function. Typically
|
||||
* `char` or `wchar_t`.
|
||||
* Gets the character type used in the format string for this function.
|
||||
*/
|
||||
Type getDefaultCharType() {
|
||||
result =
|
||||
Type getFormatCharType() {
|
||||
result =
|
||||
stripTopLevelSpecifiersOnly(
|
||||
stripTopLevelSpecifiersOnly(
|
||||
getParameter(getFormatParameterIndex()).getType().getUnderlyingType()
|
||||
@@ -67,19 +74,33 @@ abstract class FormattingFunction extends Function {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default character type expected for `%s` by this function. Typically
|
||||
* `char` or `wchar_t`.
|
||||
*/
|
||||
Type getDefaultCharType() {
|
||||
(
|
||||
isMicrosoft() and
|
||||
result = getFormatCharType()
|
||||
) or (
|
||||
not isMicrosoft() and
|
||||
result instanceof PlainCharType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the non-default character type expected for `%S` by this function. Typically
|
||||
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
|
||||
* which is correct for a particular function.
|
||||
*/
|
||||
Type getNonDefaultCharType() {
|
||||
(
|
||||
getDefaultCharType().getSize() = 1 and
|
||||
result = getAFormatterWideTypeOrDefault()
|
||||
) or (
|
||||
getDefaultCharType().getSize() > 1 and
|
||||
result instanceof PlainCharType
|
||||
)
|
||||
(
|
||||
getDefaultCharType().getSize() = 1 and
|
||||
result = getWideCharType()
|
||||
) or (
|
||||
not getDefaultCharType().getSize() = 1 and
|
||||
result instanceof PlainCharType
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,10 +110,12 @@ abstract class FormattingFunction extends Function {
|
||||
*/
|
||||
Type getWideCharType() {
|
||||
(
|
||||
result = getDefaultCharType() or
|
||||
result = getNonDefaultCharType()
|
||||
) and
|
||||
result.getSize() > 1
|
||||
result = getFormatCharType() and
|
||||
result.getSize() > 1
|
||||
) or (
|
||||
not getFormatCharType().getSize() > 1 and
|
||||
result = getAFormatterWideTypeOrDefault() // may have more than one result
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
41
cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll
Normal file
41
cpp/ql/src/semmle/code/cpp/rangeanalysis/NanAnalysis.qll
Normal file
@@ -0,0 +1,41 @@
|
||||
import cpp
|
||||
private import semmle.code.cpp.rangeanalysis.RangeSSA
|
||||
|
||||
/**
|
||||
* Holds if `guard` won't return the value `polarity` when either
|
||||
* operand is NaN.
|
||||
*/
|
||||
predicate nanExcludingComparison(ComparisonOperation guard, boolean polarity) {
|
||||
polarity = true and
|
||||
(
|
||||
guard instanceof LTExpr or
|
||||
guard instanceof LEExpr or
|
||||
guard instanceof GTExpr or
|
||||
guard instanceof GEExpr or
|
||||
guard instanceof EQExpr
|
||||
)
|
||||
or
|
||||
polarity = false and
|
||||
guard instanceof NEExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is a use of an SSA definition in `def` which cannot be NaN,
|
||||
* by virtue of the guard in `def`.
|
||||
*/
|
||||
private predicate excludesNan(RangeSsaDefinition def, VariableAccess v) {
|
||||
exists(VariableAccess inCond, ComparisonOperation guard, boolean branch, LocalScopeVariable lsv |
|
||||
def.isGuardPhi(inCond, guard, branch) and
|
||||
inCond.getTarget() = lsv and
|
||||
v = def.getAUse(lsv) and
|
||||
guard.getAnOperand() = inCond and
|
||||
nanExcludingComparison(guard, branch)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable access which cannot be NaN.
|
||||
*/
|
||||
class NonNanVariableAccess extends VariableAccess {
|
||||
NonNanVariableAccess() { excludesNan(_, this) }
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import cpp
|
||||
private import RangeAnalysisUtils
|
||||
import RangeSSA
|
||||
import SimpleRangeAnalysisCached
|
||||
private import NanAnalysis
|
||||
|
||||
/**
|
||||
* This fixed set of lower bounds is used when the lower bounds of an
|
||||
@@ -993,6 +994,25 @@ predicate unanalyzableDefBounds(
|
||||
ub = varMaxVal(v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if in the `branch` branch of a guard `guard` involving `v`,
|
||||
* we know that `v` is not NaN, and therefore it is safe to make range
|
||||
* inferences about `v`.
|
||||
*/
|
||||
bindingset[guard, v, branch]
|
||||
predicate nonNanGuardedVariable(ComparisonOperation guard, VariableAccess v, boolean branch) {
|
||||
v.getType().getUnspecifiedType() instanceof IntegralType
|
||||
or
|
||||
v.getType().getUnspecifiedType() instanceof FloatingPointType and v instanceof NonNanVariableAccess
|
||||
or
|
||||
// The reason the following case is here is to ensure that when we say
|
||||
// `if (x > 5) { ...then... } else { ...else... }`
|
||||
// it is ok to conclude that `x > 5` in the `then`, (though not safe
|
||||
// to conclude that x <= 5 in `else`) even if we had no prior
|
||||
// knowledge of `x` not being `NaN`.
|
||||
nanExcludingComparison(guard, branch)
|
||||
}
|
||||
|
||||
/**
|
||||
* If the guard is a comparison of the form `p*v + q <CMP> r`, then this
|
||||
* predicate uses the bounds information for `r` to compute a lower bound
|
||||
@@ -1004,10 +1024,12 @@ predicate lowerBoundFromGuard(
|
||||
) {
|
||||
exists (float childLB, RelationStrictness strictness
|
||||
| boundFromGuard(guard, v, childLB, true, strictness, branch)
|
||||
| if (strictness = Nonstrict() or
|
||||
not (v.getType().getUnspecifiedType() instanceof IntegralType))
|
||||
then lb = childLB
|
||||
else lb = childLB+1)
|
||||
| if nonNanGuardedVariable(guard, v, branch)
|
||||
then (if (strictness = Nonstrict() or
|
||||
not (v.getType().getUnspecifiedType() instanceof IntegralType))
|
||||
then lb = childLB
|
||||
else lb = childLB+1)
|
||||
else lb = varMinVal(v.getTarget()))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1021,10 +1043,12 @@ predicate upperBoundFromGuard(
|
||||
) {
|
||||
exists (float childUB, RelationStrictness strictness
|
||||
| boundFromGuard(guard, v, childUB, false, strictness, branch)
|
||||
| if (strictness = Nonstrict() or
|
||||
not (v.getType().getUnspecifiedType() instanceof IntegralType))
|
||||
then ub = childUB
|
||||
else ub = childUB-1)
|
||||
| if nonNanGuardedVariable(guard, v, branch)
|
||||
then (if (strictness = Nonstrict() or
|
||||
not (v.getType().getUnspecifiedType() instanceof IntegralType))
|
||||
then ub = childUB
|
||||
else ub = childUB-1)
|
||||
else ub = varMaxVal(v.getTarget()))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,14 +20,17 @@ string hashAlgorithmBlacklist() {
|
||||
|
||||
/** A regex for matching strings that look like they contain a blacklisted algorithm */
|
||||
string algorithmBlacklistRegex() {
|
||||
// algorithms usually appear in names surrounded by characters that are not
|
||||
// alphabetical characters in the same case. This handles the upper and lower
|
||||
// case cases
|
||||
result = "(^|.*[^A-Z])" + algorithmBlacklist() + "([^A-Z].*|$)"
|
||||
// for lowercase, we want to be careful to avoid being confused by camelCase
|
||||
// hence we require two preceding uppercase letters to be sure of a case switch,
|
||||
// or a preceding non-alphabetic character
|
||||
or result = "(^|.*[A-Z]{2}|.*[^a-zA-Z])" + algorithmBlacklist().toLowerCase() + "([^a-z].*|$)"
|
||||
result =
|
||||
// algorithms usually appear in names surrounded by characters that are not
|
||||
// alphabetical characters in the same case. This handles the upper and lower
|
||||
// case cases
|
||||
"(^|.*[^A-Z])(" + strictconcat(algorithmBlacklist(), "|") + ")([^A-Z].*|$)" +
|
||||
"|" +
|
||||
// for lowercase, we want to be careful to avoid being confused by camelCase
|
||||
// hence we require two preceding uppercase letters to be sure of a case switch,
|
||||
// or a preceding non-alphabetic character
|
||||
"(^|.*[A-Z]{2}|.*[^a-zA-Z])(" + strictconcat(algorithmBlacklist().toLowerCase(), "|") +
|
||||
")([^a-z].*|$)"
|
||||
}
|
||||
|
||||
/** A whitelist of algorithms that are known to be secure */
|
||||
|
||||
@@ -4,7 +4,7 @@ import external.ExternalArtifact
|
||||
predicate printfLikeFunction(Function func, int formatArg) {
|
||||
(formatArg = func.(FormattingFunction).getFormatParameterIndex() and not func instanceof UserDefinedFormattingFunction)
|
||||
or
|
||||
primitiveVariadicFormatter(func, formatArg, _)
|
||||
primitiveVariadicFormatter(func, formatArg)
|
||||
or
|
||||
exists(ExternalData data |
|
||||
// TODO Do this \ to / conversion in the toolchain?
|
||||
|
||||
@@ -245,9 +245,14 @@ predicate insideFunctionValueMoveTo(Element src, Element dest)
|
||||
and format.getConversionChar(arg - formattingSend.getTarget().getNumberOfParameters()) = argFormat
|
||||
and (argFormat = "s" or argFormat = "S" or argFormat = "@"))
|
||||
// Expressions computed from tainted data are also tainted
|
||||
or (exists (FunctionCall call | dest = call and isPureFunction(call.getTarget().getName()) |
|
||||
call.getAnArgument() = src
|
||||
and forall(Expr arg | arg = call.getAnArgument() | arg = src or predictable(arg))))
|
||||
or exists(FunctionCall call | dest = call and isPureFunction(call.getTarget().getName()) |
|
||||
call.getAnArgument() = src and
|
||||
forall(Expr arg | arg = call.getAnArgument() | arg = src or predictable(arg)) and
|
||||
|
||||
// flow through `strlen` tends to cause dubious results, if the length is
|
||||
// bounded.
|
||||
not call.getTarget().getName() = "strlen"
|
||||
)
|
||||
or exists(Element a, Element b |
|
||||
moveToDependingOnSide(a, b) and
|
||||
if insideValueSource(a) then
|
||||
|
||||
@@ -6627,3 +6627,783 @@ ir.cpp:
|
||||
# 988| Type = double
|
||||
# 988| Value = 1.0
|
||||
# 988| ValueCategory = prvalue
|
||||
# 1009| EmptyStruct& EmptyStruct::operator=(EmptyStruct const&)
|
||||
# 1009| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const EmptyStruct &
|
||||
# 1009| EmptyStruct& EmptyStruct::operator=(EmptyStruct&&)
|
||||
# 1009| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = EmptyStruct &&
|
||||
# 1011| void EmptyStructInit()
|
||||
# 1011| params:
|
||||
# 1011| body: { ... }
|
||||
# 1012| 0: declaration
|
||||
# 1012| 0: definition of s
|
||||
# 1012| Type = EmptyStruct
|
||||
# 1012| init: initializer for s
|
||||
# 1012| expr: {...}
|
||||
# 1012| Type = EmptyStruct
|
||||
# 1012| ValueCategory = prvalue
|
||||
# 1013| 1: return ...
|
||||
# 1015| (lambda [] type at line 1015, col. 12)& (lambda [] type at line 1015, col. 12)::operator=((lambda [] type at line 1015, col. 12) const&)
|
||||
# 1015| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1015, col. 12 &
|
||||
# 1015| void (lambda [] type at line 1015, col. 12)::(constructor)((lambda [] type at line 1015, col. 12) const&)
|
||||
# 1015| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1015, col. 12 &
|
||||
# 1015| void (lambda [] type at line 1015, col. 12)::(constructor)((lambda [] type at line 1015, col. 12)&&)
|
||||
# 1015| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = lambda [] type at line 1015, col. 12 &&
|
||||
# 1015| initializations:
|
||||
# 1015| body: { ... }
|
||||
# 1015| 0: return ...
|
||||
# 1015| void (lambda [] type at line 1015, col. 12)::(constructor)()
|
||||
# 1015| params:
|
||||
# 1015| void (lambda [] type at line 1015, col. 12)::_FUN()
|
||||
# 1015| params:
|
||||
# 1015| void (lambda [] type at line 1015, col. 12)::operator()() const
|
||||
# 1015| params:
|
||||
# 1015| body: { ... }
|
||||
# 1015| 0: return ...
|
||||
# 1015| void(* (lambda [] type at line 1015, col. 12)::operator void (*)()() const)()
|
||||
# 1015| params:
|
||||
#-----| body: { ... }
|
||||
# 1015| 0: return ...
|
||||
# 1015| 0: _FUN
|
||||
# 1015| Type = ..(*)(..)
|
||||
# 1015| ValueCategory = prvalue(load)
|
||||
# 1017| void Lambda(int, String const&)
|
||||
# 1017| params:
|
||||
# 1017| 0: x
|
||||
# 1017| Type = int
|
||||
# 1017| 1: s
|
||||
# 1017| Type = const String &
|
||||
# 1017| body: { ... }
|
||||
# 1018| 0: declaration
|
||||
# 1018| 0: definition of lambda_empty
|
||||
# 1018| Type = decltype([...](...){...})
|
||||
# 1018| init: initializer for lambda_empty
|
||||
# 1018| expr: [...](...){...}
|
||||
# 1018| Type = decltype([...](...){...})
|
||||
# 1018| ValueCategory = prvalue
|
||||
# 1019| 1: ExprStmt
|
||||
# 1019| 0: call to operator()
|
||||
# 1019| Type = char
|
||||
# 1019| Value = 65
|
||||
# 1019| ValueCategory = prvalue
|
||||
# 1019| -1: (const lambda [] type at line 1018, col. 23)...
|
||||
# 1019| Conversion = glvalue conversion
|
||||
# 1019| Type = const lambda [] type at line 1018, col. 23
|
||||
# 1019| ValueCategory = lvalue
|
||||
# 1019| expr: lambda_empty
|
||||
# 1019| Type = decltype([...](...){...})
|
||||
# 1019| ValueCategory = lvalue
|
||||
# 1019| 0: (float)...
|
||||
# 1019| Conversion = integral to floating point conversion
|
||||
# 1019| Type = float
|
||||
# 1019| Value = 0.0
|
||||
# 1019| ValueCategory = prvalue
|
||||
# 1019| expr: 0
|
||||
# 1019| Type = int
|
||||
# 1019| Value = 0
|
||||
# 1019| ValueCategory = prvalue
|
||||
# 1020| 2: declaration
|
||||
# 1020| 0: definition of lambda_ref
|
||||
# 1020| Type = decltype([...](...){...})
|
||||
# 1020| init: initializer for lambda_ref
|
||||
# 1020| expr: [...](...){...}
|
||||
# 1020| Type = decltype([...](...){...})
|
||||
# 1020| ValueCategory = prvalue
|
||||
# 1020| 0: {...}
|
||||
# 1020| Type = decltype([...](...){...})
|
||||
# 1020| ValueCategory = prvalue
|
||||
# 1020| .s: (reference to)
|
||||
# 1020| Type = const String &
|
||||
# 1020| ValueCategory = prvalue
|
||||
# 1020| expr: (reference dereference)
|
||||
# 1020| Type = const String
|
||||
# 1020| ValueCategory = lvalue
|
||||
#-----| expr: s
|
||||
#-----| Type = const String &
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| .x: (reference to)
|
||||
#-----| Type = int &
|
||||
#-----| ValueCategory = prvalue
|
||||
#-----| expr: x
|
||||
#-----| Type = int
|
||||
#-----| ValueCategory = lvalue
|
||||
# 1021| 3: ExprStmt
|
||||
# 1021| 0: call to operator()
|
||||
# 1021| Type = char
|
||||
# 1021| ValueCategory = prvalue
|
||||
# 1021| -1: (const lambda [] type at line 1020, col. 21)...
|
||||
# 1021| Conversion = glvalue conversion
|
||||
# 1021| Type = const lambda [] type at line 1020, col. 21
|
||||
# 1021| ValueCategory = lvalue
|
||||
# 1021| expr: lambda_ref
|
||||
# 1021| Type = decltype([...](...){...})
|
||||
# 1021| ValueCategory = lvalue
|
||||
# 1021| 0: (float)...
|
||||
# 1021| Conversion = integral to floating point conversion
|
||||
# 1021| Type = float
|
||||
# 1021| Value = 1.0
|
||||
# 1021| ValueCategory = prvalue
|
||||
# 1021| expr: 1
|
||||
# 1021| Type = int
|
||||
# 1021| Value = 1
|
||||
# 1021| ValueCategory = prvalue
|
||||
# 1022| 4: declaration
|
||||
# 1022| 0: definition of lambda_val
|
||||
# 1022| Type = decltype([...](...){...})
|
||||
# 1022| init: initializer for lambda_val
|
||||
# 1022| expr: call to (constructor)
|
||||
# 1022| Type = void
|
||||
# 1022| ValueCategory = prvalue
|
||||
# 1022| 0: (reference to)
|
||||
# 1022| Type = lambda [] type at line 1022, col. 21 &
|
||||
# 1022| ValueCategory = prvalue
|
||||
# 1022| expr: [...](...){...}
|
||||
# 1022| Type = decltype([...](...){...})
|
||||
# 1022| ValueCategory = prvalue
|
||||
# 1022| 0: {...}
|
||||
# 1022| Type = decltype([...](...){...})
|
||||
# 1022| ValueCategory = prvalue
|
||||
#-----| .s: call to String
|
||||
#-----| Type = void
|
||||
#-----| ValueCategory = prvalue
|
||||
#-----| .x: x
|
||||
#-----| Type = int
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1023| 5: ExprStmt
|
||||
# 1023| 0: call to operator()
|
||||
# 1023| Type = char
|
||||
# 1023| ValueCategory = prvalue
|
||||
# 1023| -1: (const lambda [] type at line 1022, col. 21)...
|
||||
# 1023| Conversion = glvalue conversion
|
||||
# 1023| Type = const lambda [] type at line 1022, col. 21
|
||||
# 1023| ValueCategory = lvalue
|
||||
# 1023| expr: lambda_val
|
||||
# 1023| Type = decltype([...](...){...})
|
||||
# 1023| ValueCategory = lvalue
|
||||
# 1023| 0: (float)...
|
||||
# 1023| Conversion = integral to floating point conversion
|
||||
# 1023| Type = float
|
||||
# 1023| Value = 2.0
|
||||
# 1023| ValueCategory = prvalue
|
||||
# 1023| expr: 2
|
||||
# 1023| Type = int
|
||||
# 1023| Value = 2
|
||||
# 1023| ValueCategory = prvalue
|
||||
# 1024| 6: declaration
|
||||
# 1024| 0: definition of lambda_ref_explicit
|
||||
# 1024| Type = decltype([...](...){...})
|
||||
# 1024| init: initializer for lambda_ref_explicit
|
||||
# 1024| expr: [...](...){...}
|
||||
# 1024| Type = decltype([...](...){...})
|
||||
# 1024| ValueCategory = prvalue
|
||||
# 1024| 0: {...}
|
||||
# 1024| Type = decltype([...](...){...})
|
||||
# 1024| ValueCategory = prvalue
|
||||
# 1024| .s: (reference to)
|
||||
# 1024| Type = const String &
|
||||
# 1024| ValueCategory = prvalue
|
||||
# 1024| expr: (reference dereference)
|
||||
# 1024| Type = const String
|
||||
# 1024| ValueCategory = lvalue
|
||||
# 1024| expr: s
|
||||
# 1024| Type = const String &
|
||||
# 1024| ValueCategory = prvalue(load)
|
||||
# 1025| 7: ExprStmt
|
||||
# 1025| 0: call to operator()
|
||||
# 1025| Type = char
|
||||
# 1025| ValueCategory = prvalue
|
||||
# 1025| -1: (const lambda [] type at line 1024, col. 30)...
|
||||
# 1025| Conversion = glvalue conversion
|
||||
# 1025| Type = const lambda [] type at line 1024, col. 30
|
||||
# 1025| ValueCategory = lvalue
|
||||
# 1025| expr: lambda_ref_explicit
|
||||
# 1025| Type = decltype([...](...){...})
|
||||
# 1025| ValueCategory = lvalue
|
||||
# 1025| 0: (float)...
|
||||
# 1025| Conversion = integral to floating point conversion
|
||||
# 1025| Type = float
|
||||
# 1025| Value = 3.0
|
||||
# 1025| ValueCategory = prvalue
|
||||
# 1025| expr: 3
|
||||
# 1025| Type = int
|
||||
# 1025| Value = 3
|
||||
# 1025| ValueCategory = prvalue
|
||||
# 1026| 8: declaration
|
||||
# 1026| 0: definition of lambda_val_explicit
|
||||
# 1026| Type = decltype([...](...){...})
|
||||
# 1026| init: initializer for lambda_val_explicit
|
||||
# 1026| expr: call to (constructor)
|
||||
# 1026| Type = void
|
||||
# 1026| ValueCategory = prvalue
|
||||
# 1026| 0: (reference to)
|
||||
# 1026| Type = lambda [] type at line 1026, col. 30 &
|
||||
# 1026| ValueCategory = prvalue
|
||||
# 1026| expr: [...](...){...}
|
||||
# 1026| Type = decltype([...](...){...})
|
||||
# 1026| ValueCategory = prvalue
|
||||
# 1026| 0: {...}
|
||||
# 1026| Type = decltype([...](...){...})
|
||||
# 1026| ValueCategory = prvalue
|
||||
#-----| .s: call to String
|
||||
#-----| Type = void
|
||||
#-----| ValueCategory = prvalue
|
||||
# 1027| 9: ExprStmt
|
||||
# 1027| 0: call to operator()
|
||||
# 1027| Type = char
|
||||
# 1027| ValueCategory = prvalue
|
||||
# 1027| -1: (const lambda [] type at line 1026, col. 30)...
|
||||
# 1027| Conversion = glvalue conversion
|
||||
# 1027| Type = const lambda [] type at line 1026, col. 30
|
||||
# 1027| ValueCategory = lvalue
|
||||
# 1027| expr: lambda_val_explicit
|
||||
# 1027| Type = decltype([...](...){...})
|
||||
# 1027| ValueCategory = lvalue
|
||||
# 1027| 0: (float)...
|
||||
# 1027| Conversion = integral to floating point conversion
|
||||
# 1027| Type = float
|
||||
# 1027| Value = 4.0
|
||||
# 1027| ValueCategory = prvalue
|
||||
# 1027| expr: 4
|
||||
# 1027| Type = int
|
||||
# 1027| Value = 4
|
||||
# 1027| ValueCategory = prvalue
|
||||
# 1028| 10: declaration
|
||||
# 1028| 0: definition of lambda_mixed_explicit
|
||||
# 1028| Type = decltype([...](...){...})
|
||||
# 1028| init: initializer for lambda_mixed_explicit
|
||||
# 1028| expr: [...](...){...}
|
||||
# 1028| Type = decltype([...](...){...})
|
||||
# 1028| ValueCategory = prvalue
|
||||
# 1028| 0: {...}
|
||||
# 1028| Type = decltype([...](...){...})
|
||||
# 1028| ValueCategory = prvalue
|
||||
# 1028| .s: (reference to)
|
||||
# 1028| Type = const String &
|
||||
# 1028| ValueCategory = prvalue
|
||||
# 1028| expr: (reference dereference)
|
||||
# 1028| Type = const String
|
||||
# 1028| ValueCategory = lvalue
|
||||
# 1028| expr: s
|
||||
# 1028| Type = const String &
|
||||
# 1028| ValueCategory = prvalue(load)
|
||||
# 1028| .x: x
|
||||
# 1028| Type = int
|
||||
# 1028| ValueCategory = prvalue(load)
|
||||
# 1029| 11: ExprStmt
|
||||
# 1029| 0: call to operator()
|
||||
# 1029| Type = char
|
||||
# 1029| ValueCategory = prvalue
|
||||
# 1029| -1: (const lambda [] type at line 1028, col. 32)...
|
||||
# 1029| Conversion = glvalue conversion
|
||||
# 1029| Type = const lambda [] type at line 1028, col. 32
|
||||
# 1029| ValueCategory = lvalue
|
||||
# 1029| expr: lambda_mixed_explicit
|
||||
# 1029| Type = decltype([...](...){...})
|
||||
# 1029| ValueCategory = lvalue
|
||||
# 1029| 0: (float)...
|
||||
# 1029| Conversion = integral to floating point conversion
|
||||
# 1029| Type = float
|
||||
# 1029| Value = 5.0
|
||||
# 1029| ValueCategory = prvalue
|
||||
# 1029| expr: 5
|
||||
# 1029| Type = int
|
||||
# 1029| Value = 5
|
||||
# 1029| ValueCategory = prvalue
|
||||
# 1030| 12: declaration
|
||||
# 1030| 0: definition of r
|
||||
# 1030| Type = int
|
||||
# 1030| init: initializer for r
|
||||
# 1030| expr: ... - ...
|
||||
# 1030| Type = int
|
||||
# 1030| ValueCategory = prvalue
|
||||
# 1030| 0: x
|
||||
# 1030| Type = int
|
||||
# 1030| ValueCategory = prvalue(load)
|
||||
# 1030| 1: 1
|
||||
# 1030| Type = int
|
||||
# 1030| Value = 1
|
||||
# 1030| ValueCategory = prvalue
|
||||
# 1031| 13: declaration
|
||||
# 1031| 0: definition of lambda_inits
|
||||
# 1031| Type = decltype([...](...){...})
|
||||
# 1031| init: initializer for lambda_inits
|
||||
# 1031| expr: [...](...){...}
|
||||
# 1031| Type = decltype([...](...){...})
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| 0: {...}
|
||||
# 1031| Type = decltype([...](...){...})
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| .s: (reference to)
|
||||
# 1031| Type = const String &
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| expr: (reference dereference)
|
||||
# 1031| Type = const String
|
||||
# 1031| ValueCategory = lvalue
|
||||
# 1031| expr: s
|
||||
# 1031| Type = const String &
|
||||
# 1031| ValueCategory = prvalue(load)
|
||||
# 1031| .x: x
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue(load)
|
||||
# 1031| .i: ... + ...
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| 0: x
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue(load)
|
||||
# 1031| 1: 1
|
||||
# 1031| Type = int
|
||||
# 1031| Value = 1
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| .j: (reference to)
|
||||
# 1031| Type = int &
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| expr: r
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = lvalue
|
||||
# 1032| 14: ExprStmt
|
||||
# 1032| 0: call to operator()
|
||||
# 1032| Type = char
|
||||
# 1032| ValueCategory = prvalue
|
||||
# 1032| -1: (const lambda [] type at line 1031, col. 23)...
|
||||
# 1032| Conversion = glvalue conversion
|
||||
# 1032| Type = const lambda [] type at line 1031, col. 23
|
||||
# 1032| ValueCategory = lvalue
|
||||
# 1032| expr: lambda_inits
|
||||
# 1032| Type = decltype([...](...){...})
|
||||
# 1032| ValueCategory = lvalue
|
||||
# 1032| 0: (float)...
|
||||
# 1032| Conversion = integral to floating point conversion
|
||||
# 1032| Type = float
|
||||
# 1032| Value = 6.0
|
||||
# 1032| ValueCategory = prvalue
|
||||
# 1032| expr: 6
|
||||
# 1032| Type = int
|
||||
# 1032| Value = 6
|
||||
# 1032| ValueCategory = prvalue
|
||||
# 1033| 15: return ...
|
||||
# 1018| (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)& (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)::operator=((void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23) const&)
|
||||
# 1018| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1018, col. 23 &
|
||||
# 1018| void (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23) const&)
|
||||
# 1018| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1018, col. 23 &
|
||||
# 1018| void (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)&&)
|
||||
# 1018| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = lambda [] type at line 1018, col. 23 &&
|
||||
# 1018| initializations:
|
||||
# 1018| body: { ... }
|
||||
# 1018| 0: return ...
|
||||
# 1018| void (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)::(constructor)()
|
||||
# 1018| params:
|
||||
# 1018| char (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)::_FUN(float)
|
||||
# 1018| params:
|
||||
# 1018| 0: f
|
||||
# 1018| Type = float
|
||||
# 1018| char (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)::operator()(float) const
|
||||
# 1018| params:
|
||||
# 1018| 0: f
|
||||
# 1018| Type = float
|
||||
# 1018| body: { ... }
|
||||
# 1018| 0: return ...
|
||||
# 1018| 0: 65
|
||||
# 1018| Type = char
|
||||
# 1018| Value = 65
|
||||
# 1018| ValueCategory = prvalue
|
||||
# 1018| char(* (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)::operator char (*)(float)() const)(float)
|
||||
# 1018| params:
|
||||
#-----| body: { ... }
|
||||
# 1018| 0: return ...
|
||||
# 1018| 0: _FUN
|
||||
# 1018| Type = ..(*)(..)
|
||||
# 1018| ValueCategory = prvalue(load)
|
||||
# 1020| (void Lambda(int, String const&))::(lambda [] type at line 1020, col. 21)& (void Lambda(int, String const&))::(lambda [] type at line 1020, col. 21)::operator=((void Lambda(int, String const&))::(lambda [] type at line 1020, col. 21) const&)
|
||||
# 1020| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1020, col. 21 &
|
||||
# 1020| void (void Lambda(int, String const&))::(lambda [] type at line 1020, col. 21)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1020, col. 21) const&)
|
||||
# 1020| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1020, col. 21 &
|
||||
# 1020| void (void Lambda(int, String const&))::(lambda [] type at line 1020, col. 21)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1020, col. 21)&&)
|
||||
# 1020| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = lambda [] type at line 1020, col. 21 &&
|
||||
# 1020| initializations:
|
||||
# 1020| 0: constructor init of field s
|
||||
# 1020| Type = const String &
|
||||
# 1020| ValueCategory = prvalue
|
||||
# 1020| 0: Unknown literal
|
||||
# 1020| Type = const String &
|
||||
# 1020| ValueCategory = prvalue
|
||||
# 1020| 1: constructor init of field x
|
||||
# 1020| Type = int &
|
||||
# 1020| ValueCategory = prvalue
|
||||
# 1020| 0: Unknown literal
|
||||
# 1020| Type = int &
|
||||
# 1020| ValueCategory = prvalue
|
||||
# 1020| body: { ... }
|
||||
# 1020| 0: return ...
|
||||
# 1020| void (void Lambda(int, String const&))::(lambda [] type at line 1020, col. 21)::(constructor)()
|
||||
# 1020| params:
|
||||
# 1020| char (void Lambda(int, String const&))::(lambda [] type at line 1020, col. 21)::operator()(float) const
|
||||
# 1020| params:
|
||||
# 1020| 0: f
|
||||
# 1020| Type = float
|
||||
# 1020| body: { ... }
|
||||
# 1020| 0: return ...
|
||||
# 1020| 0: access to array
|
||||
# 1020| Type = char
|
||||
# 1020| ValueCategory = prvalue(load)
|
||||
# 1020| 0: call to c_str
|
||||
# 1020| Type = const char *
|
||||
# 1020| ValueCategory = prvalue
|
||||
# 1020| -1: (reference dereference)
|
||||
# 1020| Type = const String
|
||||
# 1020| ValueCategory = lvalue
|
||||
#-----| expr: s
|
||||
#-----| Type = const String &
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1020, col. 21 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1020| 1: (reference dereference)
|
||||
# 1020| Type = int
|
||||
# 1020| ValueCategory = prvalue(load)
|
||||
#-----| expr: x
|
||||
#-----| Type = int &
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1020, col. 21 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1022| (void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21)& (void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21)::operator=((void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21) const&)
|
||||
# 1022| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1022, col. 21 &
|
||||
# 1022| void (void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21) const&)
|
||||
# 1022| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1022, col. 21 &
|
||||
# 1022| void (void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21)&&)
|
||||
# 1022| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = lambda [] type at line 1022, col. 21 &&
|
||||
# 1022| initializations:
|
||||
# 1022| 0: constructor init of field s
|
||||
# 1022| Type = const String
|
||||
# 1022| ValueCategory = prvalue
|
||||
# 1022| 0: call to String
|
||||
# 1022| Type = void
|
||||
# 1022| ValueCategory = prvalue
|
||||
# 1022| 1: constructor init of field x
|
||||
# 1022| Type = int
|
||||
# 1022| ValueCategory = prvalue
|
||||
# 1022| 0: Unknown literal
|
||||
# 1022| Type = int
|
||||
# 1022| ValueCategory = prvalue
|
||||
# 1022| body: { ... }
|
||||
# 1022| 0: return ...
|
||||
# 1022| void (void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21)::(constructor)()
|
||||
# 1022| params:
|
||||
# 1022| void (void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21)::~<unnamed>()
|
||||
# 1022| params:
|
||||
#-----| body: { ... }
|
||||
#-----| 0: return ...
|
||||
# 1022| destructions:
|
||||
# 1022| 0: destructor field destruction of s
|
||||
# 1022| Type = const String
|
||||
# 1022| ValueCategory = prvalue
|
||||
# 1022| 0: call to ~String
|
||||
# 1022| Type = void
|
||||
# 1022| ValueCategory = prvalue
|
||||
# 1022| -1: s
|
||||
# 1022| Type = const String
|
||||
# 1022| ValueCategory = lvalue
|
||||
# 1022| char (void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21)::operator()(float) const
|
||||
# 1022| params:
|
||||
# 1022| 0: f
|
||||
# 1022| Type = float
|
||||
# 1022| body: { ... }
|
||||
# 1022| 0: return ...
|
||||
# 1022| 0: access to array
|
||||
# 1022| Type = char
|
||||
# 1022| ValueCategory = prvalue(load)
|
||||
# 1022| 0: call to c_str
|
||||
# 1022| Type = const char *
|
||||
# 1022| ValueCategory = prvalue
|
||||
#-----| -1: s
|
||||
#-----| Type = const String
|
||||
#-----| ValueCategory = lvalue
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1022, col. 21 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| 1: x
|
||||
#-----| Type = int
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1022, col. 21 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1024| (void Lambda(int, String const&))::(lambda [] type at line 1024, col. 30)& (void Lambda(int, String const&))::(lambda [] type at line 1024, col. 30)::operator=((void Lambda(int, String const&))::(lambda [] type at line 1024, col. 30) const&)
|
||||
# 1024| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1024, col. 30 &
|
||||
# 1024| void (void Lambda(int, String const&))::(lambda [] type at line 1024, col. 30)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1024, col. 30) const&)
|
||||
# 1024| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1024, col. 30 &
|
||||
# 1024| void (void Lambda(int, String const&))::(lambda [] type at line 1024, col. 30)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1024, col. 30)&&)
|
||||
# 1024| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = lambda [] type at line 1024, col. 30 &&
|
||||
# 1024| initializations:
|
||||
# 1024| 0: constructor init of field s
|
||||
# 1024| Type = const String &
|
||||
# 1024| ValueCategory = prvalue
|
||||
# 1024| 0: Unknown literal
|
||||
# 1024| Type = const String &
|
||||
# 1024| ValueCategory = prvalue
|
||||
# 1024| body: { ... }
|
||||
# 1024| 0: return ...
|
||||
# 1024| void (void Lambda(int, String const&))::(lambda [] type at line 1024, col. 30)::(constructor)()
|
||||
# 1024| params:
|
||||
# 1024| char (void Lambda(int, String const&))::(lambda [] type at line 1024, col. 30)::operator()(float) const
|
||||
# 1024| params:
|
||||
# 1024| 0: f
|
||||
# 1024| Type = float
|
||||
# 1024| body: { ... }
|
||||
# 1024| 0: return ...
|
||||
# 1024| 0: access to array
|
||||
# 1024| Type = char
|
||||
# 1024| ValueCategory = prvalue(load)
|
||||
# 1024| 0: call to c_str
|
||||
# 1024| Type = const char *
|
||||
# 1024| ValueCategory = prvalue
|
||||
# 1024| -1: (reference dereference)
|
||||
# 1024| Type = const String
|
||||
# 1024| ValueCategory = lvalue
|
||||
#-----| expr: s
|
||||
#-----| Type = const String &
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1024, col. 30 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1024| 1: 0
|
||||
# 1024| Type = int
|
||||
# 1024| Value = 0
|
||||
# 1024| ValueCategory = prvalue
|
||||
# 1026| (void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)& (void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)::operator=((void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30) const&)
|
||||
# 1026| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1026, col. 30 &
|
||||
# 1026| void (void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30) const&)
|
||||
# 1026| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1026, col. 30 &
|
||||
# 1026| void (void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)&&)
|
||||
# 1026| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = lambda [] type at line 1026, col. 30 &&
|
||||
# 1026| initializations:
|
||||
# 1026| 0: constructor init of field s
|
||||
# 1026| Type = const String
|
||||
# 1026| ValueCategory = prvalue
|
||||
# 1026| 0: call to String
|
||||
# 1026| Type = void
|
||||
# 1026| ValueCategory = prvalue
|
||||
# 1026| body: { ... }
|
||||
# 1026| 0: return ...
|
||||
# 1026| void (void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)::(constructor)()
|
||||
# 1026| params:
|
||||
# 1026| void (void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)::~<unnamed>()
|
||||
# 1026| params:
|
||||
#-----| body: { ... }
|
||||
#-----| 0: return ...
|
||||
# 1026| destructions:
|
||||
# 1026| 0: destructor field destruction of s
|
||||
# 1026| Type = const String
|
||||
# 1026| ValueCategory = prvalue
|
||||
# 1026| 0: call to ~String
|
||||
# 1026| Type = void
|
||||
# 1026| ValueCategory = prvalue
|
||||
# 1026| -1: s
|
||||
# 1026| Type = const String
|
||||
# 1026| ValueCategory = lvalue
|
||||
# 1026| char (void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)::operator()(float) const
|
||||
# 1026| params:
|
||||
# 1026| 0: f
|
||||
# 1026| Type = float
|
||||
# 1026| body: { ... }
|
||||
# 1026| 0: return ...
|
||||
# 1026| 0: access to array
|
||||
# 1026| Type = char
|
||||
# 1026| ValueCategory = prvalue(load)
|
||||
# 1026| 0: call to c_str
|
||||
# 1026| Type = const char *
|
||||
# 1026| ValueCategory = prvalue
|
||||
#-----| -1: s
|
||||
#-----| Type = const String
|
||||
#-----| ValueCategory = lvalue
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1026, col. 30 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1026| 1: 0
|
||||
# 1026| Type = int
|
||||
# 1026| Value = 0
|
||||
# 1026| ValueCategory = prvalue
|
||||
# 1028| (void Lambda(int, String const&))::(lambda [] type at line 1028, col. 32)& (void Lambda(int, String const&))::(lambda [] type at line 1028, col. 32)::operator=((void Lambda(int, String const&))::(lambda [] type at line 1028, col. 32) const&)
|
||||
# 1028| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1028, col. 32 &
|
||||
# 1028| void (void Lambda(int, String const&))::(lambda [] type at line 1028, col. 32)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1028, col. 32) const&)
|
||||
# 1028| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1028, col. 32 &
|
||||
# 1028| void (void Lambda(int, String const&))::(lambda [] type at line 1028, col. 32)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1028, col. 32)&&)
|
||||
# 1028| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = lambda [] type at line 1028, col. 32 &&
|
||||
# 1028| initializations:
|
||||
# 1028| 0: constructor init of field s
|
||||
# 1028| Type = const String &
|
||||
# 1028| ValueCategory = prvalue
|
||||
# 1028| 0: Unknown literal
|
||||
# 1028| Type = const String &
|
||||
# 1028| ValueCategory = prvalue
|
||||
# 1028| 1: constructor init of field x
|
||||
# 1028| Type = int
|
||||
# 1028| ValueCategory = prvalue
|
||||
# 1028| 0: Unknown literal
|
||||
# 1028| Type = int
|
||||
# 1028| ValueCategory = prvalue
|
||||
# 1028| body: { ... }
|
||||
# 1028| 0: return ...
|
||||
# 1028| void (void Lambda(int, String const&))::(lambda [] type at line 1028, col. 32)::(constructor)()
|
||||
# 1028| params:
|
||||
# 1028| char (void Lambda(int, String const&))::(lambda [] type at line 1028, col. 32)::operator()(float) const
|
||||
# 1028| params:
|
||||
# 1028| 0: f
|
||||
# 1028| Type = float
|
||||
# 1028| body: { ... }
|
||||
# 1028| 0: return ...
|
||||
# 1028| 0: access to array
|
||||
# 1028| Type = char
|
||||
# 1028| ValueCategory = prvalue(load)
|
||||
# 1028| 0: call to c_str
|
||||
# 1028| Type = const char *
|
||||
# 1028| ValueCategory = prvalue
|
||||
# 1028| -1: (reference dereference)
|
||||
# 1028| Type = const String
|
||||
# 1028| ValueCategory = lvalue
|
||||
#-----| expr: s
|
||||
#-----| Type = const String &
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1028, col. 32 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| 1: x
|
||||
#-----| Type = int
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1028, col. 32 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1031| (void Lambda(int, String const&))::(lambda [] type at line 1031, col. 23)& (void Lambda(int, String const&))::(lambda [] type at line 1031, col. 23)::operator=((void Lambda(int, String const&))::(lambda [] type at line 1031, col. 23) const&)
|
||||
# 1031| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1031, col. 23 &
|
||||
# 1031| void (void Lambda(int, String const&))::(lambda [] type at line 1031, col. 23)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1031, col. 23) const&)
|
||||
# 1031| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = const lambda [] type at line 1031, col. 23 &
|
||||
# 1031| void (void Lambda(int, String const&))::(lambda [] type at line 1031, col. 23)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1031, col. 23)&&)
|
||||
# 1031| params:
|
||||
#-----| 0: p#0
|
||||
#-----| Type = lambda [] type at line 1031, col. 23 &&
|
||||
# 1031| initializations:
|
||||
# 1031| 0: constructor init of field s
|
||||
# 1031| Type = const String &
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| 0: Unknown literal
|
||||
# 1031| Type = const String &
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| 1: constructor init of field x
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| 0: Unknown literal
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| 2: constructor init of field i
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| 0: Unknown literal
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| 3: constructor init of field j
|
||||
# 1031| Type = int &
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| 0: Unknown literal
|
||||
# 1031| Type = int &
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| body: { ... }
|
||||
# 1031| 0: return ...
|
||||
# 1031| void (void Lambda(int, String const&))::(lambda [] type at line 1031, col. 23)::(constructor)()
|
||||
# 1031| params:
|
||||
# 1031| char (void Lambda(int, String const&))::(lambda [] type at line 1031, col. 23)::operator()(float) const
|
||||
# 1031| params:
|
||||
# 1031| 0: f
|
||||
# 1031| Type = float
|
||||
# 1031| body: { ... }
|
||||
# 1031| 0: return ...
|
||||
# 1031| 0: access to array
|
||||
# 1031| Type = char
|
||||
# 1031| ValueCategory = prvalue(load)
|
||||
# 1031| 0: call to c_str
|
||||
# 1031| Type = const char *
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| -1: (reference dereference)
|
||||
# 1031| Type = const String
|
||||
# 1031| ValueCategory = lvalue
|
||||
#-----| expr: s
|
||||
#-----| Type = const String &
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1031, col. 23 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1031| 1: ... - ...
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue
|
||||
# 1031| 0: ... + ...
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue
|
||||
#-----| 0: x
|
||||
#-----| Type = int
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1031, col. 23 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1031| 1: i
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue(load)
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1031, col. 23 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
# 1031| 1: (reference dereference)
|
||||
# 1031| Type = int
|
||||
# 1031| ValueCategory = prvalue(load)
|
||||
# 1031| expr: j
|
||||
# 1031| Type = int &
|
||||
# 1031| ValueCategory = prvalue(load)
|
||||
#-----| -1: this
|
||||
#-----| Type = const lambda [] type at line 1031, col. 23 *
|
||||
#-----| ValueCategory = prvalue(load)
|
||||
|
||||
@@ -1006,4 +1006,47 @@ void OperatorDeleteArray() {
|
||||
}
|
||||
#endif
|
||||
|
||||
struct EmptyStruct {};
|
||||
|
||||
void EmptyStructInit() {
|
||||
EmptyStruct s = {};
|
||||
}
|
||||
|
||||
auto lam = []() {};
|
||||
|
||||
void Lambda(int x, const String& s) {
|
||||
auto lambda_empty = [](float f) { return 'A'; };
|
||||
lambda_empty(0);
|
||||
auto lambda_ref = [&](float f) { return s.c_str()[x]; };
|
||||
lambda_ref(1);
|
||||
auto lambda_val = [=](float f) { return s.c_str()[x]; };
|
||||
lambda_val(2);
|
||||
auto lambda_ref_explicit = [&s](float f) { return s.c_str()[0]; };
|
||||
lambda_ref_explicit(3);
|
||||
auto lambda_val_explicit = [s](float f) { return s.c_str()[0]; };
|
||||
lambda_val_explicit(4);
|
||||
auto lambda_mixed_explicit = [&s, x](float f) { return s.c_str()[x]; };
|
||||
lambda_mixed_explicit(5);
|
||||
int r = x - 1;
|
||||
auto lambda_inits = [&s, x, i = x + 1, &j = r](float f) { return s.c_str()[x + i - j]; };
|
||||
lambda_inits(6);
|
||||
}
|
||||
|
||||
#if 0 // Explicit capture of `this` requires possible extractor fixes.
|
||||
|
||||
struct LambdaContainer {
|
||||
int x;
|
||||
|
||||
void LambdaMember(const String& s) {
|
||||
auto lambda_implicit_this = [=](float f) { return s.c_str()[x]; };
|
||||
lambda_implicit_this(1);
|
||||
auto lambda_explicit_this_byref = [this, &s](float f) { return s.c_str()[x]; };
|
||||
lambda_explicit_this_byref(2);
|
||||
auto lambda_explicit_this_bycopy = [*this, &s](float f) { return s.c_str()[x]; };
|
||||
lambda_explicit_this_bycopy(3);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
// semmle-extractor-options: -std=c++17
|
||||
|
||||
@@ -4366,3 +4366,447 @@ ir.cpp:
|
||||
# 987| v0_21(void) = ReturnValue : r0_20, mu0_2
|
||||
# 987| v0_22(void) = UnmodeledUse : mu*
|
||||
# 987| v0_23(void) = ExitFunction :
|
||||
|
||||
# 1011| void EmptyStructInit()
|
||||
# 1011| Block 0
|
||||
# 1011| v0_0(void) = EnterFunction :
|
||||
# 1011| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1011| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1012| r0_3(glval<EmptyStruct>) = VariableAddress[s] :
|
||||
# 1012| mu0_4(EmptyStruct) = Uninitialized[s] : r0_3
|
||||
# 1013| v0_5(void) = NoOp :
|
||||
# 1011| v0_6(void) = ReturnVoid :
|
||||
# 1011| v0_7(void) = UnmodeledUse : mu*
|
||||
# 1011| v0_8(void) = ExitFunction :
|
||||
|
||||
# 1015| void (lambda [] type at line 1015, col. 12)::(constructor)((lambda [] type at line 1015, col. 12)&&)
|
||||
# 1015| Block 0
|
||||
# 1015| v0_0(void) = EnterFunction :
|
||||
# 1015| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1015| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1015| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
#-----| r0_4(glval<lambda [] type at line 1015, col. 12 &&>) = VariableAddress[p#0] :
|
||||
#-----| mu0_5(lambda [] type at line 1015, col. 12 &&) = InitializeParameter[p#0] : r0_4
|
||||
# 1015| v0_6(void) = NoOp :
|
||||
# 1015| v0_7(void) = ReturnVoid :
|
||||
# 1015| v0_8(void) = UnmodeledUse : mu*
|
||||
# 1015| v0_9(void) = ExitFunction :
|
||||
|
||||
# 1015| void (lambda [] type at line 1015, col. 12)::operator()() const
|
||||
# 1015| Block 0
|
||||
# 1015| v0_0(void) = EnterFunction :
|
||||
# 1015| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1015| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1015| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
# 1015| v0_4(void) = NoOp :
|
||||
# 1015| v0_5(void) = ReturnVoid :
|
||||
# 1015| v0_6(void) = UnmodeledUse : mu*
|
||||
# 1015| v0_7(void) = ExitFunction :
|
||||
|
||||
# 1015| void(* (lambda [] type at line 1015, col. 12)::operator void (*)()() const)()
|
||||
# 1015| Block 0
|
||||
# 1015| v0_0(void) = EnterFunction :
|
||||
# 1015| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1015| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1015| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
# 1015| r0_4(glval<..(*)(..)>) = VariableAddress[#return] :
|
||||
# 1015| r0_5(glval<..(*)(..)>) = FunctionAddress[_FUN] :
|
||||
# 1015| mu0_6(..(*)(..)) = Store : r0_4, r0_5
|
||||
# 1015| r0_7(glval<..(*)(..)>) = VariableAddress[#return] :
|
||||
# 1015| v0_8(void) = ReturnValue : r0_7, mu0_2
|
||||
# 1015| v0_9(void) = UnmodeledUse : mu*
|
||||
# 1015| v0_10(void) = ExitFunction :
|
||||
|
||||
# 1017| void Lambda(int, String const&)
|
||||
# 1017| Block 0
|
||||
# 1017| v0_0(void) = EnterFunction :
|
||||
# 1017| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1017| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1017| r0_3(glval<int>) = VariableAddress[x] :
|
||||
# 1017| mu0_4(int) = InitializeParameter[x] : r0_3
|
||||
# 1017| r0_5(glval<String &>) = VariableAddress[s] :
|
||||
# 1017| mu0_6(String &) = InitializeParameter[s] : r0_5
|
||||
# 1018| r0_7(glval<decltype([...](...){...})>) = VariableAddress[lambda_empty] :
|
||||
# 1018| r0_8(glval<decltype([...](...){...})>) = VariableAddress[#temp1018:23] :
|
||||
# 1018| mu0_9(decltype([...](...){...})) = Uninitialized[#temp1018:23] : r0_8
|
||||
# 1018| r0_10(decltype([...](...){...})) = Load : r0_8, mu0_2
|
||||
# 1018| mu0_11(decltype([...](...){...})) = Store : r0_7, r0_10
|
||||
# 1019| r0_12(char) = Constant[65] :
|
||||
# 1020| r0_13(glval<decltype([...](...){...})>) = VariableAddress[lambda_ref] :
|
||||
# 1020| r0_14(glval<decltype([...](...){...})>) = VariableAddress[#temp1020:21] :
|
||||
# 1020| mu0_15(decltype([...](...){...})) = Uninitialized[#temp1020:21] : r0_14
|
||||
# 1020| r0_16(glval<String &>) = FieldAddress[s] : r0_14
|
||||
#-----| r0_17(glval<String &>) = VariableAddress[s] :
|
||||
#-----| r0_18(String &) = Load : r0_17, mu0_2
|
||||
# 1020| mu0_19(String &) = Store : r0_16, r0_18
|
||||
# 1020| r0_20(glval<int &>) = FieldAddress[x] : r0_14
|
||||
#-----| r0_21(glval<int>) = VariableAddress[x] :
|
||||
#-----| mu0_22(int &) = Store : r0_20, r0_21
|
||||
# 1020| r0_23(decltype([...](...){...})) = Load : r0_14, mu0_2
|
||||
# 1020| mu0_24(decltype([...](...){...})) = Store : r0_13, r0_23
|
||||
# 1021| r0_25(glval<decltype([...](...){...})>) = VariableAddress[lambda_ref] :
|
||||
# 1021| r0_26(glval<decltype([...](...){...})>) = Convert : r0_25
|
||||
# 1021| r0_27(glval<unknown>) = FunctionAddress[operator()] :
|
||||
# 1021| r0_28(float) = Constant[1.0] :
|
||||
# 1021| r0_29(char) = Call : r0_27, this:r0_26, r0_28
|
||||
# 1021| mu0_30(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1022| r0_31(glval<decltype([...](...){...})>) = VariableAddress[lambda_val] :
|
||||
# 1022| r0_32(glval<unknown>) = FunctionAddress[(constructor)] :
|
||||
# 1022| r0_33(glval<decltype([...](...){...})>) = VariableAddress[#temp1022:21] :
|
||||
# 1022| mu0_34(decltype([...](...){...})) = Uninitialized[#temp1022:21] : r0_33
|
||||
# 1022| r0_35(glval<String>) = FieldAddress[s] : r0_33
|
||||
#-----| r0_36(glval<unknown>) = FunctionAddress[String] :
|
||||
#-----| v0_37(void) = Call : r0_36, this:r0_35
|
||||
#-----| mu0_38(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1022| r0_39(glval<int>) = FieldAddress[x] : r0_33
|
||||
#-----| r0_40(glval<int>) = VariableAddress[x] :
|
||||
#-----| r0_41(int) = Load : r0_40, mu0_2
|
||||
#-----| mu0_42(int) = Store : r0_39, r0_41
|
||||
# 1022| r0_43(decltype([...](...){...})) = Load : r0_33, mu0_2
|
||||
# 1022| v0_44(void) = Call : r0_32, this:r0_31, r0_43
|
||||
# 1022| mu0_45(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1023| r0_46(glval<decltype([...](...){...})>) = VariableAddress[lambda_val] :
|
||||
# 1023| r0_47(glval<decltype([...](...){...})>) = Convert : r0_46
|
||||
# 1023| r0_48(glval<unknown>) = FunctionAddress[operator()] :
|
||||
# 1023| r0_49(float) = Constant[2.0] :
|
||||
# 1023| r0_50(char) = Call : r0_48, this:r0_47, r0_49
|
||||
# 1023| mu0_51(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1024| r0_52(glval<decltype([...](...){...})>) = VariableAddress[lambda_ref_explicit] :
|
||||
# 1024| r0_53(glval<decltype([...](...){...})>) = VariableAddress[#temp1024:30] :
|
||||
# 1024| mu0_54(decltype([...](...){...})) = Uninitialized[#temp1024:30] : r0_53
|
||||
# 1024| r0_55(glval<String &>) = FieldAddress[s] : r0_53
|
||||
# 1024| r0_56(glval<String &>) = VariableAddress[s] :
|
||||
# 1024| r0_57(String &) = Load : r0_56, mu0_2
|
||||
# 1024| mu0_58(String &) = Store : r0_55, r0_57
|
||||
# 1024| r0_59(decltype([...](...){...})) = Load : r0_53, mu0_2
|
||||
# 1024| mu0_60(decltype([...](...){...})) = Store : r0_52, r0_59
|
||||
# 1025| r0_61(glval<decltype([...](...){...})>) = VariableAddress[lambda_ref_explicit] :
|
||||
# 1025| r0_62(glval<decltype([...](...){...})>) = Convert : r0_61
|
||||
# 1025| r0_63(glval<unknown>) = FunctionAddress[operator()] :
|
||||
# 1025| r0_64(float) = Constant[3.0] :
|
||||
# 1025| r0_65(char) = Call : r0_63, this:r0_62, r0_64
|
||||
# 1025| mu0_66(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1026| r0_67(glval<decltype([...](...){...})>) = VariableAddress[lambda_val_explicit] :
|
||||
# 1026| r0_68(glval<unknown>) = FunctionAddress[(constructor)] :
|
||||
# 1026| r0_69(glval<decltype([...](...){...})>) = VariableAddress[#temp1026:30] :
|
||||
# 1026| mu0_70(decltype([...](...){...})) = Uninitialized[#temp1026:30] : r0_69
|
||||
# 1026| r0_71(glval<String>) = FieldAddress[s] : r0_69
|
||||
#-----| r0_72(glval<unknown>) = FunctionAddress[String] :
|
||||
#-----| v0_73(void) = Call : r0_72, this:r0_71
|
||||
#-----| mu0_74(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1026| r0_75(decltype([...](...){...})) = Load : r0_69, mu0_2
|
||||
# 1026| v0_76(void) = Call : r0_68, this:r0_67, r0_75
|
||||
# 1026| mu0_77(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1027| r0_78(glval<decltype([...](...){...})>) = VariableAddress[lambda_val_explicit] :
|
||||
# 1027| r0_79(glval<decltype([...](...){...})>) = Convert : r0_78
|
||||
# 1027| r0_80(glval<unknown>) = FunctionAddress[operator()] :
|
||||
# 1027| r0_81(float) = Constant[4.0] :
|
||||
# 1027| r0_82(char) = Call : r0_80, this:r0_79, r0_81
|
||||
# 1027| mu0_83(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1028| r0_84(glval<decltype([...](...){...})>) = VariableAddress[lambda_mixed_explicit] :
|
||||
# 1028| r0_85(glval<decltype([...](...){...})>) = VariableAddress[#temp1028:32] :
|
||||
# 1028| mu0_86(decltype([...](...){...})) = Uninitialized[#temp1028:32] : r0_85
|
||||
# 1028| r0_87(glval<String &>) = FieldAddress[s] : r0_85
|
||||
# 1028| r0_88(glval<String &>) = VariableAddress[s] :
|
||||
# 1028| r0_89(String &) = Load : r0_88, mu0_2
|
||||
# 1028| mu0_90(String &) = Store : r0_87, r0_89
|
||||
# 1028| r0_91(glval<int>) = FieldAddress[x] : r0_85
|
||||
# 1028| r0_92(glval<int>) = VariableAddress[x] :
|
||||
# 1028| r0_93(int) = Load : r0_92, mu0_2
|
||||
# 1028| mu0_94(int) = Store : r0_91, r0_93
|
||||
# 1028| r0_95(decltype([...](...){...})) = Load : r0_85, mu0_2
|
||||
# 1028| mu0_96(decltype([...](...){...})) = Store : r0_84, r0_95
|
||||
# 1029| r0_97(glval<decltype([...](...){...})>) = VariableAddress[lambda_mixed_explicit] :
|
||||
# 1029| r0_98(glval<decltype([...](...){...})>) = Convert : r0_97
|
||||
# 1029| r0_99(glval<unknown>) = FunctionAddress[operator()] :
|
||||
# 1029| r0_100(float) = Constant[5.0] :
|
||||
# 1029| r0_101(char) = Call : r0_99, this:r0_98, r0_100
|
||||
# 1029| mu0_102(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1030| r0_103(glval<int>) = VariableAddress[r] :
|
||||
# 1030| r0_104(glval<int>) = VariableAddress[x] :
|
||||
# 1030| r0_105(int) = Load : r0_104, mu0_2
|
||||
# 1030| r0_106(int) = Constant[1] :
|
||||
# 1030| r0_107(int) = Sub : r0_105, r0_106
|
||||
# 1030| mu0_108(int) = Store : r0_103, r0_107
|
||||
# 1031| r0_109(glval<decltype([...](...){...})>) = VariableAddress[lambda_inits] :
|
||||
# 1031| r0_110(glval<decltype([...](...){...})>) = VariableAddress[#temp1031:23] :
|
||||
# 1031| mu0_111(decltype([...](...){...})) = Uninitialized[#temp1031:23] : r0_110
|
||||
# 1031| r0_112(glval<String &>) = FieldAddress[s] : r0_110
|
||||
# 1031| r0_113(glval<String &>) = VariableAddress[s] :
|
||||
# 1031| r0_114(String &) = Load : r0_113, mu0_2
|
||||
# 1031| mu0_115(String &) = Store : r0_112, r0_114
|
||||
# 1031| r0_116(glval<int>) = FieldAddress[x] : r0_110
|
||||
# 1031| r0_117(glval<int>) = VariableAddress[x] :
|
||||
# 1031| r0_118(int) = Load : r0_117, mu0_2
|
||||
# 1031| mu0_119(int) = Store : r0_116, r0_118
|
||||
# 1031| r0_120(glval<int>) = FieldAddress[i] : r0_110
|
||||
# 1031| r0_121(glval<int>) = VariableAddress[x] :
|
||||
# 1031| r0_122(int) = Load : r0_121, mu0_2
|
||||
# 1031| r0_123(int) = Constant[1] :
|
||||
# 1031| r0_124(int) = Add : r0_122, r0_123
|
||||
# 1031| mu0_125(int) = Store : r0_120, r0_124
|
||||
# 1031| r0_126(glval<int &>) = FieldAddress[j] : r0_110
|
||||
# 1031| r0_127(glval<int>) = VariableAddress[r] :
|
||||
# 1031| mu0_128(int &) = Store : r0_126, r0_127
|
||||
# 1031| r0_129(decltype([...](...){...})) = Load : r0_110, mu0_2
|
||||
# 1031| mu0_130(decltype([...](...){...})) = Store : r0_109, r0_129
|
||||
# 1032| r0_131(glval<decltype([...](...){...})>) = VariableAddress[lambda_inits] :
|
||||
# 1032| r0_132(glval<decltype([...](...){...})>) = Convert : r0_131
|
||||
# 1032| r0_133(glval<unknown>) = FunctionAddress[operator()] :
|
||||
# 1032| r0_134(float) = Constant[6.0] :
|
||||
# 1032| r0_135(char) = Call : r0_133, this:r0_132, r0_134
|
||||
# 1032| mu0_136(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1033| v0_137(void) = NoOp :
|
||||
# 1017| v0_138(void) = ReturnVoid :
|
||||
# 1017| v0_139(void) = UnmodeledUse : mu*
|
||||
# 1017| v0_140(void) = ExitFunction :
|
||||
|
||||
# 1018| void (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)&&)
|
||||
# 1018| Block 0
|
||||
# 1018| v0_0(void) = EnterFunction :
|
||||
# 1018| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1018| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1018| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
#-----| r0_4(glval<lambda [] type at line 1018, col. 23 &&>) = VariableAddress[p#0] :
|
||||
#-----| mu0_5(lambda [] type at line 1018, col. 23 &&) = InitializeParameter[p#0] : r0_4
|
||||
# 1018| v0_6(void) = NoOp :
|
||||
# 1018| v0_7(void) = ReturnVoid :
|
||||
# 1018| v0_8(void) = UnmodeledUse : mu*
|
||||
# 1018| v0_9(void) = ExitFunction :
|
||||
|
||||
# 1018| char (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)::operator()(float) const
|
||||
# 1018| Block 0
|
||||
# 1018| v0_0(void) = EnterFunction :
|
||||
# 1018| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1018| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1018| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
# 1018| r0_4(glval<float>) = VariableAddress[f] :
|
||||
# 1018| mu0_5(float) = InitializeParameter[f] : r0_4
|
||||
# 1018| r0_6(glval<char>) = VariableAddress[#return] :
|
||||
# 1018| r0_7(char) = Constant[65] :
|
||||
# 1018| mu0_8(char) = Store : r0_6, r0_7
|
||||
# 1018| r0_9(glval<char>) = VariableAddress[#return] :
|
||||
# 1018| v0_10(void) = ReturnValue : r0_9, mu0_2
|
||||
# 1018| v0_11(void) = UnmodeledUse : mu*
|
||||
# 1018| v0_12(void) = ExitFunction :
|
||||
|
||||
# 1018| char(* (void Lambda(int, String const&))::(lambda [] type at line 1018, col. 23)::operator char (*)(float)() const)(float)
|
||||
# 1018| Block 0
|
||||
# 1018| v0_0(void) = EnterFunction :
|
||||
# 1018| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1018| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1018| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
# 1018| r0_4(glval<..(*)(..)>) = VariableAddress[#return] :
|
||||
# 1018| r0_5(glval<..(*)(..)>) = FunctionAddress[_FUN] :
|
||||
# 1018| mu0_6(..(*)(..)) = Store : r0_4, r0_5
|
||||
# 1018| r0_7(glval<..(*)(..)>) = VariableAddress[#return] :
|
||||
# 1018| v0_8(void) = ReturnValue : r0_7, mu0_2
|
||||
# 1018| v0_9(void) = UnmodeledUse : mu*
|
||||
# 1018| v0_10(void) = ExitFunction :
|
||||
|
||||
# 1020| char (void Lambda(int, String const&))::(lambda [] type at line 1020, col. 21)::operator()(float) const
|
||||
# 1020| Block 0
|
||||
# 1020| v0_0(void) = EnterFunction :
|
||||
# 1020| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1020| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1020| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
# 1020| r0_4(glval<float>) = VariableAddress[f] :
|
||||
# 1020| mu0_5(float) = InitializeParameter[f] : r0_4
|
||||
# 1020| r0_6(glval<char>) = VariableAddress[#return] :
|
||||
#-----| r0_7(lambda [] type at line 1020, col. 21 *) = CopyValue : r0_3
|
||||
#-----| r0_8(glval<String &>) = FieldAddress[s] : r0_7
|
||||
#-----| r0_9(String &) = Load : r0_8, mu0_2
|
||||
# 1020| r0_10(glval<unknown>) = FunctionAddress[c_str] :
|
||||
# 1020| r0_11(char *) = Call : r0_10, this:r0_9
|
||||
# 1020| mu0_12(unknown) = ^CallSideEffect : mu0_2
|
||||
#-----| r0_13(lambda [] type at line 1020, col. 21 *) = CopyValue : r0_3
|
||||
#-----| r0_14(glval<int &>) = FieldAddress[x] : r0_13
|
||||
#-----| r0_15(int &) = Load : r0_14, mu0_2
|
||||
# 1020| r0_16(int) = Load : r0_15, mu0_2
|
||||
# 1020| r0_17(char *) = PointerAdd[1] : r0_11, r0_16
|
||||
# 1020| r0_18(char) = Load : r0_17, mu0_2
|
||||
# 1020| mu0_19(char) = Store : r0_6, r0_18
|
||||
# 1020| r0_20(glval<char>) = VariableAddress[#return] :
|
||||
# 1020| v0_21(void) = ReturnValue : r0_20, mu0_2
|
||||
# 1020| v0_22(void) = UnmodeledUse : mu*
|
||||
# 1020| v0_23(void) = ExitFunction :
|
||||
|
||||
# 1022| void (void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21)::~<unnamed>()
|
||||
# 1022| Block 0
|
||||
# 1022| v0_0(void) = EnterFunction :
|
||||
# 1022| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1022| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1022| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
#-----| v0_4(void) = NoOp :
|
||||
# 1022| r0_5(glval<String>) = FieldAddress[s] : r0_3
|
||||
# 1022| r0_6(glval<unknown>) = FunctionAddress[~String] :
|
||||
# 1022| v0_7(void) = Call : r0_6, this:r0_5
|
||||
# 1022| mu0_8(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1022| v0_9(void) = ReturnVoid :
|
||||
# 1022| v0_10(void) = UnmodeledUse : mu*
|
||||
# 1022| v0_11(void) = ExitFunction :
|
||||
|
||||
# 1022| char (void Lambda(int, String const&))::(lambda [] type at line 1022, col. 21)::operator()(float) const
|
||||
# 1022| Block 0
|
||||
# 1022| v0_0(void) = EnterFunction :
|
||||
# 1022| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1022| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1022| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
# 1022| r0_4(glval<float>) = VariableAddress[f] :
|
||||
# 1022| mu0_5(float) = InitializeParameter[f] : r0_4
|
||||
# 1022| r0_6(glval<char>) = VariableAddress[#return] :
|
||||
#-----| r0_7(lambda [] type at line 1022, col. 21 *) = CopyValue : r0_3
|
||||
#-----| r0_8(glval<String>) = FieldAddress[s] : r0_7
|
||||
# 1022| r0_9(glval<unknown>) = FunctionAddress[c_str] :
|
||||
# 1022| r0_10(char *) = Call : r0_9, this:r0_8
|
||||
# 1022| mu0_11(unknown) = ^CallSideEffect : mu0_2
|
||||
#-----| r0_12(lambda [] type at line 1022, col. 21 *) = CopyValue : r0_3
|
||||
#-----| r0_13(glval<int>) = FieldAddress[x] : r0_12
|
||||
#-----| r0_14(int) = Load : r0_13, mu0_2
|
||||
# 1022| r0_15(char *) = PointerAdd[1] : r0_10, r0_14
|
||||
# 1022| r0_16(char) = Load : r0_15, mu0_2
|
||||
# 1022| mu0_17(char) = Store : r0_6, r0_16
|
||||
# 1022| r0_18(glval<char>) = VariableAddress[#return] :
|
||||
# 1022| v0_19(void) = ReturnValue : r0_18, mu0_2
|
||||
# 1022| v0_20(void) = UnmodeledUse : mu*
|
||||
# 1022| v0_21(void) = ExitFunction :
|
||||
|
||||
# 1024| char (void Lambda(int, String const&))::(lambda [] type at line 1024, col. 30)::operator()(float) const
|
||||
# 1024| Block 0
|
||||
# 1024| v0_0(void) = EnterFunction :
|
||||
# 1024| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1024| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1024| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
# 1024| r0_4(glval<float>) = VariableAddress[f] :
|
||||
# 1024| mu0_5(float) = InitializeParameter[f] : r0_4
|
||||
# 1024| r0_6(glval<char>) = VariableAddress[#return] :
|
||||
#-----| r0_7(lambda [] type at line 1024, col. 30 *) = CopyValue : r0_3
|
||||
#-----| r0_8(glval<String &>) = FieldAddress[s] : r0_7
|
||||
#-----| r0_9(String &) = Load : r0_8, mu0_2
|
||||
# 1024| r0_10(glval<unknown>) = FunctionAddress[c_str] :
|
||||
# 1024| r0_11(char *) = Call : r0_10, this:r0_9
|
||||
# 1024| mu0_12(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1024| r0_13(int) = Constant[0] :
|
||||
# 1024| r0_14(char *) = PointerAdd[1] : r0_11, r0_13
|
||||
# 1024| r0_15(char) = Load : r0_14, mu0_2
|
||||
# 1024| mu0_16(char) = Store : r0_6, r0_15
|
||||
# 1024| r0_17(glval<char>) = VariableAddress[#return] :
|
||||
# 1024| v0_18(void) = ReturnValue : r0_17, mu0_2
|
||||
# 1024| v0_19(void) = UnmodeledUse : mu*
|
||||
# 1024| v0_20(void) = ExitFunction :
|
||||
|
||||
# 1026| void (void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)&&)
|
||||
# 1026| Block 0
|
||||
# 1026| v0_0(void) = EnterFunction :
|
||||
# 1026| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1026| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1026| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
#-----| r0_4(glval<lambda [] type at line 1026, col. 30 &&>) = VariableAddress[p#0] :
|
||||
#-----| mu0_5(lambda [] type at line 1026, col. 30 &&) = InitializeParameter[p#0] : r0_4
|
||||
# 1026| r0_6(glval<String>) = FieldAddress[s] : r0_3
|
||||
# 1026| r0_7(glval<unknown>) = FunctionAddress[String] :
|
||||
# 1026| v0_8(void) = Call : r0_7, this:r0_6
|
||||
# 1026| mu0_9(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1026| v0_10(void) = NoOp :
|
||||
# 1026| v0_11(void) = ReturnVoid :
|
||||
# 1026| v0_12(void) = UnmodeledUse : mu*
|
||||
# 1026| v0_13(void) = ExitFunction :
|
||||
|
||||
# 1026| void (void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)::~<unnamed>()
|
||||
# 1026| Block 0
|
||||
# 1026| v0_0(void) = EnterFunction :
|
||||
# 1026| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1026| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1026| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
#-----| v0_4(void) = NoOp :
|
||||
# 1026| r0_5(glval<String>) = FieldAddress[s] : r0_3
|
||||
# 1026| r0_6(glval<unknown>) = FunctionAddress[~String] :
|
||||
# 1026| v0_7(void) = Call : r0_6, this:r0_5
|
||||
# 1026| mu0_8(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1026| v0_9(void) = ReturnVoid :
|
||||
# 1026| v0_10(void) = UnmodeledUse : mu*
|
||||
# 1026| v0_11(void) = ExitFunction :
|
||||
|
||||
# 1026| char (void Lambda(int, String const&))::(lambda [] type at line 1026, col. 30)::operator()(float) const
|
||||
# 1026| Block 0
|
||||
# 1026| v0_0(void) = EnterFunction :
|
||||
# 1026| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1026| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1026| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
# 1026| r0_4(glval<float>) = VariableAddress[f] :
|
||||
# 1026| mu0_5(float) = InitializeParameter[f] : r0_4
|
||||
# 1026| r0_6(glval<char>) = VariableAddress[#return] :
|
||||
#-----| r0_7(lambda [] type at line 1026, col. 30 *) = CopyValue : r0_3
|
||||
#-----| r0_8(glval<String>) = FieldAddress[s] : r0_7
|
||||
# 1026| r0_9(glval<unknown>) = FunctionAddress[c_str] :
|
||||
# 1026| r0_10(char *) = Call : r0_9, this:r0_8
|
||||
# 1026| mu0_11(unknown) = ^CallSideEffect : mu0_2
|
||||
# 1026| r0_12(int) = Constant[0] :
|
||||
# 1026| r0_13(char *) = PointerAdd[1] : r0_10, r0_12
|
||||
# 1026| r0_14(char) = Load : r0_13, mu0_2
|
||||
# 1026| mu0_15(char) = Store : r0_6, r0_14
|
||||
# 1026| r0_16(glval<char>) = VariableAddress[#return] :
|
||||
# 1026| v0_17(void) = ReturnValue : r0_16, mu0_2
|
||||
# 1026| v0_18(void) = UnmodeledUse : mu*
|
||||
# 1026| v0_19(void) = ExitFunction :
|
||||
|
||||
# 1028| char (void Lambda(int, String const&))::(lambda [] type at line 1028, col. 32)::operator()(float) const
|
||||
# 1028| Block 0
|
||||
# 1028| v0_0(void) = EnterFunction :
|
||||
# 1028| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1028| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1028| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
# 1028| r0_4(glval<float>) = VariableAddress[f] :
|
||||
# 1028| mu0_5(float) = InitializeParameter[f] : r0_4
|
||||
# 1028| r0_6(glval<char>) = VariableAddress[#return] :
|
||||
#-----| r0_7(lambda [] type at line 1028, col. 32 *) = CopyValue : r0_3
|
||||
#-----| r0_8(glval<String &>) = FieldAddress[s] : r0_7
|
||||
#-----| r0_9(String &) = Load : r0_8, mu0_2
|
||||
# 1028| r0_10(glval<unknown>) = FunctionAddress[c_str] :
|
||||
# 1028| r0_11(char *) = Call : r0_10, this:r0_9
|
||||
# 1028| mu0_12(unknown) = ^CallSideEffect : mu0_2
|
||||
#-----| r0_13(lambda [] type at line 1028, col. 32 *) = CopyValue : r0_3
|
||||
#-----| r0_14(glval<int>) = FieldAddress[x] : r0_13
|
||||
#-----| r0_15(int) = Load : r0_14, mu0_2
|
||||
# 1028| r0_16(char *) = PointerAdd[1] : r0_11, r0_15
|
||||
# 1028| r0_17(char) = Load : r0_16, mu0_2
|
||||
# 1028| mu0_18(char) = Store : r0_6, r0_17
|
||||
# 1028| r0_19(glval<char>) = VariableAddress[#return] :
|
||||
# 1028| v0_20(void) = ReturnValue : r0_19, mu0_2
|
||||
# 1028| v0_21(void) = UnmodeledUse : mu*
|
||||
# 1028| v0_22(void) = ExitFunction :
|
||||
|
||||
# 1031| char (void Lambda(int, String const&))::(lambda [] type at line 1031, col. 23)::operator()(float) const
|
||||
# 1031| Block 0
|
||||
# 1031| v0_0(void) = EnterFunction :
|
||||
# 1031| mu0_1(unknown) = AliasedDefinition :
|
||||
# 1031| mu0_2(unknown) = UnmodeledDefinition :
|
||||
# 1031| r0_3(glval<decltype([...](...){...})>) = InitializeThis :
|
||||
# 1031| r0_4(glval<float>) = VariableAddress[f] :
|
||||
# 1031| mu0_5(float) = InitializeParameter[f] : r0_4
|
||||
# 1031| r0_6(glval<char>) = VariableAddress[#return] :
|
||||
#-----| r0_7(lambda [] type at line 1031, col. 23 *) = CopyValue : r0_3
|
||||
#-----| r0_8(glval<String &>) = FieldAddress[s] : r0_7
|
||||
#-----| r0_9(String &) = Load : r0_8, mu0_2
|
||||
# 1031| r0_10(glval<unknown>) = FunctionAddress[c_str] :
|
||||
# 1031| r0_11(char *) = Call : r0_10, this:r0_9
|
||||
# 1031| mu0_12(unknown) = ^CallSideEffect : mu0_2
|
||||
#-----| r0_13(lambda [] type at line 1031, col. 23 *) = CopyValue : r0_3
|
||||
#-----| r0_14(glval<int>) = FieldAddress[x] : r0_13
|
||||
#-----| r0_15(int) = Load : r0_14, mu0_2
|
||||
#-----| r0_16(lambda [] type at line 1031, col. 23 *) = CopyValue : r0_3
|
||||
# 1031| r0_17(glval<int>) = FieldAddress[i] : r0_16
|
||||
# 1031| r0_18(int) = Load : r0_17, mu0_2
|
||||
# 1031| r0_19(int) = Add : r0_15, r0_18
|
||||
#-----| r0_20(lambda [] type at line 1031, col. 23 *) = CopyValue : r0_3
|
||||
# 1031| r0_21(glval<int &>) = FieldAddress[j] : r0_20
|
||||
# 1031| r0_22(int &) = Load : r0_21, mu0_2
|
||||
# 1031| r0_23(int) = Load : r0_22, mu0_2
|
||||
# 1031| r0_24(int) = Sub : r0_19, r0_23
|
||||
# 1031| r0_25(char *) = PointerAdd[1] : r0_11, r0_24
|
||||
# 1031| r0_26(char) = Load : r0_25, mu0_2
|
||||
# 1031| mu0_27(char) = Store : r0_6, r0_26
|
||||
# 1031| r0_28(glval<char>) = VariableAddress[#return] :
|
||||
# 1031| v0_29(void) = ReturnValue : r0_28, mu0_2
|
||||
# 1031| v0_30(void) = UnmodeledUse : mu*
|
||||
# 1031| v0_31(void) = ExitFunction :
|
||||
|
||||
@@ -371,3 +371,56 @@ void test12(bool cond)
|
||||
free(z); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
class MyBuffer13
|
||||
{
|
||||
public:
|
||||
MyBuffer13(int size)
|
||||
{
|
||||
buffer = (char *)malloc(size * sizeof(char));
|
||||
}
|
||||
|
||||
~MyBuffer13()
|
||||
{
|
||||
free(buffer); // GOOD
|
||||
}
|
||||
|
||||
char *getBuffer() // note: this should not be considered an allocation function
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
class MyPointer13
|
||||
{
|
||||
public:
|
||||
MyPointer13(char *_pointer) : pointer(_pointer)
|
||||
{
|
||||
}
|
||||
|
||||
MyPointer13(MyBuffer13 &buffer) : pointer(buffer.getBuffer())
|
||||
{
|
||||
}
|
||||
|
||||
char *getPointer() // note: this should not be considered an allocation function
|
||||
{
|
||||
return pointer;
|
||||
}
|
||||
|
||||
private:
|
||||
char *pointer;
|
||||
};
|
||||
|
||||
void test13()
|
||||
{
|
||||
MyBuffer13 myBuffer(100);
|
||||
MyPointer13 myPointer2(myBuffer);
|
||||
MyPointer13 myPointer3(new char[100]);
|
||||
|
||||
delete myPointer3.getPointer(); // GOOD
|
||||
}
|
||||
|
||||
@@ -265,3 +265,30 @@ int negative_zero(double dbl) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nan(double x) {
|
||||
if (x < 0.0) {
|
||||
return 100;
|
||||
}
|
||||
else if (x >= 0.0) { // GOOD [x could be NaN]
|
||||
return 200;
|
||||
}
|
||||
else {
|
||||
return 300;
|
||||
}
|
||||
}
|
||||
|
||||
int nan2(double x) {
|
||||
if (x == x) {
|
||||
// If x compares with anything at all, it's not NaN
|
||||
if (x < 0.0) {
|
||||
return 100;
|
||||
}
|
||||
else if (x >= 0.0) { // BAD [Always true]
|
||||
return 200;
|
||||
}
|
||||
else {
|
||||
return 300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,5 +32,6 @@
|
||||
| PointlessComparison.c:129:12:129:16 | ... > ... | Comparison is always false because a <= 3. |
|
||||
| PointlessComparison.c:197:7:197:11 | ... < ... | Comparison is always false because x >= 0. |
|
||||
| PointlessComparison.c:264:12:264:22 | ... >= ... | Comparison is always true because dbl >= 0 and -0 >= - .... |
|
||||
| PointlessComparison.c:287:14:287:21 | ... >= ... | Comparison is always true because x >= 0. |
|
||||
| RegressionTests.cpp:57:7:57:22 | ... <= ... | Comparison is always true because * ... <= 4294967295. |
|
||||
| Templates.cpp:9:10:9:24 | ... <= ... | Comparison is always true because local <= 32767. |
|
||||
|
||||
@@ -66,3 +66,17 @@ int regression_test_01(unsigned long bb) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int containsIfDef(int x) {
|
||||
int result = 0;
|
||||
if (x > 0) {
|
||||
result = 1;
|
||||
}
|
||||
#if _CONDITION
|
||||
if (x < 0) {
|
||||
result = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return result >= 0;
|
||||
}
|
||||
@@ -40,4 +40,6 @@ void test(int i, const char *str)
|
||||
printf("%2$.*4$f", 0, num, 0, precision); // GOOD [FALSE POSITIVE]
|
||||
printf("%2$.*4$f", num, 0, precision); // BAD (too few format arguments) [INCORRECT MESSAGE]
|
||||
}
|
||||
|
||||
printf("%@ %i %i", 1, 2); // GOOD
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
| tests.cpp:18:15:18:22 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| tests.cpp:19:15:19:22 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:25:17:25:23 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' |
|
||||
| tests.cpp:26:17:26:24 | Hello | This argument should be of type 'wchar_t *' but is of type 'char16_t *' |
|
||||
| tests.cpp:30:17:30:24 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| tests.cpp:31:17:31:24 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:33:36:33:42 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
|
||||
| tests.cpp:35:36:35:43 | Hello | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:38:36:38:43 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| tests.cpp:39:36:39:43 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:26:17:26:24 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| tests.cpp:27:17:27:24 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:29:17:29:23 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' |
|
||||
| tests.cpp:30:17:30:24 | Hello | This argument should be of type 'wchar_t *' but is of type 'char16_t *' |
|
||||
| tests.cpp:34:36:34:43 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| tests.cpp:35:36:35:43 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:37:36:37:42 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
|
||||
| tests.cpp:39:36:39:43 | Hello | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:42:37:42:44 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| tests.cpp:43:37:43:44 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:45:37:45:43 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
|
||||
| tests.cpp:47:37:47:44 | Hello | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
| tests.cpp:8:5:8:10 | printf | char | char16_t, wchar_t | char16_t, wchar_t |
|
||||
| tests.cpp:9:5:9:11 | wprintf | wchar_t | char | wchar_t |
|
||||
| tests.cpp:10:5:10:12 | swprintf | char16_t | char | char16_t |
|
||||
| tests.cpp:8:5:8:10 | printf | char | char | char16_t, wchar_t | char16_t, wchar_t |
|
||||
| tests.cpp:9:5:9:11 | wprintf | wchar_t | char | wchar_t | wchar_t |
|
||||
| tests.cpp:10:5:10:12 | swprintf | char16_t | char | char16_t | char16_t |
|
||||
|
||||
@@ -3,6 +3,7 @@ import cpp
|
||||
from FormattingFunction f
|
||||
select
|
||||
f,
|
||||
concat(f.getFormatCharType().toString(), ", "),
|
||||
concat(f.getDefaultCharType().toString(), ", "),
|
||||
concat(f.getNonDefaultCharType().toString(), ", "),
|
||||
concat(f.getWideCharType().toString(), ", ")
|
||||
|
||||
@@ -22,19 +22,27 @@ void tests() {
|
||||
printf("%S", u"Hello"); // GOOD
|
||||
printf("%S", L"Hello"); // GOOD
|
||||
|
||||
wprintf(L"%s", "Hello"); // BAD: expecting wchar_t
|
||||
wprintf(L"%s", u"Hello"); // BAD: expecting wchar_t
|
||||
wprintf(L"%s", L"Hello"); // GOOD
|
||||
wprintf(L"%s", "Hello"); // GOOD
|
||||
wprintf(L"%s", u"Hello"); // BAD: expecting char
|
||||
wprintf(L"%s", L"Hello"); // BAD: expecting char
|
||||
|
||||
wprintf(L"%S", "Hello"); // GOOD
|
||||
wprintf(L"%S", u"Hello"); // BAD: expecting char
|
||||
wprintf(L"%S", L"Hello"); // BAD: expecting char
|
||||
wprintf(L"%S", "Hello"); // BAD: expecting wchar_t
|
||||
wprintf(L"%S", u"Hello"); // BAD: expecting wchar_t
|
||||
wprintf(L"%S", L"Hello"); // GOOD
|
||||
|
||||
swprintf(buffer, BUF_SIZE, u"%s", "Hello"); // BAD: expecting char16_t
|
||||
swprintf(buffer, BUF_SIZE, u"%s", u"Hello"); // GOOD
|
||||
swprintf(buffer, BUF_SIZE, u"%s", L"Hello"); // BAD: expecting char16_t
|
||||
swprintf(buffer, BUF_SIZE, u"%s", "Hello"); // GOOD
|
||||
swprintf(buffer, BUF_SIZE, u"%s", u"Hello"); // BAD: expecting char
|
||||
swprintf(buffer, BUF_SIZE, u"%s", L"Hello"); // BAD: expecting char
|
||||
|
||||
swprintf(buffer, BUF_SIZE, u"%S", "Hello"); // GOOD
|
||||
swprintf(buffer, BUF_SIZE, u"%S", u"Hello"); // BAD: expecting char
|
||||
swprintf(buffer, BUF_SIZE, u"%S", L"Hello"); // BAD: expecting char
|
||||
swprintf(buffer, BUF_SIZE, u"%S", "Hello"); // BAD: expecting char16_t
|
||||
swprintf(buffer, BUF_SIZE, u"%S", u"Hello"); // GOOD
|
||||
swprintf(buffer, BUF_SIZE, u"%S", L"Hello"); // BAD: expecting char16_t
|
||||
|
||||
swprintf(buffer, BUF_SIZE, u"%hs", "Hello"); // GOOD
|
||||
swprintf(buffer, BUF_SIZE, u"%hs", u"Hello"); // BAD: expecting char
|
||||
swprintf(buffer, BUF_SIZE, u"%hs", L"Hello"); // BAD: expecting char
|
||||
|
||||
swprintf(buffer, BUF_SIZE, u"%ls", "Hello"); // BAD: expecting char16_t
|
||||
swprintf(buffer, BUF_SIZE, u"%ls", u"Hello"); // GOOD
|
||||
swprintf(buffer, BUF_SIZE, u"%ls", L"Hello"); // BAD: expecting char16_t
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
| printf.cpp:33:31:33:37 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| printf.cpp:45:29:45:35 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| printf.cpp:52:29:52:35 | test | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
| printf.cpp:15:5:15:12 | swprintf | char16_t | char | char16_t |
|
||||
| printf.cpp:15:5:15:12 | swprintf | char | char16_t | char16_t |
|
||||
| printf.cpp:26:5:26:11 | sprintf | char | char16_t | char16_t |
|
||||
|
||||
@@ -30,7 +30,7 @@ int sprintf(char *dest, char *format, ...);
|
||||
void test1() {
|
||||
WCHAR string[20];
|
||||
|
||||
swprintf(string, u"test %s", u"test"); // GOOD
|
||||
swprintf(string, u"test %s", u"test"); // BAD: `char16_t` string parameter read as `char` string
|
||||
}
|
||||
|
||||
void test2() {
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:47:19:47:21 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:126:18:126:19 | wc | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| printf1.h:127:18:127:18 | c | This argument should be of type 'wchar_t *' but is of type 'char *' |
|
||||
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
|
||||
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
|
||||
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
| common.h:12:12:12:17 | printf | char | wchar_t | wchar_t |
|
||||
| common.h:15:12:15:18 | wprintf | wchar_t | char | wchar_t |
|
||||
| common.h:15:12:15:18 | wprintf | char | wchar_t | wchar_t |
|
||||
| format.h:4:13:4:17 | error | char | wchar_t | wchar_t |
|
||||
| real_world.h:8:12:8:18 | fprintf | char | wchar_t | wchar_t |
|
||||
| real_world.h:33:6:33:12 | msg_out | char | wchar_t | wchar_t |
|
||||
|
||||
@@ -119,3 +119,11 @@ void test_chars(char c, wchar_t wc, wint_t wt)
|
||||
wprintf(L"%C", wc); // GOOD (converts to wint_t)
|
||||
wprintf(L"%C", wt); // GOOD
|
||||
}
|
||||
|
||||
void test_ws(char *c, wchar_t *wc)
|
||||
{
|
||||
wprintf(L"%s", c); // GOOD
|
||||
wprintf(L"%s", wc); // BAD
|
||||
wprintf(L"%S", c); // BAD
|
||||
wprintf(L"%S", wc); // GOOD
|
||||
}
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
| printf1.h:74:19:74:22 | C_ST | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
|
||||
| printf1.h:75:19:75:28 | sizeof(<expr>) | This argument should be of type 'ssize_t' but is of type 'unsigned long long' |
|
||||
| printf1.h:84:23:84:35 | ... - ... | This argument should be of type 'ssize_t' but is of type 'long long' |
|
||||
| printf1.h:125:18:125:18 | c | This argument should be of type '__wchar_t *' but is of type 'char *' |
|
||||
| printf1.h:128:18:128:19 | wc | This argument should be of type 'char *' but is of type '__wchar_t *' |
|
||||
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
|
||||
| real_world.h:62:22:62:23 | & ... | This argument should be of type 'short *' but is of type 'int *' |
|
||||
| real_world.h:63:22:63:24 | & ... | This argument should be of type 'short *' but is of type 'unsigned int *' |
|
||||
| real_world.h:64:22:64:24 | & ... | This argument should be of type 'short *' but is of type 'signed int *' |
|
||||
| wide_string.h:25:18:25:20 | c | This argument should be of type 'char' but is of type 'char *' |
|
||||
| wide_string.h:29:19:29:22 | c | This argument should be of type 'wchar_t' but is of type '__wchar_t *' |
|
||||
|
||||
@@ -119,3 +119,11 @@ void test_chars(char c, wchar_t wc, wint_t wt)
|
||||
wprintf(L"%C", wc); // BAD [NOT DETECTED]
|
||||
wprintf(L"%C", wt); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_ws(char *c, wchar_t *wc, wint_t *wt)
|
||||
{
|
||||
wprintf(L"%s", c); // BAD
|
||||
wprintf(L"%s", wc); // GOOD
|
||||
wprintf(L"%S", c); // GOOD
|
||||
wprintf(L"%S", wc); // BAD
|
||||
}
|
||||
|
||||
@@ -26,5 +26,5 @@ void test_wchar4(char c, const char cc, wchar_t wc, const wchar_t wcc) {
|
||||
printf("%wc", wc); // GOOD
|
||||
printf("%wc", wcc); // GOOD
|
||||
printf("%wc", L'c'); // GOOD
|
||||
printf("%wc", L"c"); // BAD [NOT DETECTED]
|
||||
printf("%wc", L"c"); // BAD
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
| test.cpp:42:31:42:36 | call to malloc | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:48:25:48:30 | call to malloc | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:49:17:49:30 | new[] | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:52:35:52:60 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
| test.cpp:55:11:55:24 | new[] | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) |
|
||||
@@ -0,0 +1 @@
|
||||
Security/CWE/CWE-190/TaintedAllocationSize.ql
|
||||
@@ -0,0 +1,58 @@
|
||||
// Associated with CWE-190: Integer Overflow or Wraparound. http://cwe.mitre.org/data/definitions/190.html
|
||||
|
||||
typedef unsigned long size_t;
|
||||
typedef struct {} FILE;
|
||||
|
||||
void *malloc(size_t size);
|
||||
void *realloc(void *ptr, size_t size);
|
||||
int atoi(const char *nptr);
|
||||
|
||||
struct MyStruct
|
||||
{
|
||||
char data[256];
|
||||
};
|
||||
|
||||
namespace std
|
||||
{
|
||||
template<class charT> struct char_traits;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
typedef basic_istream<char> istream;
|
||||
|
||||
extern istream cin;
|
||||
}
|
||||
|
||||
int getTainted() {
|
||||
int i;
|
||||
|
||||
std::cin >> i;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int tainted = atoi(argv[1]);
|
||||
|
||||
MyStruct *arr1 = (MyStruct *)malloc(sizeof(MyStruct)); // GOOD
|
||||
MyStruct *arr2 = (MyStruct *)malloc(tainted); // BAD
|
||||
MyStruct *arr3 = (MyStruct *)malloc(tainted * sizeof(MyStruct)); // BAD
|
||||
MyStruct *arr4 = (MyStruct *)malloc(getTainted() * sizeof(MyStruct)); // BAD [NOT DETECTED]
|
||||
MyStruct *arr5 = (MyStruct *)malloc(sizeof(MyStruct) + tainted); // BAD [NOT DETECTED]
|
||||
|
||||
int size = tainted * 8;
|
||||
char *chars1 = (char *)malloc(size); // BAD
|
||||
char *chars2 = new char[size]; // BAD
|
||||
char *chars3 = new char[8]; // GOOD
|
||||
|
||||
arr1 = (MyStruct *)realloc(arr1, sizeof(MyStruct) * tainted); // BAD
|
||||
|
||||
size = 8;
|
||||
chars3 = new char[size]; // GOOD [FALSE POSITIVE]
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -8,6 +8,3 @@
|
||||
| test.c:14:15:14:28 | maxConnections | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:11:29:11:32 | argv | User-provided value |
|
||||
| test.c:44:7:44:10 | len2 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:41:17:41:20 | argv | User-provided value |
|
||||
| test.c:54:7:54:10 | len3 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:51:17:51:20 | argv | User-provided value |
|
||||
| test.c:74:7:74:10 | len5 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:71:19:71:22 | argv | User-provided value |
|
||||
| test.c:84:7:84:10 | len6 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:81:19:81:22 | argv | User-provided value |
|
||||
| test.c:94:7:94:10 | len7 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:91:19:91:22 | argv | User-provided value |
|
||||
|
||||
@@ -71,7 +71,7 @@ int main(int argc, char** argv) {
|
||||
len5 = strlen(argv[1]);
|
||||
while (len5)
|
||||
{
|
||||
len5--; // GOOD: can't underflow [FALSE POSITIVE]
|
||||
len5--; // GOOD: can't underflow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ int main(int argc, char** argv) {
|
||||
len6 = strlen(argv[1]);
|
||||
while (len6 != 0)
|
||||
{
|
||||
len6--; // GOOD: can't underflow [FALSE POSITIVE]
|
||||
len6--; // GOOD: can't underflow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ int main(int argc, char** argv) {
|
||||
len7 = strlen(argv[1]);
|
||||
while ((len7) && (1))
|
||||
{
|
||||
len7--; // GOOD: can't underflow [FALSE POSITIVE]
|
||||
len7--; // GOOD: can't underflow
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
struct QObject {
|
||||
QObject(QObject *parent);
|
||||
void setParent(QObject *parent);
|
||||
};
|
||||
|
||||
struct DerivedFromQObject : public QObject {
|
||||
DerivedFromQObject(QObject *parent);
|
||||
};
|
||||
|
||||
class MyQtUser {
|
||||
DerivedFromQObject *noParent, *constructorParent, *laterParent;
|
||||
|
||||
MyQtUser(QObject *parent) {
|
||||
// This object sets its parent pointer to null and thus must be deleted
|
||||
// manually.
|
||||
noParent = new DerivedFromQObject(nullptr); // BAD [NOT DETECTED]
|
||||
|
||||
// This object does not need to be deleted because it will be deleted by
|
||||
// its parent object when the time is right.
|
||||
constructorParent = new DerivedFromQObject(parent); // GOOD
|
||||
|
||||
laterParent = new DerivedFromQObject(nullptr); // GOOD
|
||||
laterParent->setParent(parent);
|
||||
}
|
||||
};
|
||||
@@ -73,3 +73,39 @@ public:
|
||||
|
||||
int *a, *b, *c;
|
||||
};
|
||||
|
||||
class MyClass7
|
||||
{
|
||||
public:
|
||||
MyClass7()
|
||||
{
|
||||
}
|
||||
|
||||
bool open()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
// ...
|
||||
}
|
||||
};
|
||||
|
||||
class myClass7Test
|
||||
{
|
||||
public:
|
||||
myClass7Test()
|
||||
{
|
||||
success = mc7.open(); // GOOD
|
||||
}
|
||||
|
||||
~myClass7Test()
|
||||
{
|
||||
mc7.close();
|
||||
}
|
||||
|
||||
private:
|
||||
MyClass7 mc7;
|
||||
bool success;
|
||||
};
|
||||
|
||||
@@ -37,19 +37,21 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
if (!def.PublicKey.IsNil)
|
||||
assemblyName.SetPublicKey(cx.mdReader.GetBlobBytes(def.PublicKey));
|
||||
|
||||
ShortId = cx.GetId(assemblyName.FullName) + "#file:///" + cx.assemblyPath.Replace("\\", "/");
|
||||
ShortId = cx.GetId(FullName) + "#file:///" + cx.assemblyPath.Replace("\\", "/");
|
||||
|
||||
file = new File(cx, cx.assemblyPath);
|
||||
}
|
||||
|
||||
static readonly Id suffix = new StringId(";assembly");
|
||||
|
||||
string FullName => assemblyName.GetPublicKey() is null ? assemblyName.FullName + ", PublicKeyToken=null" : assemblyName.FullName;
|
||||
|
||||
public override IEnumerable<IExtractionProduct> Contents
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return file;
|
||||
yield return Tuples.assemblies(this, file, assemblyName.FullName, assemblyName.Name, assemblyName.Version.ToString());
|
||||
yield return Tuples.assemblies(this, file, FullName, assemblyName.Name, assemblyName.Version.ToString());
|
||||
|
||||
if (cx.pdb != null)
|
||||
{
|
||||
|
||||
@@ -139,6 +139,7 @@ namespace Semmle.BuildAnalyser
|
||||
* loading the same assembly from different locations.
|
||||
*/
|
||||
using (var pereader = new System.Reflection.PortableExecutable.PEReader(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)))
|
||||
using (var sha1 = new SHA1CryptoServiceProvider())
|
||||
{
|
||||
var metadata = pereader.GetMetadata();
|
||||
unsafe
|
||||
@@ -173,7 +174,5 @@ namespace Semmle.BuildAnalyser
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static readonly SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,9 +51,12 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
int child = 0;
|
||||
foreach (var arg in syntax.ArgumentList.Arguments)
|
||||
{
|
||||
Expression.Create(cx, arg.Expression, this, child++);
|
||||
var expr = Expression.Create(cx, arg.Expression, this, child++);
|
||||
if (!(arg.NameEquals is null))
|
||||
{
|
||||
cx.Emit(Tuples.expr_argument_name(expr, arg.NameEquals.Name.Identifier.Text));
|
||||
}
|
||||
}
|
||||
// !! Handle named arguments
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -66,14 +69,6 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExtractAttributes(Context cx, IEnumerable<AttributeListSyntax> attributes, IEntity entity)
|
||||
{
|
||||
foreach (var attributeSyntax in attributes.SelectMany(l => l.Attributes))
|
||||
{
|
||||
new Attribute(cx, attributeSyntax, entity);
|
||||
}
|
||||
}
|
||||
|
||||
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,18 +49,28 @@ predicate isTriedAgainstException(ControlFlowElement cfe, ExceptionClass ec) {
|
||||
)
|
||||
}
|
||||
|
||||
private class DisposeCall extends MethodCall {
|
||||
DisposeCall() { this.getTarget() instanceof DisposeMethod }
|
||||
}
|
||||
|
||||
private predicate reachesDisposeCall(DisposeCall disposeCall, DataFlow::Node node) {
|
||||
DataFlow::localFlowStep(node, DataFlow::exprNode(disposeCall.getQualifier()))
|
||||
or
|
||||
exists(DataFlow::Node mid | reachesDisposeCall(disposeCall, mid) |
|
||||
DataFlow::localFlowStep(node, mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `disposeCall` disposes the object created by `disposableCreation`.
|
||||
*/
|
||||
predicate disposeReachableFromDisposableCreation(MethodCall disposeCall, Expr disposableCreation) {
|
||||
predicate disposeReachableFromDisposableCreation(DisposeCall disposeCall, Expr disposableCreation) {
|
||||
// The qualifier of the Dispose call flows from something that introduced a disposable into scope
|
||||
(
|
||||
disposableCreation instanceof LocalScopeDisposableCreation or
|
||||
disposableCreation instanceof MethodCall
|
||||
) and
|
||||
DataFlow::localFlowStep+(DataFlow::exprNode(disposableCreation),
|
||||
DataFlow::exprNode(disposeCall.getQualifier())) and
|
||||
disposeCall.getTarget() instanceof DisposeMethod
|
||||
reachesDisposeCall(disposeCall, DataFlow::exprNode(disposableCreation))
|
||||
}
|
||||
|
||||
class MethodCallThatMayThrow extends MethodCall {
|
||||
@@ -73,7 +83,7 @@ ControlFlowElement getACatchOrFinallyClauseChild() {
|
||||
result = getACatchOrFinallyClauseChild().getAChild()
|
||||
}
|
||||
|
||||
from MethodCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows
|
||||
from DisposeCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows
|
||||
where
|
||||
disposeReachableFromDisposableCreation(disposeCall, disposableCreation) and
|
||||
// The dispose call is not, itself, within a dispose method.
|
||||
|
||||
@@ -1,36 +1,33 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.DataFlow
|
||||
|
||||
import csharp
|
||||
|
||||
class ImplementsICryptoTransform extends Class {
|
||||
ImplementsICryptoTransform() {
|
||||
this.getABaseType*().hasQualifiedName("System.Security.Cryptography", "ICryptoTransform")
|
||||
}
|
||||
}
|
||||
|
||||
predicate usesICryptoTransformType( ValueOrRefType t ) {
|
||||
exists( ImplementsICryptoTransform ict |
|
||||
ict = t
|
||||
or usesICryptoTransformType( t.getAChild() )
|
||||
predicate usesICryptoTransformType(ValueOrRefType t) {
|
||||
exists(ImplementsICryptoTransform ict |
|
||||
ict = t or
|
||||
usesICryptoTransformType(t.getAChild())
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasICryptoTransformMember( Class c) {
|
||||
predicate hasICryptoTransformMember(Class c) {
|
||||
c.getAField().getType() instanceof UsesICryptoTransform
|
||||
}
|
||||
|
||||
class UsesICryptoTransform extends Class {
|
||||
UsesICryptoTransform() {
|
||||
usesICryptoTransformType(this) or hasICryptoTransformMember(this)
|
||||
}
|
||||
UsesICryptoTransform() { usesICryptoTransformType(this) or hasICryptoTransformMember(this) }
|
||||
}
|
||||
|
||||
class LambdaCapturingICryptoTransformSource extends DataFlow::Node {
|
||||
LambdaCapturingICryptoTransformSource() {
|
||||
exists( LambdaExpr l, LocalScopeVariable lsvar, UsesICryptoTransform ict |
|
||||
l = this.asExpr() |
|
||||
ict = lsvar.getType()
|
||||
and lsvar.getACapturingCallable() = l
|
||||
exists(LambdaExpr l, LocalScopeVariable lsvar, UsesICryptoTransform ict | l = this.asExpr() |
|
||||
ict = lsvar.getType() and
|
||||
lsvar.getACapturingCallable() = l
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,25 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.DataFlow
|
||||
|
||||
abstract class ParallelSink extends DataFlow::Node {
|
||||
}
|
||||
abstract class ParallelSink extends DataFlow::Node { }
|
||||
|
||||
class LambdaParallelSink extends ParallelSink {
|
||||
LambdaParallelSink() {
|
||||
exists( Class c, Method m, MethodCall mc, Expr e |
|
||||
e = this.asExpr() |
|
||||
c.getABaseType*().hasQualifiedName("System.Threading.Tasks", "Parallel")
|
||||
and c.getAMethod() = m
|
||||
and m.getName() = "Invoke"
|
||||
and m.getACall() = mc
|
||||
and mc.getAnArgument() = e
|
||||
)
|
||||
exists(Class c, Method m, MethodCall mc, Expr e | e = this.asExpr() |
|
||||
c.getABaseType*().hasQualifiedName("System.Threading.Tasks", "Parallel") and
|
||||
c.getAMethod() = m and
|
||||
m.getName() = "Invoke" and
|
||||
m.getACall() = mc and
|
||||
mc.getAnArgument() = e
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class ThreadStartParallelSink extends ParallelSink {
|
||||
ThreadStartParallelSink() {
|
||||
exists( DelegateCreation dc, Expr e |
|
||||
e = this.asExpr() |
|
||||
dc.getArgument() = e
|
||||
and dc.getType().getName().matches("%Start")
|
||||
exists(DelegateCreation dc, Expr e | e = this.asExpr() |
|
||||
dc.getArgument() = e and
|
||||
dc.getType().getName().matches("%Start")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,7 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Classes that implement <code>System.Security.Cryptography.ICryptoTransform</code> are not thread safe.</p>
|
||||
<p>This problem is caused by the way these classes are implemented using Microsoft CAPI/CNG patterns.</p>
|
||||
<p>For example, when a hash class implements this interface, there would typically be an instance-specific hash object created (for example using <code>BCryptCreateHash</code> function). This object can be called multiple times to add data to the hash (for example <code>BCryptHashData</code>). Finally, a function is called that finishes the hash and returns the data (for example <code>BCryptFinishHash</code>).</p>
|
||||
<p>Allowing the same hash object to be called with data from multiple threads before calling the finish function could potentially lead to incorrect results.</p>
|
||||
<p>For example, if you have multiple threads hashing <code>"abc"</code> on a static hash object, you may occasionally obtain the results (incorrectly) for hashing <code>"abcabc"</code>, or face other unexpected behavior.</p>
|
||||
<p>It is very unlikely somebody outside Microsoft would write a class that implements <code>ICryptoTransform</code>, and even if they do, it is likely that they will follow the same common pattern as the existing classes implementing this interface.</p>
|
||||
<p>Any object that implements <code>System.Security.Cryptography.ICryptoTransform</code> should not be used in concurrent threads as the instance members of such object are also not thread safe.</p>
|
||||
<p>Potential problems may not be evident at first, but can range from explicit errors such as exceptions, to incorrect results when sharing an instance of such an object in multiple threads.</p>
|
||||
|
||||
<include src="ThreadUnsafeICryptoTransformOverview.qhelp" />
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>If the object is shared across instances, you should consider changing the code to use a non-static object of type <code>System.Security.Cryptography.ICryptoTransform</code> instead.</p>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Class defines a field that uses an ICryptoTransform class in a way that would be unsafe for concurrent threads.
|
||||
* @description The class has a field that directly or indirectly make use of a static System.Security.Cryptography.ICryptoTransform object.
|
||||
* Using this an instance of this class in concurrent threads is dangerous as it may not only result in an error,
|
||||
* Using this an instance of this class in concurrent threads is dangerous as it may not only result in an error,
|
||||
* but under some circumstances may also result in incorrect results.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
@@ -20,66 +20,65 @@ class ICryptoTransform extends Class {
|
||||
}
|
||||
}
|
||||
|
||||
predicate usesICryptoTransformType( Type t ) {
|
||||
exists( ICryptoTransform ict |
|
||||
ict = t
|
||||
or usesICryptoTransformType( t.getAChild() )
|
||||
predicate usesICryptoTransformType(Type t) {
|
||||
exists(ICryptoTransform ict |
|
||||
ict = t or
|
||||
usesICryptoTransformType(t.getAChild())
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasICryptoTransformMember( Class c) {
|
||||
exists( Field f |
|
||||
f = c.getAMember()
|
||||
and (
|
||||
exists( ICryptoTransform ict | ict = f.getType() )
|
||||
or hasICryptoTransformMember(f.getType())
|
||||
or usesICryptoTransformType(f.getType())
|
||||
predicate hasICryptoTransformMember(Class c) {
|
||||
exists(Field f |
|
||||
f = c.getAMember() and
|
||||
(
|
||||
exists(ICryptoTransform ict | ict = f.getType()) or
|
||||
hasICryptoTransformMember(f.getType()) or
|
||||
usesICryptoTransformType(f.getType())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasICryptoTransformStaticMemberNested( Class c ) {
|
||||
exists( Field f |
|
||||
f = c.getAMember() |
|
||||
hasICryptoTransformStaticMemberNested( f.getType() )
|
||||
or (
|
||||
f.isStatic() and hasICryptoTransformMember(f.getType())
|
||||
and not exists( Attribute a
|
||||
| a = f.getAnAttribute() |
|
||||
a.getType().getQualifiedName() = "System.ThreadStaticAttribute"
|
||||
predicate hasICryptoTransformStaticMemberNested(Class c) {
|
||||
exists(Field f | f = c.getAMember() |
|
||||
hasICryptoTransformStaticMemberNested(f.getType())
|
||||
or
|
||||
f.isStatic() and
|
||||
hasICryptoTransformMember(f.getType()) and
|
||||
not exists(Attribute a | a = f.getAnAttribute() |
|
||||
a.getType().getQualifiedName() = "System.ThreadStaticAttribute"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasICryptoTransformStaticMember(Class c, string msg) {
|
||||
exists(Field f |
|
||||
f = c.getAMember*() and
|
||||
f.isStatic() and
|
||||
not exists(Attribute a |
|
||||
a = f.getAnAttribute() and
|
||||
a.getType().getQualifiedName() = "System.ThreadStaticAttribute"
|
||||
) and
|
||||
(
|
||||
exists(ICryptoTransform ict |
|
||||
ict = f.getType() and
|
||||
msg = "Static field " + f + " of type " + f.getType() +
|
||||
", implements 'System.Security.Cryptography.ICryptoTransform', but it does not have an attribute [ThreadStatic]. The usage of this class is unsafe for concurrent threads."
|
||||
)
|
||||
or
|
||||
not exists(ICryptoTransform ict | ict = f.getType()) and // Avoid dup messages
|
||||
exists(Type t | t = f.getType() |
|
||||
usesICryptoTransformType(t) and
|
||||
msg = "Static field " + f + " of type " + f.getType() +
|
||||
" makes usage of 'System.Security.Cryptography.ICryptoTransform', but it does not have an attribute [ThreadStatic]. The usage of this class is unsafe for concurrent threads."
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
hasICryptoTransformStaticMemberNested(c) and
|
||||
msg = "Class" + c +
|
||||
" implementation depends on a static object of type 'System.Security.Cryptography.ICryptoTransform' in a way that is unsafe for concurrent threads."
|
||||
}
|
||||
|
||||
predicate hasICryptoTransformStaticMember( Class c, string msg) {
|
||||
exists( Field f |
|
||||
f = c.getAMember*()
|
||||
and f.isStatic()
|
||||
and not exists( Attribute a
|
||||
| a = f.getAnAttribute()
|
||||
and a.getType().getQualifiedName() = "System.ThreadStaticAttribute"
|
||||
)
|
||||
and (
|
||||
exists( ICryptoTransform ict |
|
||||
ict = f.getType()
|
||||
and msg = "Static field " + f + " of type " + f.getType() + ", implements 'System.Security.Cryptography.ICryptoTransform', but it does not have an attribute [ThreadStatic]. The usage of this class is unsafe for concurrent threads."
|
||||
)
|
||||
or
|
||||
(
|
||||
not exists( ICryptoTransform ict | ict = f.getType() ) // Avoid dup messages
|
||||
and exists( Type t | t = f.getType() |
|
||||
usesICryptoTransformType(t)
|
||||
and msg = "Static field " + f + " of type " + f.getType() + " makes usage of 'System.Security.Cryptography.ICryptoTransform', but it does not have an attribute [ThreadStatic]. The usage of this class is unsafe for concurrent threads."
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
or ( hasICryptoTransformStaticMemberNested(c)
|
||||
and msg = "Class" + c + " implementation depends on a static object of type 'System.Security.Cryptography.ICryptoTransform' in a way that is unsafe for concurrent threads."
|
||||
)
|
||||
}
|
||||
|
||||
from Class c , string s
|
||||
where hasICryptoTransformStaticMember(c, s)
|
||||
from Class c, string s
|
||||
where hasICryptoTransformStaticMember(c, s)
|
||||
select c, s
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<include src="ThreadUnsafeICryptoTransform.qhelp" />
|
||||
|
||||
<include src="ThreadUnsafeICryptoTransformOverview.qhelp" />
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Create new instances of the object that implements or has a field of type <code>System.Security.Cryptography.ICryptoTransform</code> to avoid sharing it accross multiple threads.</p>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* @name Potential usage of an object implementing ICryptoTransform class in a way that would be unsafe for concurrent threads.
|
||||
* @description An instance of a class that either implements or has a field of type System.Security.Cryptography.ICryptoTransform is being captured by a lambda,
|
||||
* @description An instance of a class that either implements or has a field of type System.Security.Cryptography.ICryptoTransform is being captured by a lambda,
|
||||
* and used in what seems to be a thread initialization method.
|
||||
* Using an instance of this class in concurrent threads is dangerous as it may not only result in an error,
|
||||
* Using an instance of this class in concurrent threads is dangerous as it may not only result in an error,
|
||||
* but under some circumstances may also result in incorrect results.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
@@ -18,20 +18,20 @@ import semmle.code.csharp.dataflow.DataFlow
|
||||
import ParallelSink
|
||||
import ICryptoTransform
|
||||
|
||||
class NotThreadSafeCryptoUsageIntoParallelInvokeConfig extends TaintTracking::Configuration {
|
||||
NotThreadSafeCryptoUsageIntoParallelInvokeConfig() { this = "NotThreadSafeCryptoUsageIntoParallelInvokeConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
class NotThreadSafeCryptoUsageIntoParallelInvokeConfig extends TaintTracking::Configuration {
|
||||
NotThreadSafeCryptoUsageIntoParallelInvokeConfig() {
|
||||
this = "NotThreadSafeCryptoUsageIntoParallelInvokeConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof LambdaCapturingICryptoTransformSource
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof ParallelSink
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ParallelSink }
|
||||
}
|
||||
|
||||
from Expr e, string m, LambdaExpr l, NotThreadSafeCryptoUsageIntoParallelInvokeConfig config
|
||||
where
|
||||
config.hasFlow(DataFlow::exprNode(l), DataFlow::exprNode(e))
|
||||
and m = "A $@ seems to be used to start a new thread is capturing a local variable that either implements 'System.Security.Cryptography.ICryptoTransform' or has a field of this type."
|
||||
where
|
||||
config.hasFlow(DataFlow::exprNode(l), DataFlow::exprNode(e)) and
|
||||
m = "A $@ seems to be used to start a new thread is capturing a local variable that either implements 'System.Security.Cryptography.ICryptoTransform' or has a field of this type."
|
||||
select e, m, l, "lambda expression"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<fragment>
|
||||
<p>Classes that implement <code>System.Security.Cryptography.ICryptoTransform</code> are not thread safe.</p>
|
||||
<p>This problem is caused by the way these classes are implemented using Microsoft CAPI/CNG patterns.</p>
|
||||
<p>For example, when a hash class implements this interface, there would typically be an instance-specific hash object created (for example using <code>BCryptCreateHash</code> function). This object can be called multiple times to add data to the hash (for example <code>BCryptHashData</code>). Finally, a function is called that finishes the hash and returns the data (for example <code>BCryptFinishHash</code>).</p>
|
||||
<p>Allowing the same hash object to be called with data from multiple threads before calling the finish function could potentially lead to incorrect results.</p>
|
||||
<p>For example, if you have multiple threads hashing <code>"abc"</code> on a static hash object, you may occasionally obtain the results (incorrectly) for hashing <code>"abcabc"</code>, or face other unexpected behavior.</p>
|
||||
<p>It is very unlikely somebody outside Microsoft would write a class that implements <code>ICryptoTransform</code>, and even if they do, it is likely that they will follow the same common pattern as the existing classes implementing this interface.</p>
|
||||
<p>Any object that implements <code>System.Security.Cryptography.ICryptoTransform</code> should not be used in concurrent threads as the instance members of such object are also not thread safe.</p>
|
||||
<p>Potential problems may not be evident at first, but can range from explicit errors such as exceptions, to incorrect results when sharing an instance of such an object in multiple threads.</p>
|
||||
</fragment>
|
||||
|
||||
</qhelp>
|
||||
@@ -10,70 +10,4 @@
|
||||
* maintainability
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.commons.Strings
|
||||
import semmle.code.csharp.frameworks.System
|
||||
|
||||
/**
|
||||
* Holds if expression `e`, of type `t`, invokes `ToString()` either explicitly
|
||||
* or implicitly.
|
||||
*/
|
||||
predicate invokesToString(Expr e, ValueOrRefType t) {
|
||||
// Explicit invocation
|
||||
exists(MethodCall mc | mc.getQualifier() = e |
|
||||
mc.getTarget() instanceof ToStringMethod and
|
||||
t = mc.getQualifier().getType()
|
||||
)
|
||||
or
|
||||
// Explicit or implicit invocation
|
||||
e instanceof ImplicitToStringExpr and
|
||||
t = e.stripCasts().getType()
|
||||
or
|
||||
// Implicit invocation via forwarder method
|
||||
t = e.stripCasts().getType() and
|
||||
not t instanceof StringType and
|
||||
exists(AssignableDefinitions::ImplicitParameterDefinition def, Parameter p, ParameterRead pr |
|
||||
e = p.getAnAssignedArgument()
|
||||
|
|
||||
def.getParameter() = p and
|
||||
pr = def.getAReachableRead() and
|
||||
pr.getAControlFlowNode().postDominates(p.getCallable().getEntryPoint()) and
|
||||
invokesToString(pr, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t`, or any sub type of `t`, inherits the default `ToString()`
|
||||
* method from `System.Object` or `System.ValueType`.
|
||||
*/
|
||||
predicate alwaysDefaultToString(ValueOrRefType t) {
|
||||
exists(ToStringMethod m | t.hasMethod(m) |
|
||||
m.getDeclaringType() instanceof SystemObjectClass or
|
||||
m.getDeclaringType() instanceof SystemValueTypeClass
|
||||
) and
|
||||
not exists(RefType overriding |
|
||||
overriding.getAMethod() instanceof ToStringMethod and
|
||||
overriding.getABaseType+() = t
|
||||
) and
|
||||
((t.isAbstract() or t instanceof Interface) implies not t.isEffectivelyPublic())
|
||||
}
|
||||
|
||||
class DefaultToStringType extends ValueOrRefType {
|
||||
DefaultToStringType() { alwaysDefaultToString(this) }
|
||||
|
||||
// A workaround for generating empty URLs for non-source locations, because qltest
|
||||
// does not support non-source locations
|
||||
override Location getLocation() {
|
||||
result = super.getLocation() and
|
||||
result instanceof SourceLocation
|
||||
or
|
||||
not super.getLocation() instanceof SourceLocation and
|
||||
result instanceof EmptyLocation
|
||||
}
|
||||
}
|
||||
|
||||
from Expr e, DefaultToStringType t
|
||||
where invokesToString(e, t)
|
||||
select e,
|
||||
"Default 'ToString()': $@ inherits 'ToString()' from 'Object', and so is not suitable for printing.",
|
||||
t, t.getName()
|
||||
import DefaultToString
|
||||
|
||||
57
csharp/ql/src/Useless code/DefaultToString.qll
Normal file
57
csharp/ql/src/Useless code/DefaultToString.qll
Normal file
@@ -0,0 +1,57 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.commons.Strings
|
||||
import semmle.code.csharp.frameworks.System
|
||||
|
||||
/**
|
||||
* Holds if expression `e`, of type `t`, invokes `ToString()` either explicitly
|
||||
* or implicitly.
|
||||
*/
|
||||
predicate invokesToString(Expr e, ValueOrRefType t) {
|
||||
// Explicit invocation
|
||||
exists(MethodCall mc | mc.getQualifier() = e |
|
||||
mc.getTarget() instanceof ToStringMethod and
|
||||
t = mc.getQualifier().getType()
|
||||
)
|
||||
or
|
||||
// Explicit or implicit invocation
|
||||
e instanceof ImplicitToStringExpr and
|
||||
t = e.stripCasts().getType()
|
||||
or
|
||||
// Implicit invocation via forwarder method
|
||||
t = e.stripCasts().getType() and
|
||||
not t instanceof StringType and
|
||||
exists(AssignableDefinitions::ImplicitParameterDefinition def, Parameter p, ParameterRead pr |
|
||||
e = p.getAnAssignedArgument()
|
||||
|
|
||||
def.getParameter() = p and
|
||||
pr = def.getAReachableRead() and
|
||||
pr.getAControlFlowNode().postDominates(p.getCallable().getEntryPoint()) and
|
||||
invokesToString(pr, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t`, or any sub type of `t`, inherits the default `ToString()`
|
||||
* method from `System.Object` or `System.ValueType`.
|
||||
*/
|
||||
predicate alwaysDefaultToString(ValueOrRefType t) {
|
||||
exists(ToStringMethod m | t.hasMethod(m) |
|
||||
m.getDeclaringType() instanceof SystemObjectClass or
|
||||
m.getDeclaringType() instanceof SystemValueTypeClass
|
||||
) and
|
||||
not exists(RefType overriding |
|
||||
overriding.getAMethod() instanceof ToStringMethod and
|
||||
overriding.getABaseType+() = t
|
||||
) and
|
||||
((t.isAbstract() or t instanceof Interface) implies not t.isEffectivelyPublic())
|
||||
}
|
||||
|
||||
class DefaultToStringType extends ValueOrRefType {
|
||||
DefaultToStringType() { alwaysDefaultToString(this) }
|
||||
}
|
||||
|
||||
query predicate problems(Expr e, string s, DefaultToStringType t, string name) {
|
||||
invokesToString(e, t) and
|
||||
s = "Default 'ToString()': $@ inherits 'ToString()' from 'Object', and so is not suitable for printing." and
|
||||
name = t.getName()
|
||||
}
|
||||
19
csharp/ql/src/external/ExternalArtifact.qll
vendored
19
csharp/ql/src/external/ExternalArtifact.qll
vendored
@@ -1,6 +1,17 @@
|
||||
import csharp
|
||||
|
||||
class ExternalDefect extends @externalDefect, Element {
|
||||
class ExternalElement extends @external_element {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { none() }
|
||||
|
||||
/** Gets the file containing this element. */
|
||||
File getFile() { result = getLocation().getFile() }
|
||||
}
|
||||
|
||||
class ExternalDefect extends ExternalElement, @externalDefect {
|
||||
string getQueryPath() {
|
||||
exists(string path |
|
||||
externalDefects(this, path, _, _, _) and
|
||||
@@ -19,7 +30,7 @@ class ExternalDefect extends @externalDefect, Element {
|
||||
}
|
||||
}
|
||||
|
||||
class ExternalMetric extends @externalMetric, Element {
|
||||
class ExternalMetric extends ExternalElement, @externalMetric {
|
||||
string getQueryPath() { externalMetrics(this, result, _, _) }
|
||||
|
||||
float getValue() { externalMetrics(this, _, _, result) }
|
||||
@@ -29,7 +40,7 @@ class ExternalMetric extends @externalMetric, Element {
|
||||
override string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() }
|
||||
}
|
||||
|
||||
class ExternalData extends @externalDataElement {
|
||||
class ExternalData extends ExternalElement, @externalDataElement {
|
||||
string getDataPath() { externalData(this, result, _, _) }
|
||||
|
||||
string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
|
||||
@@ -44,7 +55,7 @@ class ExternalData extends @externalDataElement {
|
||||
|
||||
date getFieldAsDate(int index) { result = getField(index).toDate() }
|
||||
|
||||
string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
|
||||
override string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
|
||||
|
||||
private string buildTupleString(int start) {
|
||||
start = getNumFields() - 1 and result = getField(start)
|
||||
|
||||
@@ -257,9 +257,7 @@ private module Internal {
|
||||
or
|
||||
cfn.isJoin()
|
||||
or
|
||||
exists(ControlFlowNode pred | pred = cfn.getAPredecessor() |
|
||||
strictcount(pred.getASuccessor()) > 1
|
||||
)
|
||||
cfn.getAPredecessor().isBranch()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,3 +17,4 @@ import Handler
|
||||
import ControlFlow
|
||||
import DataFlow
|
||||
import Attribute
|
||||
import Stubs
|
||||
|
||||
53
csharp/ql/src/semmle/code/cil/CallableReturns.qll
Normal file
53
csharp/ql/src/semmle/code/cil/CallableReturns.qll
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Provides predicates for analysing the return values of callables.
|
||||
*/
|
||||
|
||||
private import CIL
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/** Holds if method `m` always returns null. */
|
||||
cached
|
||||
predicate alwaysNullMethod(Method m) { forex(Expr e | m.canReturn(e) | alwaysNullExpr(e)) }
|
||||
|
||||
/** Holds if method `m` always returns non-null. */
|
||||
cached
|
||||
predicate alwaysNotNullMethod(Method m) { forex(Expr e | m.canReturn(e) | alwaysNotNullExpr(e)) }
|
||||
|
||||
/** Holds if method `m` always throws an exception. */
|
||||
cached
|
||||
predicate alwaysThrowsMethod(Method m) {
|
||||
m.hasBody() and
|
||||
not exists(m.getImplementation().getAnInstruction().(Return))
|
||||
}
|
||||
|
||||
/** Holds if method `m` always throws an exception of type `t`. */
|
||||
cached
|
||||
predicate alwaysThrowsException(Method m, Type t) {
|
||||
alwaysThrowsMethod(m) and
|
||||
forex(Throw ex | ex = m.getImplementation().getAnInstruction() | t = ex.getExpr().getType())
|
||||
}
|
||||
}
|
||||
import Cached
|
||||
|
||||
/** Holds if expression `expr` always evaluates to `null`. */
|
||||
private predicate alwaysNullExpr(Expr expr) {
|
||||
expr instanceof NullLiteral
|
||||
or
|
||||
alwaysNullMethod(expr.(StaticCall).getTarget())
|
||||
or
|
||||
forex(VariableUpdate vu | DefUse::variableUpdateUse(_, vu, expr) | alwaysNullExpr(vu.getSource()))
|
||||
}
|
||||
|
||||
/** Holds if expression `expr` always evaluates to non-null. */
|
||||
private predicate alwaysNotNullExpr(Expr expr) {
|
||||
expr instanceof Opcodes::Newobj
|
||||
or
|
||||
expr instanceof Literal and not expr instanceof NullLiteral
|
||||
or
|
||||
alwaysNotNullMethod(expr.(StaticCall).getTarget())
|
||||
or
|
||||
forex(VariableUpdate vu | DefUse::variableUpdateUse(_, vu, expr) |
|
||||
alwaysNotNullExpr(vu.getSource())
|
||||
)
|
||||
}
|
||||
@@ -104,7 +104,10 @@ class ControlFlowNode extends @cil_controlflow_node {
|
||||
Type getType() { none() }
|
||||
|
||||
/** Holds if this control flow node has more than one predecessor. */
|
||||
predicate isJoin() { count(getAPredecessor()) > 1 }
|
||||
predicate isJoin() { strictcount(this.getAPredecessor()) > 1 }
|
||||
|
||||
/** Holds if this control flow node has more than one successor. */
|
||||
predicate isBranch() { strictcount(this.getASuccessor()) > 1 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,6 +140,6 @@ class TrueFlow extends FlowType, TTrueFlow {
|
||||
}
|
||||
|
||||
/** False control flow. */
|
||||
class FalseFlow extends FlowType, TTrueFlow {
|
||||
class FalseFlow extends FlowType, TFalseFlow {
|
||||
override string toString() { result = "false" }
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class Tainted extends TaintType, TTaintedValue { }
|
||||
private predicate localExactStep(DataFlowNode src, DataFlowNode sink) {
|
||||
src = sink.(Opcodes::Dup).getAnOperand()
|
||||
or
|
||||
DefUse::defUse(_, src, sink)
|
||||
defUse(_, src, sink)
|
||||
or
|
||||
src = sink.(ParameterReadAccess).getTarget()
|
||||
or
|
||||
@@ -88,7 +88,7 @@ private predicate localTaintStep(DataFlowNode src, DataFlowNode sink) {
|
||||
}
|
||||
|
||||
cached
|
||||
private module DefUse {
|
||||
module DefUse {
|
||||
/**
|
||||
* A classification of variable references into reads and writes.
|
||||
*/
|
||||
@@ -185,21 +185,26 @@ private module DefUse {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the update `def` can be used at the read `use`. */
|
||||
/** Holds if the variable update `vu` can be used at the read `use`. */
|
||||
cached
|
||||
predicate defUse(StackVariable target, DataFlowNode def, ReadAccess use) {
|
||||
exists(VariableUpdate vu | def = vu.getSource() |
|
||||
defReachesReadWithinBlock(target, vu, use)
|
||||
or
|
||||
exists(BasicBlock bb, int i |
|
||||
exists(refRank(bb, i, target, Read())) and
|
||||
use = bb.getNode(i) and
|
||||
defReachesEndOfBlock(bb.getAPredecessor(), vu, target) and
|
||||
not defReachesReadWithinBlock(target, _, use)
|
||||
)
|
||||
predicate variableUpdateUse(StackVariable target, VariableUpdate vu, ReadAccess use) {
|
||||
defReachesReadWithinBlock(target, vu, use)
|
||||
or
|
||||
exists(BasicBlock bb, int i |
|
||||
exists(refRank(bb, i, target, Read())) and
|
||||
use = bb.getNode(i) and
|
||||
defReachesEndOfBlock(bb.getAPredecessor(), vu, target) and
|
||||
not defReachesReadWithinBlock(target, _, use)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the update `def` can be used at the read `use`. */
|
||||
cached
|
||||
predicate defUse(StackVariable target, Expr def, ReadAccess use) {
|
||||
exists(VariableUpdate vu | def = vu.getSource() | variableUpdateUse(target, vu, use))
|
||||
}
|
||||
}
|
||||
private import DefUse
|
||||
|
||||
abstract library class VariableUpdate extends Instruction {
|
||||
abstract Expr getSource();
|
||||
|
||||
@@ -127,6 +127,9 @@ class FloatLiteral extends Literal, @cil_ldc_r { }
|
||||
/** An expression that pushes a `null` value onto the stack. */
|
||||
class NullLiteral extends Literal, @cil_ldnull { }
|
||||
|
||||
/** An expression that pushes a string onto the stack. */
|
||||
class StringLiteral extends Literal, @cil_ldstr { }
|
||||
|
||||
/** A branch with one operand. */
|
||||
class UnaryBranch extends ConditionalBranch, @cil_unary_jump {
|
||||
override int getPopCount() { result = 1 }
|
||||
|
||||
@@ -234,7 +234,7 @@ module Opcodes {
|
||||
override string getOpcodeName() { result = "nop" }
|
||||
}
|
||||
|
||||
class Ldstr extends Literal, @cil_ldstr {
|
||||
class Ldstr extends StringLiteral, @cil_ldstr {
|
||||
override string getOpcodeName() { result = "ldstr" }
|
||||
|
||||
override string getExtra() { result = "\"" + getValue() + "\"" }
|
||||
|
||||
@@ -50,6 +50,15 @@ class MethodImplementation extends EntryPoint, @cil_method_implementation {
|
||||
int getStackSize() { cil_method_stack_size(this, result) }
|
||||
|
||||
override string toString() { result = getMethod().toString() }
|
||||
|
||||
/** Gets a string representing the disassembly of this implementation. */
|
||||
string getDisassembly() {
|
||||
result = concat(Instruction i |
|
||||
i = this.getAnInstruction()
|
||||
|
|
||||
i.toString(), ", " order by i.getIndex()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +72,9 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
|
||||
*/
|
||||
MethodImplementation getAnImplementation() { result.getMethod() = this }
|
||||
|
||||
/** Gets the "best" implementation of this method, if any. */
|
||||
BestImplementation getImplementation() { result = getAnImplementation() }
|
||||
|
||||
override Method getMethod() { result = this }
|
||||
|
||||
override string getName() { cil_method(this, result, _, _) }
|
||||
@@ -102,7 +114,7 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
|
||||
int getCallPopCount() { result = count(getRawParameter(_)) }
|
||||
|
||||
/** Gets a method called by this method. */
|
||||
Method getACallee() { result = getAnImplementation().getAnInstruction().(Call).getTarget() }
|
||||
Method getACallee() { result = getImplementation().getAnInstruction().(Call).getTarget() }
|
||||
|
||||
/** Holds if this method is `virtual`. */
|
||||
predicate isVirtual() { cil_virtual(this) }
|
||||
@@ -168,10 +180,10 @@ class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowN
|
||||
/** Gets a method that overrides this method, if any. */
|
||||
final Method getAnOverrider() { result.getOverriddenMethod() = this }
|
||||
|
||||
override predicate hasBody() { exists(getAnImplementation()) }
|
||||
override predicate hasBody() { exists(getImplementation()) }
|
||||
|
||||
override predicate canReturn(DotNet::Expr expr) {
|
||||
exists(Return ret | ret.getImplementation().getMethod() = this and expr = ret.getExpr())
|
||||
exists(Return ret | ret.getImplementation() = this.getImplementation() and expr = ret.getExpr())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +210,7 @@ class InstanceConstructor extends Constructor {
|
||||
/** A method that always returns the `this` parameter. */
|
||||
class ChainingMethod extends Method {
|
||||
ChainingMethod() {
|
||||
forex(Return ret | ret = getAnImplementation().getAnInstruction() |
|
||||
forex(Return ret | ret = getImplementation().getAnInstruction() |
|
||||
ret.getExpr() instanceof ThisAccess
|
||||
)
|
||||
}
|
||||
@@ -231,9 +243,7 @@ class TrivialGetter extends Method {
|
||||
}
|
||||
|
||||
/** Gets the underlying field of this getter. */
|
||||
Field getField() {
|
||||
getAnImplementation().getAnInstruction().(FieldReadAccess).getTarget() = result
|
||||
}
|
||||
Field getField() { getImplementation().getAnInstruction().(FieldReadAccess).getTarget() = result }
|
||||
}
|
||||
|
||||
/** A setter. */
|
||||
@@ -249,7 +259,7 @@ class Setter extends Accessor {
|
||||
*/
|
||||
class TrivialSetter extends Method {
|
||||
TrivialSetter() {
|
||||
exists(MethodImplementation impl | impl = getAnImplementation() |
|
||||
exists(MethodImplementation impl | impl = getImplementation() |
|
||||
impl.getInstruction(0) instanceof ThisAccess and
|
||||
impl.getInstruction(1).(ParameterReadAccess).getTarget().getIndex() = 1 and
|
||||
impl.getInstruction(2) instanceof FieldWriteAccess
|
||||
@@ -258,7 +268,7 @@ class TrivialSetter extends Method {
|
||||
|
||||
/** Gets the underlying field of this setter. */
|
||||
Field getField() {
|
||||
result = getAnImplementation().getAnInstruction().(FieldWriteAccess).getTarget()
|
||||
result = getImplementation().getAnInstruction().(FieldWriteAccess).getTarget()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
54
csharp/ql/src/semmle/code/cil/Stubs.qll
Normal file
54
csharp/ql/src/semmle/code/cil/Stubs.qll
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Provides classes and predicates for identifying stub code.
|
||||
*/
|
||||
|
||||
import CIL
|
||||
|
||||
/**
|
||||
* The average number of instructions per method,
|
||||
* below which an assembly is probably a stub.
|
||||
*/
|
||||
private float stubInstructionThreshold() { result = 5.1 }
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* A simple heuristic for determining whether an assembly is a
|
||||
* reference assembly where the method bodies have dummy implementations.
|
||||
* Look at the average number of instructions per method.
|
||||
*/
|
||||
cached
|
||||
predicate assemblyIsStubImpl(Assembly asm) {
|
||||
exists(int totalInstructions, int totalImplementations |
|
||||
totalInstructions = count(Instruction i | i.getImplementation().getLocation() = asm) and
|
||||
totalImplementations = count(MethodImplementation i |
|
||||
i.getImplementation().getLocation() = asm
|
||||
) and
|
||||
totalInstructions.(float) / totalImplementations.(float) < stubInstructionThreshold()
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate bestImplementation(MethodImplementation mi) {
|
||||
not assemblyIsStubImpl(mi.getLocation()) and
|
||||
not exists(MethodImplementation better | mi.getMethod() = better.getMethod() |
|
||||
mi.getNumberOfInstructions() < better.getNumberOfInstructions()
|
||||
or
|
||||
mi.getNumberOfInstructions() = better.getNumberOfInstructions() and
|
||||
mi.getLocation().getFile().toString() > better.getLocation().getFile().toString()
|
||||
) and
|
||||
exists(mi.getAnInstruction())
|
||||
}
|
||||
}
|
||||
private import Cached
|
||||
|
||||
predicate assemblyIsStub = assemblyIsStubImpl/1;
|
||||
|
||||
/**
|
||||
* A method implementation that is the "best" one for a particular method,
|
||||
* if there are several potential implementations to choose between, and
|
||||
* excludes implementations that are probably from stub/reference assemblies.
|
||||
*/
|
||||
class BestImplementation extends MethodImplementation {
|
||||
BestImplementation() { bestImplementation(this) }
|
||||
}
|
||||
@@ -28,7 +28,9 @@ class Variable extends DotNet::Variable, Declaration, DataFlowNode, @cil_variabl
|
||||
}
|
||||
|
||||
/** A stack variable. Either a local variable (`LocalVariable`) or a parameter (`Parameter`). */
|
||||
class StackVariable extends Variable, @cil_stack_variable { }
|
||||
class StackVariable extends Variable, @cil_stack_variable {
|
||||
override predicate hasQualifiedName(string qualifier, string name) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A local variable.
|
||||
|
||||
@@ -76,6 +76,11 @@ class AssignableRead extends AssignableAccess {
|
||||
not nameOfChild(_, this)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private ControlFlow::Node getAnAdjacentReadSameVar() {
|
||||
Ssa::Internal::adjacentReadPairSameVar(this.getAControlFlowNode(), result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a next read of the same underlying assignable. That is, a read
|
||||
* that can be reached from this read without passing through any other reads,
|
||||
@@ -102,7 +107,7 @@ class AssignableRead extends AssignableAccess {
|
||||
*/
|
||||
AssignableRead getANextRead() {
|
||||
forex(ControlFlow::Node cfn | cfn = result.getAControlFlowNode() |
|
||||
Ssa::Internal::adjacentReadPairSameVar(this.getAControlFlowNode(), cfn)
|
||||
cfn = this.getAnAdjacentReadSameVar()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,21 @@ class Attributable extends @attributable {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/** Gets the location of this element, if any. */
|
||||
Location getLocation() { none() }
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this
|
||||
.(Element)
|
||||
.getLocation()
|
||||
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,9 +54,37 @@ class Attribute extends TopLevelExprParent, @attribute {
|
||||
/** Gets the element that this attribute is attached to. */
|
||||
Attributable getTarget() { attributes(this, _, result) }
|
||||
|
||||
/** Gets the `i`th argument of this attribute. */
|
||||
/**
|
||||
* Gets the `i`th argument of this attribute. This includes both constructor
|
||||
* arguments and named arguments.
|
||||
*/
|
||||
Expr getArgument(int i) { result = this.getChildExpr(i) }
|
||||
|
||||
/**
|
||||
* Gets the `i`th constructor argument of this attribute. For example, only
|
||||
* `true` is a constructor argument in
|
||||
*
|
||||
* ```
|
||||
* MyAttribute[true, Foo = 0]
|
||||
* ```
|
||||
*/
|
||||
Expr getConstructorArgument(int i) {
|
||||
result = this.getArgument(i) and not exists(result.getExplicitArgumentName())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the named argument `name` of this attribute. For example, only
|
||||
* `0` is a named argument in
|
||||
*
|
||||
* ```
|
||||
* MyAttribute[true, Foo = 0]
|
||||
* ```
|
||||
*/
|
||||
Expr getNamedArgument(string name) {
|
||||
result = this.getArgument(_) and
|
||||
result.getExplicitArgumentName() = name
|
||||
}
|
||||
|
||||
override Location getALocation() { attribute_location(this, result) }
|
||||
|
||||
override string toString() {
|
||||
|
||||
@@ -283,8 +283,6 @@ class Method extends Callable, Virtualizable, Attributable, @method {
|
||||
|
||||
override string toString() { result = Callable.super.toString() }
|
||||
|
||||
override Location getLocation() { result = Callable.super.getLocation() }
|
||||
|
||||
override Parameter getRawParameter(int i) {
|
||||
if this.isStatic() then result = this.getParameter(i) else result = this.getParameter(i - 1)
|
||||
}
|
||||
@@ -354,8 +352,6 @@ class Constructor extends DotNet::Constructor, Callable, Member, Attributable, @
|
||||
|
||||
override string toString() { result = Callable.super.toString() }
|
||||
|
||||
override Location getLocation() { result = Callable.super.getLocation() }
|
||||
|
||||
override Parameter getRawParameter(int i) {
|
||||
if this.isStatic() then result = this.getParameter(i) else result = this.getParameter(i - 1)
|
||||
}
|
||||
@@ -419,8 +415,6 @@ class Destructor extends DotNet::Destructor, Callable, Member, Attributable, @de
|
||||
override Location getALocation() { destructor_location(this, result) }
|
||||
|
||||
override string toString() { result = Callable.super.toString() }
|
||||
|
||||
override Location getLocation() { result = Callable.super.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,8 +441,6 @@ class Operator extends Callable, Member, Attributable, @operator {
|
||||
|
||||
override string toString() { result = Callable.super.toString() }
|
||||
|
||||
override Location getLocation() { result = Callable.super.getLocation() }
|
||||
|
||||
override Parameter getRawParameter(int i) { result = getParameter(i) }
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ class Element extends DotNet::Element, @element {
|
||||
* Where an element has multiple locations (for example a source file and an assembly),
|
||||
* gets only the source location.
|
||||
*/
|
||||
override Location getLocation() { result = ExprOrStmtParentCached::bestLocation(this) }
|
||||
final override Location getLocation() { result = ExprOrStmtParentCached::bestLocation(this) }
|
||||
|
||||
/** Gets a location of this element, including sources and assemblies. */
|
||||
override Location getALocation() { none() }
|
||||
|
||||
@@ -126,11 +126,7 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter {
|
||||
/** Gets the constraints on this type parameter, if any. */
|
||||
TypeParameterConstraints getConstraints() { result.getTypeParameter() = this }
|
||||
|
||||
/**
|
||||
* Holds if this type parameter is guaranteed to always be instantiated
|
||||
* to a reference type.
|
||||
*/
|
||||
predicate isRefType() {
|
||||
override predicate isRefType() {
|
||||
exists(TypeParameterConstraints tpc | tpc = getConstraints() |
|
||||
tpc.hasRefTypeConstraint() or
|
||||
exists(tpc.getClassConstraint()) or
|
||||
@@ -139,11 +135,7 @@ class TypeParameter extends DotNet::TypeParameter, Type, @type_parameter {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this type parameter is guaranteed to always be instantiated
|
||||
* to a value type.
|
||||
*/
|
||||
predicate isValueType() {
|
||||
override predicate isValueType() {
|
||||
exists(TypeParameterConstraints tpc | tpc = getConstraints() |
|
||||
tpc.hasValueTypeConstraint() or
|
||||
tpc.getATypeParameterConstraint().isValueType()
|
||||
|
||||
@@ -43,8 +43,6 @@ class DeclarationWithAccessors extends AssignableMember, Virtualizable, Attribut
|
||||
override Type getType() { none() }
|
||||
|
||||
override string toString() { result = AssignableMember.super.toString() }
|
||||
|
||||
override Location getLocation() { result = AssignableMember.super.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,8 +358,6 @@ class Accessor extends Callable, Modifiable, Attributable, @callable_accessor {
|
||||
override Location getALocation() { accessor_location(this, result) }
|
||||
|
||||
override string toString() { result = getName() }
|
||||
|
||||
override Location getLocation() { result = Callable.super.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,6 +37,12 @@ class Type extends DotNet::Type, Member, TypeContainer, @type {
|
||||
or
|
||||
not this instanceof UnboundGenericType and getAChild().containsTypeParameters()
|
||||
}
|
||||
|
||||
/** Holds if this type is a reference type, or a type parameter that is a reference type. */
|
||||
predicate isRefType() { none() }
|
||||
|
||||
/** Holds if this type is a value type, or a type parameter that is a value type. */
|
||||
predicate isValueType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -356,8 +362,6 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
|
||||
}
|
||||
|
||||
override string toString() { result = Type.super.toString() }
|
||||
|
||||
override Location getLocation() { result = Type.super.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,7 +408,9 @@ class VoidType extends DotNet::ValueOrRefType, Type, @void_type {
|
||||
* Either a simple type (`SimpleType`), an `enum` (`Enum`), a `struct` (`Struct`),
|
||||
* or a nullable type (`NullableType`).
|
||||
*/
|
||||
class ValueType extends ValueOrRefType, @value_type { }
|
||||
class ValueType extends ValueOrRefType, @value_type {
|
||||
override predicate isValueType() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple type. Simple types in C# are predefined `struct` types.
|
||||
@@ -659,6 +665,8 @@ class RefType extends ValueOrRefType, @ref_type {
|
||||
result = (this.getNumberOverridden() * this.getInheritanceDepth()) /
|
||||
this.getNumberOfCallables().(float)
|
||||
}
|
||||
|
||||
override predicate isRefType() { any() }
|
||||
}
|
||||
|
||||
// Helper predicate to avoid slow "negation_body"
|
||||
|
||||
@@ -111,6 +111,8 @@ class LocalScopeVariable extends Variable, @local_scope_variable {
|
||||
* Holds if this local variable or parameter is a `ref`.
|
||||
*/
|
||||
predicate isRef() { none() }
|
||||
|
||||
override predicate hasQualifiedName(string qualifier, string name) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,8 +224,6 @@ class Parameter extends DotNet::Parameter, LocalScopeVariable, Attributable, Top
|
||||
|
||||
override string toString() { result = this.getName() }
|
||||
|
||||
override Location getLocation() { result = LocalScopeVariable.super.getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the default value of this parameter, if any. For example, the
|
||||
* default value of `numberOfTries` is `3` in
|
||||
@@ -400,8 +400,6 @@ class Field extends Variable, AssignableMember, Attributable, TopLevelExprParent
|
||||
override FieldAccess getAnAccess() { result = Variable.super.getAnAccess() }
|
||||
|
||||
override string toString() { result = Variable.super.toString() }
|
||||
|
||||
override Location getLocation() { result = Variable.super.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,19 +4,19 @@ import csharp
|
||||
private import semmle.code.csharp.commons.ComparisonTest
|
||||
private import semmle.code.csharp.commons.StructuralComparison as StructuralComparison
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isConstantCondition0(ControlFlow::Node cfn, boolean b) {
|
||||
exists(
|
||||
cfn.getASuccessorByType(any(ControlFlow::SuccessorTypes::BooleanSuccessor t | t.getValue() = b))
|
||||
) and
|
||||
strictcount(ControlFlow::SuccessorType t | exists(cfn.getASuccessorByType(t))) = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is a condition that always evaluates to Boolean value `b`.
|
||||
*/
|
||||
predicate isConstantCondition(Expr e, boolean b) {
|
||||
forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() |
|
||||
exists(
|
||||
cfn
|
||||
.getASuccessorByType(any(ControlFlow::SuccessorTypes::BooleanSuccessor t |
|
||||
t.getValue() = b
|
||||
))
|
||||
) and
|
||||
strictcount(ControlFlow::SuccessorType t | exists(cfn.getASuccessorByType(t))) = 1
|
||||
)
|
||||
forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() | isConstantCondition0(cfn, b))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -336,9 +336,7 @@ private module Internal {
|
||||
or
|
||||
cfn.isJoin()
|
||||
or
|
||||
exists(ControlFlow::Node pred | pred = cfn.getAPredecessor() |
|
||||
strictcount(pred.getASuccessor()) > 1
|
||||
)
|
||||
cfn.getAPredecessor().isBranch()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,9 +20,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
Callable getEnclosingCallable() { none() }
|
||||
|
||||
/** Gets the assembly that this element was compiled into. */
|
||||
Assembly getAssembly() {
|
||||
result = this.getEnclosingCallable().getDeclaringType().getALocation()
|
||||
}
|
||||
Assembly getAssembly() { result = this.getEnclosingCallable().getDeclaringType().getALocation() }
|
||||
|
||||
/**
|
||||
* Gets a control flow node for this element. That is, a node in the
|
||||
@@ -75,6 +73,40 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
getAControlFlowNode().getBasicBlock().getASuccessor+().getANode() = result.getAControlFlowNode()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate immediatelyControlsBlockSplit0(
|
||||
ConditionBlock cb, BasicBlock succ, ConditionalSuccessor s
|
||||
) {
|
||||
// Only calculate dominance by explicit recursion for split nodes;
|
||||
// all other nodes can use regular CFG dominance
|
||||
this instanceof ControlFlow::Internal::SplitControlFlowElement and
|
||||
cb.getLastNode() = this.getAControlFlowNode() and
|
||||
succ = cb.getASuccessorByType(s)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate immediatelyControlsBlockSplit1(
|
||||
ConditionBlock cb, BasicBlock succ, ConditionalSuccessor s, BasicBlock pred, SuccessorType t
|
||||
) {
|
||||
this.immediatelyControlsBlockSplit0(cb, succ, s) and
|
||||
pred = succ.getAPredecessorByType(t) and
|
||||
pred != cb
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate immediatelyControlsBlockSplit2(
|
||||
ConditionBlock cb, BasicBlock succ, ConditionalSuccessor s, BasicBlock pred, SuccessorType t
|
||||
) {
|
||||
this.immediatelyControlsBlockSplit1(cb, succ, s, pred, t) and
|
||||
(
|
||||
succ.dominates(pred)
|
||||
or
|
||||
// `pred` might be another split of this element
|
||||
pred.getLastNode().getElement() = this and
|
||||
t = s
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if basic block `succ` is immediately controlled by this control flow
|
||||
* element with conditional value `s`. That is, `succ` can only be reached from
|
||||
@@ -96,19 +128,11 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate immediatelyControlsBlockSplit(BasicBlock succ, ConditionalSuccessor s) {
|
||||
// Only calculate dominance by explicit recursion for split nodes;
|
||||
// all other nodes can use regular CFG dominance
|
||||
this instanceof ControlFlow::Internal::SplitControlFlowElement and
|
||||
exists(ConditionBlock cb | cb.getLastNode() = this.getAControlFlowNode() |
|
||||
succ = cb.getASuccessorByType(s) and
|
||||
exists(ConditionBlock cb | this.immediatelyControlsBlockSplit0(cb, succ, s) |
|
||||
forall(BasicBlock pred, SuccessorType t |
|
||||
pred = succ.getAPredecessorByType(t) and pred != cb
|
||||
this.immediatelyControlsBlockSplit1(cb, succ, s, pred, t)
|
||||
|
|
||||
succ.dominates(pred)
|
||||
or
|
||||
// `pred` might be another split of this element
|
||||
pred.getLastNode().getElement() = this and
|
||||
t = s
|
||||
this.immediatelyControlsBlockSplit2(cb, succ, s, pred, t)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -273,7 +273,10 @@ module ControlFlow {
|
||||
}
|
||||
|
||||
/** Holds if this node has more than one predecessor. */
|
||||
predicate isJoin() { strictcount(getAPredecessor()) > 1 }
|
||||
predicate isJoin() { strictcount(this.getAPredecessor()) > 1 }
|
||||
|
||||
/** Holds if this node has more than one successor. */
|
||||
predicate isBranch() { strictcount(this.getASuccessor()) > 1 }
|
||||
}
|
||||
|
||||
/** Provides different types of control flow nodes. */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user