Merge branch 'master' into cpp340a

So as to get to change-notes/1.21/analysis-cpp.md
This commit is contained in:
Ziemowit Laski
2019-04-01 18:51:03 -07:00
285 changed files with 5527 additions and 1594 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -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``"
1 Language Variants Compilers Extensions
13
14
15
16

View 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

View 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

View File

@@ -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`.

View File

@@ -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

View File

@@ -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()))
)
)
)
}

View File

@@ -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

View File

@@ -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

View 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>

View File

@@ -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
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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 + ")"

View File

@@ -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" }

View File

@@ -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>

View File

@@ -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

View File

@@ -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()
)

View File

@@ -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
)
}

View File

@@ -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)
)
)
}

View 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
)
)
}

View File

@@ -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.
*/

View File

@@ -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)
}
}
/**

View File

@@ -3,6 +3,6 @@ private import semmle.code.cpp.ir.internal.TempVariableTag
class TempVariableTag extends TTempVariableTag {
string toString() {
result = "Tag"
result = getTempVariableTagId(this)
}
}

View File

@@ -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

View File

@@ -268,6 +268,9 @@ newtype TTranslatedElement =
) or
exists(ThrowExpr throw |
throw.getExpr().getFullyConverted() = expr
) or
exists(LambdaExpression lambda |
lambda.getInitializer().getFullyConverted() = expr
)
)
} or

View File

@@ -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())
}
}

View File

@@ -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)
}

View File

@@ -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"
}

View File

@@ -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
)
}
/**

View 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) }
}

View File

@@ -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()))
}
/**

View File

@@ -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 */

View File

@@ -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?

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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 :

View File

@@ -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
}

View File

@@ -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;
}
}
}

View File

@@ -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. |

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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 *' |

View File

@@ -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 |

View File

@@ -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(), ", ")

View File

@@ -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
}

View File

@@ -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 *' |

View File

@@ -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 |

View File

@@ -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() {

View File

@@ -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 *' |

View File

@@ -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 |

View File

@@ -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
}

View File

@@ -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 *' |

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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) |

View File

@@ -0,0 +1 @@
Security/CWE/CWE-190/TaintedAllocationSize.ql

View File

@@ -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;
}

View File

@@ -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 |

View File

@@ -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
}
}

View File

@@ -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);
}
};

View File

@@ -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;
};

View File

@@ -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)
{

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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.

View File

@@ -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
)
}
}

View File

@@ -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")
)
}
}

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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

View 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()
}

View File

@@ -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)

View File

@@ -257,9 +257,7 @@ private module Internal {
or
cfn.isJoin()
or
exists(ControlFlowNode pred | pred = cfn.getAPredecessor() |
strictcount(pred.getASuccessor()) > 1
)
cfn.getAPredecessor().isBranch()
}
/**

View File

@@ -17,3 +17,4 @@ import Handler
import ControlFlow
import DataFlow
import Attribute
import Stubs

View 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())
)
}

View File

@@ -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" }
}

View File

@@ -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();

View File

@@ -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 }

View File

@@ -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() + "\"" }

View File

@@ -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()
}
}

View 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) }
}

View File

@@ -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.

View File

@@ -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()
)
}

View File

@@ -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() {

View File

@@ -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) }
}

View File

@@ -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() }

View File

@@ -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()

View File

@@ -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() }
}
/**

View File

@@ -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"

View File

@@ -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() }
}
/**

View File

@@ -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))
}
/**

View File

@@ -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()
}
/**

View File

@@ -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)
)
)
}

View File

@@ -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