diff --git a/.codeqlmanifest.json b/.codeqlmanifest.json index a3caa99f3c0..0a3a216dd1c 100644 --- a/.codeqlmanifest.json +++ b/.codeqlmanifest.json @@ -1,3 +1,5 @@ { "provide": [ "*/ql/src/qlpack.yml", + "*/upgrades/qlpack.yml", "misc/legacy-support/*/qlpack.yml", - "misc/suite-helpers/qlpack.yml" ] } + "misc/suite-helpers/qlpack.yml", + "codeql/.codeqlmanifest.json" ] } diff --git a/change-notes/1.23/analysis-cpp.md b/change-notes/1.23/analysis-cpp.md index 3ae57bc6d1d..f22436f55cb 100644 --- a/change-notes/1.23/analysis-cpp.md +++ b/change-notes/1.23/analysis-cpp.md @@ -22,6 +22,7 @@ The following changes in version 1.23 affect C/C++ analysis in all applications. | Too few arguments to formatting function (`cpp/wrong-number-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. | | Too many arguments to formatting function (`cpp/too-many-format-arguments`) | Fewer false positive results | Fixed false positives resulting from mistmatching declarations of a formatting function. | | Unclear comparison precedence (`cpp/comparison-precedence`) | Fewer false positive results | False positives involving template classes and functions have been fixed. | +| Comparison of narrow type with wide type in loop condition (`cpp/comparison-with-wider-type`) | Higher precision | The precision of this query has been increased to "high" as the alerts from this query have proved to be valuable on real-world projects. With this precision, results are now displayed by default in LGTM. | ## Changes to QL libraries @@ -38,6 +39,10 @@ The following changes in version 1.23 affect C/C++ analysis in all applications. definition of `x` when `x` is a variable of pointer type. It no longer considers deep paths such as `f(&x.myField)` to be definitions of `x`. These changes are in line with the user expectations we've observed. +* The data-flow library now makes it easier to specify barriers/sanitizers + arising from guards by overriding the predicate + `isBarrierGuard`/`isSanitizerGuard` on data-flow and taint-tracking + configurations respectively. * There is now a `DataFlow::localExprFlow` predicate and a `TaintTracking::localExprTaint` predicate to make it easy to use the most common case of local data flow and taint: from one `Expr` to another. diff --git a/change-notes/1.23/analysis-csharp.md b/change-notes/1.23/analysis-csharp.md index 7ec412a0eb2..df11c0c4abc 100644 --- a/change-notes/1.23/analysis-csharp.md +++ b/change-notes/1.23/analysis-csharp.md @@ -8,6 +8,7 @@ The following changes in version 1.23 affect C# analysis in all applications. | **Query** | **Tags** | **Purpose** | |-----------------------------|-----------|--------------------------------------------------------------------| +| Deserialized delegate (`cs/deserialized-delegate`) | security, external/cwe/cwe-502 | Finds unsafe deserialization of delegate types. | | Unsafe year argument for 'DateTime' constructor (`cs/unsafe-year-construction`) | reliability, date-time | Finds incorrect manipulation of `DateTime` values, which could lead to invalid dates. | | Mishandling the Japanese era start date (`cs/mishandling-japanese-era`) | reliability, date-time | Finds hard-coded Japanese era start dates that could be invalid. | @@ -43,5 +44,6 @@ The following changes in version 1.23 affect C# analysis in all applications. * There is now a `DataFlow::localExprFlow` predicate and a `TaintTracking::localExprTaint` predicate to make it easy to use the most common case of local data flow and taint: from one `Expr` to another. +* Data is now tracked through null-coalescing expressions (`??`). ## Changes to autobuilder diff --git a/change-notes/1.23/analysis-java.md b/change-notes/1.23/analysis-java.md index 7650a283266..b81b681aac5 100644 --- a/change-notes/1.23/analysis-java.md +++ b/change-notes/1.23/analysis-java.md @@ -2,6 +2,12 @@ The following changes in version 1.23 affect Java analysis in all applications. +## New queries + +| **Query** | **Tags** | **Purpose** | +|-----------------------------|-----------|--------------------------------------------------------------------| +| Continue statement that does not continue (`java/continue-in-false-loop`) | correctness | Finds `continue` statements in `do { ... } while (false)` loops. | + ## Changes to existing queries | **Query** | **Expected impact** | **Change** | diff --git a/change-notes/1.23/analysis-javascript.md b/change-notes/1.23/analysis-javascript.md index 73bbf1247f7..49f527af424 100644 --- a/change-notes/1.23/analysis-javascript.md +++ b/change-notes/1.23/analysis-javascript.md @@ -2,7 +2,7 @@ ## General improvements -* Suppor for `globalThis` has been added. +* Support for `globalThis` has been added. * Support for the following frameworks and libraries has been improved: - [firebase](https://www.npmjs.com/package/firebase) @@ -12,22 +12,28 @@ * The call graph has been improved to resolve method calls in more cases. This may produce more security alerts. +* TypeScript 3.6 and 3.7 features are now supported. + +* Automatic classification of generated files has been improved, in particular files generated by Doxygen are now recognized. + ## New queries | **Query** | **Tags** | **Purpose** | |---------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | Unused index variable (`js/unused-index-variable`) | correctness | Highlights loops that iterate over an array, but do not use the index variable to access array elements, indicating a possible typo or logic error. Results are shown on LGTM by default. | -| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are not shown on LGTM by default. | +| Loop bound injection (`js/loop-bound-injection`) | security, external/cwe/cwe-834 | Highlights loops where a user-controlled object with an arbitrary .length value can trick the server to loop indefinitely. Results are shown on LGTM by default. | | Suspicious method name (`js/suspicious-method-name-declaration`) | correctness, typescript, methods | Highlights suspiciously named methods where the developer likely meant to write a constructor or function. Results are shown on LGTM by default. | | Shell command built from environment values (`js/shell-command-injection-from-environment`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights shell commands that may change behavior inadvertently depending on the execution environment, indicating a possible violation of [CWE-78](https://cwe.mitre.org/data/definitions/78.html). Results are shown on LGTM by default.| | Use of returnless function (`js/use-of-returnless-function`) | maintainability, correctness | Highlights calls where the return value is used, but the callee never returns a value. Results are shown on LGTM by default. | | Useless regular expression character escape (`js/useless-regexp-character-escape`) | correctness, security, external/cwe/cwe-20 | Highlights regular expression strings with useless character escapes, indicating a possible violation of [CWE-20](https://cwe.mitre.org/data/definitions/20.html). Results are shown on LGTM by default. | | Unreachable method overloads (`js/unreachable-method-overloads`) | correctness, typescript | Highlights method overloads that are impossible to use from client code. Results are shown on LGTM by default. | +| Ignoring result from pure array method (`js/ignore-array-result`) | maintainability, correctness | Highlights calls to array methods without side effects where the return value is ignored. Results are shown on LGTM by default. | ## Changes to existing queries | **Query** | **Expected impact** | **Change** | |--------------------------------|------------------------------|---------------------------------------------------------------------------| +| Double escaping or unescaping (`js/double-escaping`) | More results | This rule now detects additional escaping and unescaping functions. | | Incomplete string escaping or encoding (`js/incomplete-sanitization`) | Fewer false-positive results | This rule now recognizes additional ways delimiters can be stripped away. | | Client-side cross-site scripting (`js/xss`) | More results, fewer false-positive results | More potential vulnerabilities involving functions that manipulate DOM attributes are now recognized, and more sanitizers are detected. | | Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving functions that manipulate DOM event handler attributes are now recognized. | @@ -40,6 +46,8 @@ | Reflected cross-site scripting (`js/reflected-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. | | Stored cross-site scripting (`js/stored-xss`) | Fewer false-positive results | The query now recognizes more sanitizers. | | Uncontrolled command line (`js/command-line-injection`) | More results | This query now treats responses from servers as untrusted. | +| Uncontrolled data used in path expression (`js/path-injection`) | Fewer false-positive results | This query now recognizes calls to Express `sendFile` as safe in some cases. | +| Unknown directive (`js/unknown-directive`) | Fewer false positive results | This query no longer flags uses of ":", which is sometimes used like a directive. | ## Changes to QL libraries diff --git a/change-notes/1.23/analysis-python.md b/change-notes/1.23/analysis-python.md index 5f5c288bbec..6cea1745284 100644 --- a/change-notes/1.23/analysis-python.md +++ b/change-notes/1.23/analysis-python.md @@ -19,4 +19,9 @@ | **Query** | **Expected impact** | **Change** | |----------------------------|------------------------|------------| | Unreachable code | Fewer false positives | Analysis now accounts for uses of `contextlib.suppress` to suppress exceptions. | +| `__iter__` method returns a non-iterator | Better alert message | Alert now highlights which class is expected to be an iterator. | + +## Changes to QL libraries + +* Django library now recognizes positional arguments from a `django.conf.urls.url` regex (Django version 1.x) diff --git a/change-notes/1.23/extractor-javascript.md b/change-notes/1.23/extractor-javascript.md index 18a67a65900..8b7a35f5b4f 100644 --- a/change-notes/1.23/extractor-javascript.md +++ b/change-notes/1.23/extractor-javascript.md @@ -5,6 +5,17 @@ ## Changes to code extraction * Asynchronous generator methods are now parsed correctly and no longer cause a spurious syntax error. +* Files in `node_modules` and `bower_components` folders are no longer extracted by default. If you still want to extract files from these folders, you can add the following filters to your `lgtm.yml` file (or add them to existing filters): + +```yaml +extraction: + javascript: + index: + filters: + - include: "**/node_modules" + - include: "**/bower_components" +``` + * Recognition of CommonJS modules has improved. As a result, some files that were previously extracted as global scripts are now extracted as modules. * Top-level `await` is now supported. diff --git a/config/identical-files.json b/config/identical-files.json index 25c696289b8..4fdac5c7fea 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -76,7 +76,11 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll", "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll", - "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll" + "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll" + ], + "IR IRType": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll" ], "IR Operand Tag": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/internal/OperandTag.qll", @@ -169,22 +173,39 @@ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintIRImports.qll" ], + "C++ SSA SSAConstructionImports": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll" + ], "C++ SSA AliasAnalysis": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll" ], - "C++ SSA SSAConstruction": [ + "C++ IR ValueNumberingImports": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll", + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll" + ], + "IR SSA SimpleSSA": [ + "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll" + ], + "IR SSA SSAConstruction": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll" + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll" ], - "C++ SSA PrintSSA": [ + "IR SSA PrintSSA": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll" + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll" ], - "C++ IR ValueNumber": [ + "IR ValueNumber": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll", "cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll", - "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll" + "cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll" ], "C++ IR ConstantAnalysis": [ "cpp/ql/src/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll", @@ -235,5 +256,9 @@ "C# IR PrintIRImports": [ "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/PrintIRImports.qll", "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll" + ], + "C# IR ValueNumberingImports": [ + "csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll", + "csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll" ] } diff --git a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql index 26cf42521e5..3ad43998d18 100644 --- a/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql +++ b/cpp/ql/src/Best Practices/Unused Entities/UnusedStaticVariables.ql @@ -21,6 +21,7 @@ from Variable v where v.isStatic() and v.hasDefinition() and + not v.isConstexpr() and not exists(VariableAccess a | a.getTarget() = v) and not v instanceof MemberVariable and not declarationHasSideEffects(v) and diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp index a8c2b1567a7..0af74559dff 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheck.qhelp @@ -2,36 +2,39 @@ "-//Semmle//qhelp//EN" "qhelp.dtd"> - -

- Checking for overflow of integer addition needs to be done with - care, because automatic type promotion can prevent the check - from working correctly. -

-
- -

- Use an explicit cast to make sure that the result of the addition is - not implicitly converted to a larger type. -

-
- - -

- On a typical architecture where short is 16 bits - and int is 32 bits, the operands of the addition are - automatically promoted to int, so it cannot overflow - and the result of the comparison is always false. -

-

- The code below implements the check correctly, by using an - explicit cast to make sure that the result of the addition - is unsigned short. -

- -
- -
  • Preserving Rules
  • -
  • Understand integer conversion rules
  • -
    + + +

    +Checking for overflow of integer addition needs to be done with +care, because automatic type promotion can prevent the check +from working as intended, with the same value (true +or false) always being returned. +

    +
    + +

    +Use an explicit cast to make sure that the result of the addition is +not implicitly converted to a larger type. +

    +
    + + +

    +On a typical architecture where short is 16 bits +and int is 32 bits, the operands of the addition are +automatically promoted to int, so it cannot overflow +and the result of the comparison is always false. +

    +

    +The code below implements the check correctly, by using an +explicit cast to make sure that the result of the addition +is unsigned short (which may overflow, in which case +the comparison would evaluate to true). +

    + +
    + +
  • Preserving Rules
  • +
  • Understand integer conversion rules
  • +
    diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp index 4da403cdf51..0e1e539ccb9 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp +++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadAdditionOverflowCheckExample1.cpp @@ -1,3 +1,4 @@ bool checkOverflow(unsigned short x, unsigned short y) { - return (x + y < x); // BAD: x and y are automatically promoted to int. + // BAD: comparison is always false due to type promotion + return (x + y < x); } diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql index 9f28862b273..d4b23265eed 100644 --- a/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql +++ b/cpp/ql/src/Likely Bugs/Arithmetic/ComparisonPrecedence.ql @@ -18,4 +18,4 @@ where co.getAChild() = chco and not chco.isParenthesised() and not co.isFromUninstantiatedTemplate(_) -select co, "Check the comparison operator precedence." +select co, "Comparison as an operand to another comparison." diff --git a/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/BufferAccess.qll b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/BufferAccess.qll new file mode 100644 index 00000000000..286db31020f --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/BufferAccess.qll @@ -0,0 +1,174 @@ +import cpp +import semmle.code.cpp.dataflow.TaintTracking +private import semmle.code.cpp.dataflow.RecursionPrevention + +/** + * A buffer which includes an allocation size. + */ +abstract class BufferWithSize extends DataFlow::Node { + abstract Expr getSizeExpr(); + + BufferAccess getAnAccess() { + any(BufferWithSizeConfig bsc).hasFlow(this, DataFlow::exprNode(result.getPointer())) + } +} + +/** An allocation function. */ +abstract class Alloc extends Function { } + +/** + * Allocation functions identified by the QL for C/C++ standard library. + */ +class DefaultAlloc extends Alloc { + DefaultAlloc() { allocationFunction(this) } +} + +/** A buffer created through a call to an allocation function. */ +class AllocBuffer extends BufferWithSize { + FunctionCall call; + + AllocBuffer() { + asExpr() = call and + call.getTarget() instanceof Alloc + } + + override Expr getSizeExpr() { result = call.getArgument(0) } +} + +/** + * Find accesses of buffers for which we have a size expression. + */ +private class BufferWithSizeConfig extends TaintTracking::Configuration { + BufferWithSizeConfig() { this = "BufferWithSize" } + + override predicate isSource(DataFlow::Node n) { n = any(BufferWithSize b) } + + override predicate isSink(DataFlow::Node n) { n.asExpr() = any(BufferAccess ae).getPointer() } + + override predicate isSanitizer(DataFlow::Node s) { + s = any(BufferWithSize b) and + s.asExpr().getControlFlowScope() instanceof Alloc + } +} + +/** + * An access (read or write) to a buffer, provided as a pair of + * a pointer to the buffer and the length of data to be read or written. + * Extend this class to support different kinds of buffer access. + */ +abstract class BufferAccess extends Locatable { + /** Gets the pointer to the buffer being accessed. */ + abstract Expr getPointer(); + + /** Gets the length of the data being read or written by this buffer access. */ + abstract Expr getAccessedLength(); +} + +/** + * A buffer access through an array expression. + */ +class ArrayBufferAccess extends BufferAccess, ArrayExpr { + override Expr getPointer() { result = this.getArrayBase() } + + override Expr getAccessedLength() { result = this.getArrayOffset() } +} + +/** + * A buffer access through an overloaded array expression. + */ +class OverloadedArrayBufferAccess extends BufferAccess, OverloadedArrayExpr { + override Expr getPointer() { result = this.getQualifier() } + + override Expr getAccessedLength() { result = this.getAnArgument() } +} + +/** + * A buffer access through pointer arithmetic. + */ +class PointerArithmeticAccess extends BufferAccess, Expr { + PointerArithmeticOperation p; + + PointerArithmeticAccess() { + this = p and + p.getAnOperand().getType().getUnspecifiedType() instanceof IntegralType and + not p.getParent() instanceof ComparisonOperation + } + + override Expr getPointer() { + result = p.getAnOperand() and + result.getType().getUnspecifiedType() instanceof PointerType + } + + override Expr getAccessedLength() { + result = p.getAnOperand() and + result.getType().getUnspecifiedType() instanceof IntegralType + } +} + +/** + * A pair of buffer accesses through a call to memcpy. + */ +class MemCpy extends BufferAccess, FunctionCall { + MemCpy() { getTarget().hasName("memcpy") } + + override Expr getPointer() { + result = getArgument(0) or + result = getArgument(1) + } + + override Expr getAccessedLength() { result = getArgument(2) } +} + +class StrncpySizeExpr extends BufferAccess, FunctionCall { + StrncpySizeExpr() { getTarget().hasName("strncpy") } + + override Expr getPointer() { + result = getArgument(0) or + result = getArgument(1) + } + + override Expr getAccessedLength() { result = getArgument(2) } +} + +class RecvSizeExpr extends BufferAccess, FunctionCall { + RecvSizeExpr() { getTarget().hasName("recv") } + + override Expr getPointer() { result = getArgument(1) } + + override Expr getAccessedLength() { result = getArgument(2) } +} + +class SendSizeExpr extends BufferAccess, FunctionCall { + SendSizeExpr() { getTarget().hasName("send") } + + override Expr getPointer() { result = getArgument(1) } + + override Expr getAccessedLength() { result = getArgument(2) } +} + +class SnprintfSizeExpr extends BufferAccess, FunctionCall { + SnprintfSizeExpr() { getTarget().hasName("snprintf") } + + override Expr getPointer() { result = getArgument(0) } + + override Expr getAccessedLength() { result = getArgument(1) } +} + +class MemcmpSizeExpr extends BufferAccess, FunctionCall { + MemcmpSizeExpr() { getTarget().hasName("Memcmp") } + + override Expr getPointer() { + result = getArgument(0) or + result = getArgument(1) + } + + override Expr getAccessedLength() { result = getArgument(2) } +} + +class MallocSizeExpr extends BufferAccess, FunctionCall { + MallocSizeExpr() { getTarget().hasName("malloc") } + + override Expr getPointer() { none() } + + override Expr getAccessedLength() { result = getArgument(1) } +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayBad.cpp b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayBad.cpp new file mode 100644 index 00000000000..0185bbb9da6 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayBad.cpp @@ -0,0 +1,6 @@ +int get_number_from_network(); + +int process_network(int[] buff, int buffSize) { + int i = ntohl(get_number_from_network()); + return buff[i]; +} \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayGood.cpp b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayGood.cpp new file mode 100644 index 00000000000..2d5ec265261 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayGood.cpp @@ -0,0 +1,10 @@ +uint32_t get_number_from_network(); + +int process_network(int[] buff, uint32_t buffSize) { + uint32_t i = ntohl(get_number_from_network()); + if (i < buffSize) { + return buff[i]; + } else { + return -1; + } +} \ No newline at end of file diff --git a/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayNoBound.qll b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayNoBound.qll new file mode 100644 index 00000000000..3e1f74bfbed --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayNoBound.qll @@ -0,0 +1,33 @@ +import cpp +import semmle.code.cpp.dataflow.DataFlow +import semmle.code.cpp.controlflow.Guards +import BufferAccess +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +class NetworkFunctionCall extends FunctionCall { + NetworkFunctionCall() { + getTarget().hasName("ntohd") or + getTarget().hasName("ntohf") or + getTarget().hasName("ntohl") or + getTarget().hasName("ntohll") or + getTarget().hasName("ntohs") + } +} + +class NetworkToBufferSizeConfiguration extends DataFlow::Configuration { + NetworkToBufferSizeConfiguration() { this = "NetworkToBufferSizeConfiguration" } + + override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof NetworkFunctionCall } + + override predicate isSink(DataFlow::Node node) { + node.asExpr() = any(BufferAccess ba).getAccessedLength() + } + + override predicate isBarrier(DataFlow::Node node) { + exists(GuardCondition gc, GVN gvn | + gc.getAChild*() = gvn.getAnExpr() and + globalValueNumber(node.asExpr()) = gvn and + gc.controls(node.asExpr().getBasicBlock(), _) + ) + } +} diff --git a/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayNoBoundOpenSource.qhelp b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayNoBoundOpenSource.qhelp new file mode 100644 index 00000000000..fc8f309f73a --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayNoBoundOpenSource.qhelp @@ -0,0 +1,54 @@ + + + + +

    +Data received over a network connection may be received in a different byte order than +the byte order used by the local host, making the data difficult to process. To address this, +data received over the wire is usually converted to host byte order by a call to a network-to-host +byte order function, such as ntohl. +

    +

    +The use of a network-to-host byte order function is therefore a good indicator that the returned +value is unvalidated data retrieved from the network, and should not be used without further +validation. In particular, the returned value should not be used as an array index or array length +value without validation, as this could result in a buffer overflow vulnerability. +

    +
    + + +

    +Validate data returned by network-to-host byte order functions before use and especially before +using the value as an array index or bound. +

    +
    + + +

    In the example below, network data is retrieved and passed to ntohl to convert +it to host byte order. The data is then used as an index in an array access expression. However, +there is no validation that the data returned by ntohl is within the bounds of the array, +which could lead to reading outside the bounds of the buffer. +

    + +

    In the corrected example, the returned data is validated against the known size of the buffer, +before being used as an array index.

    + +
    + + +
  • + + ntohl - winsock reference + +
  • +
  • + + ntohl - Linux man page + +
  • + +
    + +
    diff --git a/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayNoBoundOpenSource.ql b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayNoBoundOpenSource.ql new file mode 100644 index 00000000000..d6d0a55d148 --- /dev/null +++ b/cpp/ql/src/Likely Bugs/Memory Management/Buffer Overflow/NtohlArrayNoBoundOpenSource.ql @@ -0,0 +1,17 @@ +/** + * @id cpp/network-to-host-function-as-array-bound + * @name Untrusted network-to-host usage + * @description Using the result of a network-to-host byte order function, such as ntohl, as an + * array bound or length value without checking it may result in buffer overflows or + * other vulnerabilties. + * @kind problem + * @problem.severity error + */ + +import cpp +import NtohlArrayNoBound +import semmle.code.cpp.dataflow.DataFlow + +from NetworkToBufferSizeConfiguration bufConfig, DataFlow::Node source, DataFlow::Node sink +where bufConfig.hasFlow(source, sink) +select sink, "Unchecked use of data from network function $@", source, source.toString() diff --git a/cpp/ql/src/Microsoft/SAL.qll b/cpp/ql/src/Microsoft/SAL.qll index b9f6c2d037d..fe6b455fa9b 100644 --- a/cpp/ql/src/Microsoft/SAL.qll +++ b/cpp/ql/src/Microsoft/SAL.qll @@ -2,29 +2,45 @@ import cpp class SALMacro extends Macro { SALMacro() { - this.getFile().getBaseName() = "sal.h" or - this.getFile().getBaseName() = "specstrings_strict.h" or - this.getFile().getBaseName() = "specstrings.h" + exists(string filename | filename = this.getFile().getBaseName() | + filename = "sal.h" or + filename = "specstrings_strict.h" or + filename = "specstrings.h" or + filename = "w32p.h" or + filename = "minwindef.h" + ) and + ( + // Dialect for Windows 8 and above + this.getName().matches("\\_%\\_") + or + // Dialect for Windows 7 + this.getName().matches("\\_\\_%") + ) } } +pragma[noinline] +predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) } + class SALAnnotation extends MacroInvocation { SALAnnotation() { this.getMacro() instanceof SALMacro and - not exists(this.getParentInvocation()) + isTopLevelMacroAccess(this) } /** Returns the `Declaration` annotated by `this`. */ - Declaration getDeclaration() { annotatesAt(this, result.getADeclarationEntry(), _, _) } + Declaration getDeclaration() { + annotatesAt(this, result.getADeclarationEntry(), _, _) and + not result instanceof Type // exclude typedefs + } /** Returns the `DeclarationEntry` annotated by `this`. */ - DeclarationEntry getDeclarationEntry() { annotatesAt(this, result, _, _) } + DeclarationEntry getDeclarationEntry() { + annotatesAt(this, result, _, _) and + not result instanceof TypeDeclarationEntry // exclude typedefs + } } -/* - * Particular SAL annotations of interest - */ - class SALCheckReturn extends SALAnnotation { SALCheckReturn() { exists(SALMacro m | m = this.getMacro() | @@ -39,8 +55,8 @@ class SALNotNull extends SALAnnotation { exists(SALMacro m | m = this.getMacro() | not m.getName().matches("%\\_opt\\_%") and ( - m.getName().matches("\\_In%") or - m.getName().matches("\\_Out%") or + m.getName().matches("_In%") or + m.getName().matches("_Out%") or m.getName() = "_Ret_notnull_" ) ) and @@ -63,42 +79,124 @@ class SALMaybeNull extends SALAnnotation { } } -/* - * Implementation details +/////////////////////////////////////////////////////////////////////////////// +// Implementation details +/** + * Holds if `a` annotates the declaration entry `d` and + * its start position is the `idx`th position in `file` that holds a SAL element. */ - -private predicate annotatesAt(SALAnnotation a, DeclarationEntry e, File file, int idx) { - a = salElementAt(file, idx) and - ( - // Base case: `a` right before `e` - e = salElementAt(file, idx + 1) - or - // Recursive case: `a` right before some annotation on `e` - annotatesAt(_, e, file, idx + 1) - ) -} - -library class SALElement extends Element { - SALElement() { - this instanceof DeclarationEntry or - this instanceof SALAnnotation - } -} - -/** Gets the `idx`th `SALElement` in `file`. */ -private SALElement salElementAt(File file, int idx) { - interestingLoc(file, result, interestingStartPos(file, idx)) +predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) { + annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx) } /** - * Holds if an SALElement element at character `result` comes at - * position `idx` in `file`. + * Holds if `pos` is the `idx`th position in `file` that holds a SAL element, + * which annotates the declaration entry `d` (by occurring before it without + * any other declaration entries in between). */ -private int interestingStartPos(File file, int idx) { - result = rank[idx](int otherStart | interestingLoc(file, _, otherStart)) +// For performance reasons, do not mention the annotation itself here, +// but compute with positions instead. This performs better on databases +// with many annotations at the same position. +private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File file, int idx) { + pos = salRelevantPositionAt(file, idx) and + salAnnotationPos(pos) and + ( + // Base case: `pos` right before `d` + d.(SALElement).getStartPosition() = salRelevantPositionAt(file, idx + 1) + or + // Recursive case: `pos` right before some annotation on `d` + annotatesAtPosition(_, d, file, idx + 1) + ) } -/** Holds if `element` in `file` is at character `startPos`. */ -private predicate interestingLoc(File file, SALElement element, int startPos) { - element.getLocation().charLoc(file, startPos, _) +/** + * A parameter annotated by one or more SAL annotations. + */ +class SALParameter extends Parameter { + /** One of this parameter's annotations. */ + SALAnnotation a; + + SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) } + + predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") } + + predicate isOut() { a.getMacroName().toLowerCase().matches("%\\_out%") } + + predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") } +} + +/** + * A SAL element, i.e. a SAL annotation or a declaration entry + * that may have SAL annotations. + */ +library class SALElement extends Element { + SALElement() { + containsSALAnnotation(this.(DeclarationEntry).getFile()) or + this instanceof SALAnnotation + } + + predicate hasStartPosition(File file, int line, int col) { + exists(Location loc | loc = this.getLocation() | + file = loc.getFile() and + line = loc.getStartLine() and + col = loc.getStartColumn() + ) + } + + predicate hasEndPosition(File file, int line, int col) { + exists(Location loc | + loc = this.(FunctionDeclarationEntry).getBlock().getLocation() + or + this = any(VariableDeclarationEntry vde | + vde.isDefinition() and + loc = vde.getVariable().getInitializer().getLocation() + ) + | + file = loc.getFile() and + line = loc.getEndLine() and + col = loc.getEndColumn() + ) + } + + SALPosition getStartPosition() { + exists(File file, int line, int col | + this.hasStartPosition(file, line, col) and + result = MkSALPosition(file, line, col) + ) + } +} + +/** Holds if `file` contains a SAL annotation. */ +pragma[noinline] +private predicate containsSALAnnotation(File file) { any(SALAnnotation a).getFile() = file } + +/** + * A source-file position of a `SALElement`. Unlike location, this denotes a + * point in the file rather than a range. + */ +private newtype SALPosition = + MkSALPosition(File file, int line, int col) { + exists(SALElement e | + e.hasStartPosition(file, line, col) + or + e.hasEndPosition(file, line, col) + ) + } + +/** Holds if `pos` is the start position of a SAL annotation. */ +pragma[noinline] +private predicate salAnnotationPos(SALPosition pos) { + any(SALAnnotation a).(SALElement).getStartPosition() = pos +} + +/** + * Gets the `idx`th position in `file` that holds a SAL element, + * ordering positions lexicographically by their start line and start column. + */ +private SALPosition salRelevantPositionAt(File file, int idx) { + result = rank[idx](SALPosition pos, int line, int col | + pos = MkSALPosition(file, line, col) + | + pos order by line, col + ) } diff --git a/cpp/ql/src/Microsoft/SAL/IgnoreReturnValueSAL.ql b/cpp/ql/src/Microsoft/SAL/IgnoreReturnValueSAL.ql new file mode 100644 index 00000000000..569f684a4c3 --- /dev/null +++ b/cpp/ql/src/Microsoft/SAL/IgnoreReturnValueSAL.ql @@ -0,0 +1,25 @@ +/** + * @name SAL requires inspecting return value + * @description When a return value is discarded even though the SAL annotation + * requires inspecting it, a recoverable error may turn into a + * whole-program crash. + * @kind problem + * @problem.severity warning + * @tags reliability + * external/cwe/cwe-573 + * external/cwe/cwe-252 + * @opaque-id SM02344 + * @microsoft.severity Important + * @id cpp/ignorereturnvaluesal + */ + +import Microsoft.SAL + +from Function f, FunctionCall call +where + call.getTarget() = f and + call instanceof ExprInVoidContext and + any(SALCheckReturn a).getDeclaration() = f and + not any(Options o).okToIgnoreReturnValue(call) +select call, "Return value of $@ discarded although a SAL annotation " + "requires inspecting it.", + f, f.getName() diff --git a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql index c704506a4fa..92f679a0f1f 100644 --- a/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql +++ b/cpp/ql/src/Security/CWE/CWE-190/ComparisonWithWiderType.ql @@ -5,7 +5,7 @@ * @id cpp/comparison-with-wider-type * @kind problem * @problem.severity warning - * @precision medium + * @precision high * @tags reliability * security * external/cwe/cwe-190 diff --git a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.qhelp b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.qhelp new file mode 100644 index 00000000000..8e6a8903483 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.qhelp @@ -0,0 +1,59 @@ + + + + +

    A common pattern is to initialize a local variable by calling another function (an +"initialization" function) with the address of the local variable as a pointer argument. That +function is then responsible for writing to the memory location referenced by the pointer.

    + +

    In some cases, the called function may not always write to the memory pointed to by the +pointer argument. In such cases, the function will typically return a "status" code, informing the +caller as to whether the initialization succeeded or not. If the caller does not check the status +code before reading the local variable, it may read unitialized memory, which can result in +unexpected behavior.

    + +
    + + +

    When using a initialization function that does not guarantee to initialize the memory pointed to +by the passed pointer, and returns a status code to indicate whether such initialization occurred, +the status code should be checked before reading from the local variable.

    + +
    + + +

    In this hypothetical example we have code for managing a series of devices. The code +includes a DeviceConfig struct that can represent properties about each device. +The initDeviceConfig function can be called to initialize one of these structures, by +providing a "device number", which can be used to look up the appropriate properties in some data +store. If an invalid device number is provided, the function returns a status code of +-1, and does not initialize the provided pointer.

    + +

    In the first code sample below, the notify function calls the +initDeviceConfig function with a pointer to the local variable config, +which is then subsequently accessed to fetch properties of the device. However, the code does not +check the return value from the function call to initDeviceConfig. If the +device number passed to the notify function was invalid, the +initDeviceConfig function will leave the config variable uninitialized, +which will result in the notify function accessing uninitialized memory.

    + + + +

    To fix this, the code needs to check that the return value of the call to +initDeviceConfig is zero. If that is true, then the calling code can safely assume +that the local variable has been initialized.

    + + + +
    + + +
  • + Wikipedia: + Uninitialized variable. +
  • + +
    +
    \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql new file mode 100644 index 00000000000..f9eb2fe5400 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariable.ql @@ -0,0 +1,33 @@ +/** + * @name Conditionally uninitialized variable + * @description When an initialization function is used to initialize a local variable, but the + * returned status code is not checked, the variable may be left in an uninitialized + * state, and reading the variable may result in undefined behavior. + * @kind problem + * @problem.severity warning + * @opaque-id SM02313 + * @id cpp/conditionally-uninitialized-variable + * @tags security + * external/cwe/cwe-457 + */ + +import cpp +import semmle.code.cpp.controlflow.SSA +private import UninitializedVariables + +from + ConditionallyInitializedVariable v, ConditionalInitializationFunction f, + ConditionalInitializationCall call, string defined, Evidence e +where + exists(v.getARiskyAccess(f, call, e)) and + ( + if e = DefinitionInSnapshot() + then defined = "" + else + if e = SuggestiveSALAnnotation() + then defined = "externally defined (SAL) " + else defined = "externally defined (CSV) " + ) +select call, + "The status of this call to " + defined + + "$@ is not checked, potentially leaving $@ uninitialized.", f, f.getName(), v, v.getName() diff --git a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableBad.c b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableBad.c new file mode 100644 index 00000000000..1f281f3cfd9 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableBad.c @@ -0,0 +1,25 @@ +struct DeviceConfig { + bool isEnabled; + int channel; +}; + +int initDeviceConfig(DeviceConfig *ref, int deviceNumber) { + if (deviceNumber >= getMaxDevices()) { + // No device with that number, return -1 to indicate failure + return -1; + } + // Device with that number, fetch parameters and initialize struct + ref->isEnabled = fetchIsDeviceEnabled(deviceNumber); + ref->channel = fetchDeviceChannel(deviceNumber); + // Return 0 to indicate success + return 0; +} + +int notify(int deviceNumber) { + DeviceConfig config; + initDeviceConfig(&config, deviceNumber); + // BAD: Using config without checking the status code that is returned + if (config->isEnabled) { + notifyChannel(config->channel); + } +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableGood.c b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableGood.c new file mode 100644 index 00000000000..a9dcc06c9a5 --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-457/ConditionallyUninitializedVariableGood.c @@ -0,0 +1,27 @@ +struct DeviceConfig { + bool isEnabled; + int channel; +}; + +int initDeviceConfig(DeviceConfig *ref, int deviceNumber) { + if (deviceNumber >= getMaxDevices()) { + // No device with that number, return -1 to indicate failure + return -1; + } + // Device with that number, fetch parameters and initialize struct + ref->isEnabled = fetchIsDeviceEnabled(deviceNumber); + ref->channel = fetchDeviceChannel(deviceNumber); + // Return 0 to indicate success + return 0; +} + +void notify(int deviceNumber) { + DeviceConfig config; + int statusCode = initDeviceConfig(&config, deviceNumber); + if (statusCode == 0) { + // GOOD: Status code returned by initialization function is checked, so this is safe + if (config->isEnabled) { + notifyChannel(config->channel); + } + } +} \ No newline at end of file diff --git a/cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll b/cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll new file mode 100644 index 00000000000..240bd7aa25e --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-457/InitializationFunctions.qll @@ -0,0 +1,690 @@ +/** + * Provides classes and predicates for identifying functions that initialize their arguments. + */ + +import cpp +import external.ExternalArtifact +private import semmle.code.cpp.dispatch.VirtualDispatch +import semmle.code.cpp.NestedFields +import Microsoft.SAL +import semmle.code.cpp.controlflow.Guards + +/** A context under which a function may be called. */ +private newtype TContext = + /** No specific call context. */ + NoContext() or + /** + * The call context is that the given other parameter is null. + * + * This context is created for all parameters that are null checked in the body of the function. + */ + ParamNull(Parameter p) { p = any(ParameterNullCheck pnc).getParameter() } or + /** + * The call context is that the given other parameter is not null. + * + * This context is created for all parameters that are null checked in the body of the function. + */ + ParamNotNull(Parameter p) { p = any(ParameterNullCheck pnc).getParameter() } + +/** + * A context under which a function may be called. + * + * Some functions may conditionally initialize a parameter depending on the value of another + * parameter. Consider: + * ``` + * int MyInitFunction(int* paramToBeInitialized, int* paramToCheck) { + * if (!paramToCheck) { + * // fail! + * return -1; + * } + * paramToBeInitialized = 0; + * } + * ``` + * In this case, whether `paramToBeInitialized` is initialized when this function call completes + * depends on whether `paramToCheck` is or is not null. A call-context insensitive analysis will + * determine that any call to this function may leave the parameter uninitialized, even if the + * argument to paramToCheck is guaranteed to be non-null (`&foo`, for example). + * + * This class models call contexts that can be considered when calculating whether a given parameter + * initializes or not. The supported contexts are: + * - `ParamNull(otherParam)` - the given `otherParam` is considered to be null. Applies when + * exactly one parameter other than this one is null checked. + * - `ParamNotNull(otherParam)` - the given `otherParam` is considered to be not null. Applies when + * exactly one parameter other than this one is null checked. + * - `NoContext()` - applies in all other circumstances. + */ +class Context extends TContext { + string toString() { + this = NoContext() and result = "NoContext" + or + this = ParamNull(any(Parameter p | result = "ParamNull(" + p.getName() + ")")) + or + this = ParamNotNull(any(Parameter p | result = "ParamNotNull(" + p.getName() + ")")) + } +} + +/** + * A check against a parameter. + */ +abstract class ParameterCheck extends Expr { + /** + * Gets a successor of this check that should be ignored for the given context. + */ + abstract ControlFlowNode getIgnoredSuccessorForContext(Context c); +} + +/** A null-check expression on a parameter. */ +class ParameterNullCheck extends ParameterCheck { + Parameter p; + ControlFlowNode nullSuccessor; + ControlFlowNode notNullSuccessor; + + ParameterNullCheck() { + this.isCondition() and + p.getFunction() instanceof InitializationFunction and + p.getType().getUnspecifiedType() instanceof PointerType and + exists(VariableAccess va | va = p.getAnAccess() | + nullSuccessor = getATrueSuccessor() and + notNullSuccessor = getAFalseSuccessor() and + ( + va = this.(NotExpr).getOperand() or + va = any(EQExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or + va = getAssertedFalseCondition(this) or + va = any(NEExpr eq | + eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0" + ).getAnOperand() + ) + or + nullSuccessor = getAFalseSuccessor() and + notNullSuccessor = getATrueSuccessor() and + ( + va = this or + va = any(NEExpr eq | eq = this and eq.getAnOperand().getValue() = "0").getAnOperand() or + va = any(EQExpr eq | + eq = getAssertedFalseCondition(this) and eq.getAnOperand().getValue() = "0" + ).getAnOperand() + ) + ) + } + + /** The parameter being null-checked. */ + Parameter getParameter() { result = p } + + override ControlFlowNode getIgnoredSuccessorForContext(Context c) { + c = ParamNull(p) and result = notNullSuccessor + or + c = ParamNotNull(p) and result = nullSuccessor + } + + /** The successor at which the parameter is confirmed to be null. */ + ControlFlowNode getNullSuccessor() { result = nullSuccessor } + + /** The successor at which the parameter is confirmed to be not-null. */ + ControlFlowNode getNotNullSuccessor() { result = notNullSuccessor } +} + +/** + * An entry in a CSV file in cond-init that contains externally defined functions that are + * conditional initializers. These files are typically produced by running the + * ConditionallyInitializedFunction companion query. + */ +class ValidatedExternalCondInitFunction extends ExternalData { + ValidatedExternalCondInitFunction() { this.getDataPath().matches("%cond-init%.csv") } + + predicate isExternallyVerified(Function f, int param) { + functionSignature(f, getField(1), getField(2)) and param = getFieldAsInt(3) + } +} + +/** + * The type of evidence used to determine whether a function initializes a parameter. + */ +newtype Evidence = + /** + * The function is defined in the snapshot, and the CFG has been analyzed to determine that the + * parameter is not initialized on at least one path to the exit. + */ + DefinitionInSnapshot() or + /** + * The function is externally defined, but the parameter has an `_out` SAL annotation which + * suggests that it is initialized in the function. + */ + SuggestiveSALAnnotation() or + /** + * We have been given a CSV file which indicates this parameter is conditionally initialized. + */ + ExternalEvidence() + +/** + * A call to an function which initializes one or more of its parameters. + */ +class InitializationFunctionCall extends FunctionCall { + Expr initializedArgument; + + InitializationFunctionCall() { initializedArgument = getAnInitializedArgument(this) } + + /** Gets a parameter that is initialized by this call. */ + Parameter getAnInitParameter() { result.getAnAccess() = initializedArgument } +} + +/** + * A variable access which is dereferenced then assigned to. + */ +private predicate isPointerDereferenceAssignmentTarget(VariableAccess target) { + target.getParent().(PointerDereferenceExpr) = any(Assignment e).getLValue() +} + +/** + * A function which initializes one or more of its parameters. + */ +class InitializationFunction extends Function { + int i; + Evidence evidence; + + InitializationFunction() { + evidence = DefinitionInSnapshot() and + ( + // Assignment by pointer dereferencing the parameter + isPointerDereferenceAssignmentTarget(this.getParameter(i).getAnAccess()) or + // Field wise assignment to the parameter + any(Assignment e).getLValue() = getAFieldAccess(this.getParameter(i)) or + i = this + .(MemberFunction) + .getAnOverridingFunction+() + .(InitializationFunction) + .initializedParameter() or + getParameter(i) = any(InitializationFunctionCall c).getAnInitParameter() + ) + or + // If we have no definition, we look at SAL annotations + not this.isDefined() and + this.getParameter(i).(SALParameter).isOut() and + evidence = SuggestiveSALAnnotation() + or + // We have some external information that this function conditionally initializes + not this.isDefined() and + any(ValidatedExternalCondInitFunction vc).isExternallyVerified(this, i) and + evidence = ExternalEvidence() + } + + /** Gets a parameter index which is initialized by this function. */ + int initializedParameter() { result = i } + + /** Gets a `ControlFlowNode` which assigns a new value to the parameter with the given index. */ + ControlFlowNode paramReassignment(int index) { + index = i and + ( + result = this.getParameter(i).getAnAccess() and + ( + result = any(Assignment a).getLValue().(PointerDereferenceExpr).getOperand() + or + // Field wise assignment to the parameter + result = any(Assignment a).getLValue().(FieldAccess).getQualifier() + or + // Assignment to a nested field of the parameter + result = any(Assignment a).getLValue().(NestedFieldAccess).getUltimateQualifier() + or + result = getAnInitializedArgument(any(Call c)) + or + exists(IfStmt check | result = check.getCondition().getAChild*() | + paramReassignmentCondition(check) + ) + ) + or + result = any(AssumeExpr e | + e.getEnclosingFunction() = this and e.getAChild().(Literal).getValue() = "0" + ) + ) + } + + /** + * Helper predicate: holds if the `if` statement `check` contains a + * reassignment to the `i`th parameter within its `then` statement. + */ + pragma[noinline] + private predicate paramReassignmentCondition(IfStmt check) { + this.paramReassignment(i).getEnclosingStmt().getParentStmt*() = check.getThen() + } + + /** Holds if `n` can be reached without the parameter at `index` being reassigned. */ + predicate paramNotReassignedAt(ControlFlowNode n, int index, Context c) { + c = getAContext(index) and + ( + not exists(this.getEntryPoint()) and index = i and n = this + or + n = this.getEntryPoint() and index = i + or + exists(ControlFlowNode mid | paramNotReassignedAt(mid, index, c) | + n = mid.getASuccessor() and + not n = paramReassignment(index) and + /* + * Ignore successor edges where the parameter is null, because it is then confirmed to be + * initialized. + */ + + not exists(ParameterNullCheck nullCheck | + nullCheck = mid and + nullCheck = getANullCheck(index) and + n = nullCheck.getNullSuccessor() + ) and + /* + * Ignore successor edges which are excluded by the given context + */ + + not exists(ParameterCheck paramCheck | paramCheck = mid | + n = paramCheck.getIgnoredSuccessorForContext(c) + ) + ) + ) + } + + /** Gets a null-check on the parameter at `index`. */ + private ParameterNullCheck getANullCheck(int index) { + getParameter(index) = result.getParameter() + } + + /** Gets a parameter which is not at the given index. */ + private Parameter getOtherParameter(int index) { + index = i and + result = getAParameter() and + not result.getIndex() = index + } + + /** + * Gets a call `Context` that is applicable when considering whether parameter at the `index` can + * be conditionally initialized. + */ + Context getAContext(int index) { + index = i and + /* + * If there is one and only one other parameter which is null checked in the body of the method, + * then we have two contexts to consider - that the other param is null, or that the other param + * is not null. + */ + + if + strictcount(Parameter p | + exists(Context c | c = ParamNull(p) or c = ParamNotNull(p)) and + p = getOtherParameter(index) + ) = 1 + then + exists(Parameter p | p = getOtherParameter(index) | + result = ParamNull(p) or result = ParamNotNull(p) + ) + else + // Otherwise, only consider NoContext. + result = NoContext() + } + + /** + * Holds if this function should be whitelisted - that is, not considered as conditionally + * initializing its parameters. + */ + predicate whitelisted() { + exists(string name | this.hasName(name) | + // Return value is not a success code but the output functions never fail. + name.matches("_Interlocked%") + or + // Functions that never fail, according to MSDN. + name = "QueryPerformanceCounter" + or + name = "QueryPerformanceFrequency" + or + // Functions that never fail post-Vista, according to MSDN. + name = "InitializeCriticalSectionAndSpinCount" + or + // `rand_s` writes 0 to a non-null argument if it fails, according to MSDN. + name = "rand_s" + or + // IntersectRect initializes the argument regardless of whether the input intersects + name = "IntersectRect" + or + name = "SetRect" + or + name = "UnionRect" + or + // These functions appears to have an incorrect CFG, which leads to false positives + name = "PhysicalToLogicalDPIPoint" + or + name = "LogicalToPhysicalDPIPoint" + or + // Sets NtProductType to default on error + name = "RtlGetNtProductType" + or + // Our CFG is not sophisticated enough to detect that the argument is always initialized + name = "StringCchLengthA" + or + // All paths init the argument, and always returns SUCCESS. + name = "RtlUnicodeToMultiByteSize" + or + // All paths init the argument, and always returns SUCCESS. + name = "RtlMultiByteToUnicodeSize" + or + // All paths init the argument, and always returns SUCCESS. + name = "RtlUnicodeToMultiByteN" + or + // Always initializes argument + name = "RtlGetFirstRange" + or + // Destination range is zeroed out on failure, assuming first two parameters are valid + name = "memcpy_s" + or + // This zeroes the memory unconditionally + name = "SeCreateAccessState" + ) + } +} + +/** + * A function which initializes one or more of its parameters, but not on all paths. + */ +class ConditionalInitializationFunction extends InitializationFunction { + Context c; + + ConditionalInitializationFunction() { + c = this.getAContext(i) and + not this.whitelisted() and + exists(Type status | status = this.getType().getUnspecifiedType() | + status instanceof IntegralType or + status instanceof Enum + ) and + not this.getType().getName().toLowerCase() = "size_t" and + ( + /* + * If there is no definition, consider this to be conditionally initializing (based on either + * SAL or external data). + */ + + not evidence = DefinitionInSnapshot() + or + /* + * If this function is defined in this snapshot, then it conditionally initializes if there + * is at least one path through the function which doesn't initialize the parameter. + * + * Explicitly ignore pure virtual functions. + */ + + this.isDefined() and + this.paramNotReassignedAt(this, i, c) and + not this instanceof PureVirtualFunction + ) + } + + /** Gets the evidence associated with the given parameter. */ + Evidence getEvidence(int param) { + /* + * Note: due to the way the predicate dispatch interacts with fields, this needs to be + * implemented on this class, not `InitializationFunction`. If implemented on the latter it + * can return evidence that does not result in conditional initialization. + */ + + param = i and evidence = result + } + + /** Gets the index of a parameter which is conditionally initialized. */ + int conditionallyInitializedParameter(Context context) { result = i and context = c } +} + +/** + * More elaborate tracking, flagging cases where the status is checked after + * the potentially uninitialized variable has been used, and ignoring cases + * where the status is not checked but there is no use of the potentially + * uninitialized variable, may be obtained via `getARiskyAccess`. + */ +class ConditionalInitializationCall extends FunctionCall { + ConditionalInitializationFunction target; + + ConditionalInitializationCall() { target = getTarget(this) } + + /** Gets the argument passed for the given parameter to this call. */ + Expr getArgumentForParameter(Parameter p) { + p = getTarget().getAParameter() and + result = getArgument(p.getIndex()) + } + + /** + * Gets an argument conditionally initialized by this call. + */ + Expr getAConditionallyInitializedArgument(ConditionalInitializationFunction condTarget, Evidence e) { + condTarget = target and + exists(Context context | + result = getAConditionallyInitializedArgument(this, condTarget, context, e) + | + context = NoContext() + or + exists(Parameter otherP, Expr otherArg | + context = ParamNotNull(otherP) or + context = ParamNull(otherP) + | + otherArg = getArgumentForParameter(otherP) and + (otherArg instanceof AddressOfExpr implies context = ParamNotNull(otherP)) and + (otherArg.getType() instanceof ArrayType implies context = ParamNotNull(otherP)) and + (otherArg.getValue() = "0" implies context = ParamNull(otherP)) + ) + ) + } + + VariableAccess getAConditionallyInitializedVariable() { + not result.getTarget().getAnAssignedValue().getASuccessor+() = result and + // Should not be assigned field-wise prior to the call. + not exists(Assignment a, FieldAccess fa | + fa.getQualifier() = result.getTarget().getAnAccess() and + a.getLValue() = fa and + fa.getASuccessor+() = result + ) and + result = this + .getArgument(getTarget(this) + .(ConditionalInitializationFunction) + .conditionallyInitializedParameter(_)) + .(AddressOfExpr) + .getOperand() + } + + Variable getStatusVariable() { + exists(AssignExpr a | a.getLValue() = result.getAnAccess() | a.getRValue() = this) + or + result.getInitializer().getExpr() = this + } + + Expr getSuccessCheck() { + exists(this.getAFalseSuccessor()) and result = this + or + result = this.getParent() and + ( + result instanceof NotExpr or + result.(EQExpr).getAnOperand().getValue() = "0" or + result.(GEExpr).getLesserOperand().getValue() = "0" + ) + } + + Expr getFailureCheck() { + result = this.getParent() and + ( + result instanceof NotExpr or + result.(NEExpr).getAnOperand().getValue() = "0" or + result.(LTExpr).getLesserOperand().getValue() = "0" + ) + } + + private predicate inCheckedContext() { + exists(Call parent | this = parent.getAnArgument() | + parent.getTarget() instanceof Operator or + parent.getTarget().hasName("VerifyOkCatastrophic") + ) + } + + ControlFlowNode uncheckedReaches(LocalVariable var) { + ( + not exists(var.getInitializer()) and + var = this.getAConditionallyInitializedVariable().getTarget() and + if exists(this.getFailureCheck()) + then result = this.getFailureCheck().getATrueSuccessor() + else + if exists(this.getSuccessCheck()) + then result = this.getSuccessCheck().getAFalseSuccessor() + else ( + result = this.getASuccessor() and not this.inCheckedContext() + ) + ) + or + exists(ControlFlowNode mid | mid = uncheckedReaches(var) | + not mid = getStatusVariable().getAnAccess() and + not mid = var.getAnAccess() and + not exists(VariableAccess write | result = write and write = var.getAnAccess() | + write = any(AssignExpr a).getLValue() or + write = any(AddressOfExpr a).getOperand() + ) and + result = mid.getASuccessor() + ) + } + + VariableAccess getARiskyRead(Function f) { + f = this.getTarget() and + exists(this.getFile().getRelativePath()) and + result = this.uncheckedReaches(result.getTarget()) and + not this.(GuardCondition).controls(result.getBasicBlock(), _) + } +} + +/** + * Gets the position of an argument to the call which is initialized by the call. + */ +pragma[nomagic] +int initializedArgument(Call call) { + exists(InitializationFunction target | + target = getTarget(call) and + result = target.initializedParameter() + ) +} + +/** + * Gets an argument which is initialized by the call. + */ +Expr getAnInitializedArgument(Call call) { result = call.getArgument(initializedArgument(call)) } + +/** + * Gets the position of an argument to the call to the target which is conditionally initialized by + * the call, under the given context and evidence. + */ +pragma[nomagic] +int conditionallyInitializedArgument( + Call call, ConditionalInitializationFunction target, Context c, Evidence e +) { + target = getTarget(call) and + c = target.getAContext(result) and + e = target.getEvidence(result) and + result = target.conditionallyInitializedParameter(c) +} + +/** + * Gets an argument which is conditionally initialized by the call to the given target under the given context and evidence. + */ +Expr getAConditionallyInitializedArgument( + Call call, ConditionalInitializationFunction target, Context c, Evidence e +) { + result = call.getArgument(conditionallyInitializedArgument(call, target, c, e)) +} + +/** + * Gets the type signature for the functions parameters. + */ +string typeSig(Function f) { + result = concat(int i, Type pt | + pt = f.getParameter(i).getType() + | + pt.getUnspecifiedType().toString(), "," order by i + ) +} + +/** + * Holds where qualifiedName and typeSig make up the signature for the function. + */ +predicate functionSignature(Function f, string qualifiedName, string typeSig) { + qualifiedName = f.getQualifiedName() and + typeSig = typeSig(f) +} + +/** + * Gets a possible definition for the undefined function by matching the undefined function name + * and parameter arity with a defined function. + * + * This is useful for identifying call to target dependencies across libraries, where the libraries + * are never statically linked together. + */ +Function getAPossibleDefinition(Function undefinedFunction) { + not undefinedFunction.isDefined() and + exists(string qn, string typeSig | + functionSignature(undefinedFunction, qn, typeSig) and functionSignature(result, qn, typeSig) + ) and + result.isDefined() +} + +/** + * Gets a possible target for the Call, using the name and parameter matching if we did not associate + * this call with a specific definition at link or compile time, and performing simple virtual + * dispatch resolution. + */ +Function getTarget(Call c) { + if VirtualDispatch::getAViableTarget(c).isDefined() + then + /* + * If there is at least one defined target after performing some simple virtual dispatch + * resolution, then the result is all the defined targets. + */ + + result = VirtualDispatch::getAViableTarget(c) and + result.isDefined() + else + if exists(getAPossibleDefinition(VirtualDispatch::getAViableTarget(c))) + then + /* + * If we can use the heuristic matching of functions to find definitions for some of the viable + * targets, return those. + */ + + result = getAPossibleDefinition(VirtualDispatch::getAViableTarget(c)) + else + // Otherwise, the result is the undefined `Function` instances + result = VirtualDispatch::getAViableTarget(c) +} + +/** + * Get an access of a field on `Variable` v. + */ +FieldAccess getAFieldAccess(Variable v) { + exists(VariableAccess va, Expr qualifierExpr | + // Find an access of the variable, or an AddressOfExpr that has the access + va = v.getAnAccess() and + ( + qualifierExpr = va or + qualifierExpr.(AddressOfExpr).getOperand() = va + ) + | + // Direct field access + qualifierExpr = result.getQualifier() + or + // Nested field access + qualifierExpr = result.(NestedFieldAccess).getUltimateQualifier() + ) +} + +/** + * Gets a condition which is asserted to be false by the given `ne` expression, according to this pattern: + * ``` + * int a = !!result; + * if (!a) { // <- ne + * .... + * } + * ``` + */ +Expr getAssertedFalseCondition(NotExpr ne) { + exists(LocalVariable v | + result = v.getInitializer().getExpr().(NotExpr).getOperand().(NotExpr).getOperand() and + ne.getOperand() = v.getAnAccess() and + nonAssignedVariable(v) + // and not passed by val? + ) +} + +pragma[noinline] +private predicate nonAssignedVariable(Variable v) { not exists(v.getAnAssignment()) } diff --git a/cpp/ql/src/Security/CWE/CWE-457/UninitializedVariables.qll b/cpp/ql/src/Security/CWE/CWE-457/UninitializedVariables.qll new file mode 100644 index 00000000000..4289f66e21d --- /dev/null +++ b/cpp/ql/src/Security/CWE/CWE-457/UninitializedVariables.qll @@ -0,0 +1,190 @@ +/** + * A module for identifying conditionally initialized variables. + */ + +import cpp +import InitializationFunctions + +// Optimised reachability predicates +private predicate reaches(ControlFlowNode a, ControlFlowNode b) = fastTC(successor/2)(a, b) + +private predicate successor(ControlFlowNode a, ControlFlowNode b) { b = a.getASuccessor() } + +class WhitelistedCallsConfig extends string { + WhitelistedCallsConfig() { this = "config" } + + abstract predicate isWhitelisted(Call c); +} + +abstract class WhitelistedCall extends Call { + override Function getTarget() { none() } +} + +private predicate hasConditionalInitialization( + ConditionalInitializationFunction f, ConditionalInitializationCall call, LocalVariable v, + VariableAccess initAccess, Evidence e +) { + // Ignore whitelisted calls + not call instanceof WhitelistedCall and + f = getTarget(call) and + initAccess = v.getAnAccess() and + initAccess = call.getAConditionallyInitializedArgument(f, e).(AddressOfExpr).getOperand() +} + +/** + * A variable that can be conditionally initialized by a call. + */ +class ConditionallyInitializedVariable extends LocalVariable { + ConditionalInitializationCall call; + ConditionalInitializationFunction f; + VariableAccess initAccess; + Evidence e; + + ConditionallyInitializedVariable() { + // Find a call that conditionally initializes this variable + hasConditionalInitialization(f, call, this, initAccess, e) and + // Ignore cases where the variable is assigned prior to the call + not reaches(getAnAssignedValue(), initAccess) and + // Ignore cases where the variable is assigned field-wise prior to the call. + not exists(FieldAccess fa | + exists(Assignment a | + fa = getAFieldAccess(this) and + a.getLValue() = fa + ) + | + reaches(fa, initAccess) + ) and + // Ignore cases where the variable is assigned by a prior call to an initialization function + not exists(Call c | + getAnAccess() = getAnInitializedArgument(c).(AddressOfExpr).getOperand() and + reaches(c, initAccess) + ) and + /* + * Static local variables with constant initializers do not have the initializer expr as part of + * the CFG, but should always be considered as initialized, so exclude them. + */ + + not exists(getInitializer().getExpr()) + } + + /** + * Gets an access of the variable `v` which is not used as an lvalue, and not used as an argument + * to an initialization function. + */ + private VariableAccess getAReadAccess() { + result = this.getAnAccess() and + // Not used as an lvalue + not result = any(AssignExpr a).getLValue() and + // Not passed to another initialization function + not exists(Call c, int j | j = c.getTarget().(InitializationFunction).initializedParameter() | + result = c.getArgument(j).(AddressOfExpr).getOperand() + ) and + // Not a pointless read + not result = any(ExprStmt es).getExpr() + } + + /** + * Gets a read access of variable `v` that occurs after the `initializingCall`. + */ + private VariableAccess getAReadAccessAfterCall(ConditionalInitializationCall initializingCall) { + // Variable associated with this particular call + call = initializingCall and + // Access is a meaningful read access + result = getAReadAccess() and + // Which occurs after the call + reaches(call, result) and + /* + * Ignore risky accesses which are arguments to calls which also include another parameter to + * the original call. This is an attempt to eliminate results where the "status" can be checked + * through another parameter that assigned as part of the original call. + */ + + not exists(Call c | + c.getAnArgument() = result or + c.getAnArgument().(AddressOfExpr).getOperand() = result + | + exists(LocalVariable lv | + call.getAnArgument().(AddressOfExpr).getOperand() = lv.getAnAccess() and + not lv = this + | + c.getAnArgument() = lv.getAnAccess() + ) + ) + } + + /** + * Gets an access to the variable that is risky because the variable may not be initialized after + * the `call`, and the status of the call is never checked. + */ + VariableAccess getARiskyAccessWithNoStatusCheck( + ConditionalInitializationFunction initializingFunction, + ConditionalInitializationCall initializingCall, Evidence evidence + ) { + // Variable associated with this particular call + call = initializingCall and + initializingFunction = f and + e = evidence and + result = getAReadAccessAfterCall(initializingCall) and + ( + // Access is risky because status return code ignored completely + call instanceof ExprInVoidContext + or + // Access is risky because status return code ignored completely + exists(LocalVariable status | call = status.getAnAssignedValue() | + not exists(status.getAnAccess()) + ) + ) + } + + /** + * Gets an access to the variable that is risky because the variable may not be initialized after + * the `call`, and the status of the call is only checked after the risky access. + */ + VariableAccess getARiskyAccessBeforeStatusCheck( + ConditionalInitializationFunction initializingFunction, + ConditionalInitializationCall initializingCall, Evidence evidence + ) { + // Variable associated with this particular call + call = initializingCall and + initializingFunction = f and + e = evidence and + result = getAReadAccessAfterCall(initializingCall) and + exists(LocalVariable status, Assignment a | + a.getRValue() = call and + call = status.getAnAssignedValue() and + // There exists a check of the status code + definitionUsePair(status, a, _) and + // And the check of the status code does not occur before the risky access + not exists(VariableAccess statusAccess | + definitionUsePair(status, a, statusAccess) and + reaches(statusAccess, result) + ) and + // Ignore cases where the assignment to the status code is used directly + a instanceof ExprInVoidContext and + /* + * Ignore risky accesses which are arguments to calls which also include the status code. + * If both the risky value and status code are passed to a different function, that + * function is responsible for checking the status code. + */ + + not exists(Call c | + c.getAnArgument() = result or + c.getAnArgument().(AddressOfExpr).getOperand() = result + | + definitionUsePair(status, a, c.getAnArgument()) + ) + ) + } + + /** + * Gets an access to the variable that is risky because the variable may not be initialized after + * the `call`. + */ + VariableAccess getARiskyAccess( + ConditionalInitializationFunction initializingFunction, + ConditionalInitializationCall initializingCall, Evidence evidence + ) { + result = getARiskyAccessBeforeStatusCheck(initializingFunction, initializingCall, evidence) or + result = getARiskyAccessWithNoStatusCheck(initializingFunction, initializingCall, evidence) + } +} diff --git a/cpp/ql/src/semmle/code/cpp/NestedFields.qll b/cpp/ql/src/semmle/code/cpp/NestedFields.qll new file mode 100644 index 00000000000..12109be8ece --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/NestedFields.qll @@ -0,0 +1,41 @@ +import cpp + +/** + * Gets a `Field` that is nested within the given `Struct`. + * + * This identifies `Field`s which are located in the same memory + */ +private Field getANestedField(Struct s) { + result = s.getAField() + or + exists(NestedStruct ns | + s = ns.getDeclaringType() and + result = getANestedField(ns) + ) +} + +/** + * Unwraps a series of field accesses to determine the inner-most qualifier. + */ +private Expr getUltimateQualifier(FieldAccess fa) { + exists(Expr qualifier | qualifier = fa.getQualifier() | + result = getUltimateQualifier(qualifier) + or + not qualifier instanceof FieldAccess and result = qualifier + ) +} + +/** + * Accesses to nested fields. + */ +class NestedFieldAccess extends FieldAccess { + Expr ultimateQualifier; + + NestedFieldAccess() { + ultimateQualifier = getUltimateQualifier(this) and + getTarget() = getANestedField(ultimateQualifier.getType().stripType()) + } + + /** Gets the ultimate qualifier of this nested field access. */ + Expr getUltimateQualifier() { result = ultimateQualifier } +} diff --git a/cpp/ql/src/semmle/code/cpp/PrintAST.ql b/cpp/ql/src/semmle/code/cpp/PrintAST.ql index 6fc40dd0525..e4c53030da5 100644 --- a/cpp/ql/src/semmle/code/cpp/PrintAST.ql +++ b/cpp/ql/src/semmle/code/cpp/PrintAST.ql @@ -7,3 +7,15 @@ import cpp import PrintAST + +/** + * Temporarily tweak this class or make a copy to control which functions are + * printed. + */ +class Cfg extends PrintASTConfiguration { + /** + * TWEAK THIS PREDICATE AS NEEDED. + * Holds if the AST for `func` should be printed. + */ + override predicate shouldPrintFunction(Function func) { any() } +} diff --git a/cpp/ql/src/semmle/code/cpp/Type.qll b/cpp/ql/src/semmle/code/cpp/Type.qll index c65c4941355..10a1b6b1724 100644 --- a/cpp/ql/src/semmle/code/cpp/Type.qll +++ b/cpp/ql/src/semmle/code/cpp/Type.qll @@ -610,7 +610,9 @@ class FloatingPointType extends ArithmeticType { ( kind >= 24 and kind <= 32 or - kind = 38 + kind >= 38 and kind <= 42 + or + kind >= 45 and kind <= 50 ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll b/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll index 08def499e13..4b0b1299fd8 100644 --- a/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll +++ b/cpp/ql/src/semmle/code/cpp/commons/VoidContext.qll @@ -35,7 +35,8 @@ private predicate exprInVoidContext(Expr e) { exists(CommaExpr c | c.getLeftOperand() = e) or exists(CommaExpr c | c.getRightOperand() = e and c instanceof ExprInVoidContext) + or + exists(ForStmt for | for.getUpdate() = e) ) and - not e instanceof Qualifier and not e.getActualType() instanceof VoidType } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index 0caa2ab05df..fc0b5a90882 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -5,6 +5,8 @@ private import cpp private import semmle.code.cpp.dataflow.internal.FlowVar private import semmle.code.cpp.models.interfaces.DataFlow +private import semmle.code.cpp.controlflow.Guards +private import semmle.code.cpp.valuenumbering.GlobalValueNumbering cached private newtype TNode = @@ -680,12 +682,16 @@ VariableAccess getAnAccessToAssignedVariable(Expr assign) { * * It is important that all extending classes in scope are disjoint. */ -class BarrierGuard extends Expr { - /** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `branch`. */ - abstract deprecated predicate checks(Expr e, boolean branch); +class BarrierGuard extends GuardCondition { + /** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */ + abstract predicate checks(Expr e, boolean b); /** Gets a node guarded by this guard. */ - final Node getAGuardedNode() { - none() // stub + final ExprNode getAGuardedNode() { + exists(GVN value, boolean branch | + result.getExpr() = value.getAnExpr() and + this.checks(value.getAnExpr(), branch) and + this.controls(result.getExpr().getBasicBlock(), branch) + ) } } diff --git a/cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatch.qll b/cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatch.qll new file mode 100644 index 00000000000..2fa9322843e --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/dispatch/VirtualDispatch.qll @@ -0,0 +1,83 @@ +import cpp + +/** + * A module for performing simple virtual dispatch analysis. + */ +module VirtualDispatch { + /** + * Gets a possible implementation target when the given function is the static target of a virtual call. + */ + private MemberFunction getAPossibleImplementation(MemberFunction staticTarget) { + /* + * `IUnknown` is a COM interface with many sub-types, and many overrides (tens of thousands on + * some databases), so we ignore any member functions defined within that interface. + */ + + not staticTarget.getDeclaringType().hasName("IUnknown") and + result = staticTarget.getAnOverridingFunction*() + } + + /** Gets the static type of the qualifier expression for the given call. */ + private Class getCallQualifierType(FunctionCall c) { + result = c.getQualifier().getType().stripType() and + /* + * `IUnknown` is a COM interface with many sub-types (tens of thousands on some databases), so + * we ignore any cases where the qualifier type is that interface. + */ + + not result.hasName("IUnknown") + } + + /** + * Gets a viable target for the given function call. + * + * If `c` is a virtual call, then we will perform a simple virtual dispatch analysis to return + * the `Function` instances which might be a viable target, based on an analysis of the declared + * type of the qualifier expression. + * + * (This analysis is imprecise: it looks for subtypes of the declared type of the qualifier expression + * and the possible implementations of `c.getTarget()` that are declared or inherited by those subtypes. + * This does not account for virtual inheritance and the ways this affects dispatch.) + * + * If `c` is not a virtual call, the result will be `c.getTarget()`. + */ + Function getAViableTarget(Call c) { + exists(Function staticTarget | staticTarget = c.getTarget() | + if c.(FunctionCall).isVirtual() and staticTarget instanceof MemberFunction + then + exists(Class qualifierType, Class qualifierSubType | + result = getAPossibleImplementation(staticTarget) and + qualifierType = getCallQualifierType(c) and + qualifierType = qualifierSubType.getABaseClass*() and + mayInherit(qualifierSubType, result) and + not cannotInherit(qualifierSubType, result) + ) + else result = staticTarget + ) + } + + /** Holds if `f` is declared in `c` or a transitive base class of `c`. */ + private predicate mayInherit(Class c, MemberFunction f) { + f.getDeclaringType() = c.getABaseClass*() + } + + /** + * Holds if `c` cannot inherit the member function `f`, + * i.e. `c` or one of its supertypes overrides `f`. + */ + private predicate cannotInherit(Class c, MemberFunction f) { + exists(Class overridingType, MemberFunction override | + cannotInheritHelper(c, f, overridingType, override) and + override.overrides+(f) + ) + } + + pragma[noinline] + private predicate cannotInheritHelper( + Class c, MemberFunction f, Class overridingType, MemberFunction override + ) { + c.getABaseClass*() = overridingType and + override.getDeclaringType() = overridingType and + overridingType.getABaseClass+() = f.getDeclaringType() + } +} diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll index e4da80fd70b..d020ab82e10 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Expr.qll @@ -540,6 +540,13 @@ class ErrorExpr extends Expr, @errorexpr { */ class AssumeExpr extends Expr, @assume { override string toString() { result = "__assume(...)" } + + override string getCanonicalQLClass() { result = "AssumeExpr" } + + /** + * Gets the operand of the `__assume` expressions. + */ + Expr getOperand() { this.hasChild(result, 0) } } /** diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll index 08556a4af39..0753dfd266e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll @@ -37,7 +37,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration { } private predicate accessesVariable(CopyInstruction copy, Variable var) { - exists(VariableAddressInstruction va | va.getVariable().getAST() = var | + exists(VariableAddressInstruction va | va.getASTVariable() = var | copy.(StoreInstruction).getDestinationAddress() = va or copy.(LoadInstruction).getSourceAddress() = va diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index 0476ea3c30a..cd989c94710 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -5,6 +5,7 @@ private import cpp private import semmle.code.cpp.ir.IR private import semmle.code.cpp.controlflow.IRGuards +private import semmle.code.cpp.ir.ValueNumbering /** * A newtype wrapper to prevent accidental casts between `Node` and @@ -213,6 +214,14 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction */ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) } +/** + * Holds if data can flow from `i1` to `i2` in zero or more + * local (intra-procedural) steps. + */ +predicate localInstructionFlow(Instruction e1, Instruction e2) { + localFlow(instructionNode(e1), instructionNode(e2)) +} + /** * Holds if data can flow from `e1` to `e2` in zero or more * local (intra-procedural) steps. @@ -220,7 +229,7 @@ predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) } predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) } /** - * A guard that validates some expression. + * A guard that validates some instruction. * * To use this in a configuration, extend the class and provide a * characteristic predicate precisely specifying the guard, and override @@ -229,11 +238,15 @@ predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2) * It is important that all extending classes in scope are disjoint. */ class BarrierGuard extends IRGuardCondition { - /** NOT YET SUPPORTED. Holds if this guard validates `e` upon evaluating to `b`. */ - abstract deprecated predicate checks(Instruction e, boolean b); + /** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */ + abstract predicate checks(Instruction instr, boolean b); /** Gets a node guarded by this guard. */ final Node getAGuardedNode() { - none() // stub + exists(ValueNumber value, boolean edge | + result.asInstruction() = value.getAnInstruction() and + this.checks(value.getAnInstruction(), edge) and + this.controls(result.asInstruction().getBlock(), edge) + ) } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll index e34709e94ec..8d7c9194f4f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll @@ -53,6 +53,14 @@ private predicate localInstructionTaintStep(Instruction nodeFrom, Instruction no */ predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) } +/** + * Holds if taint can flow from `i1` to `i2` in zero or more + * local (intra-procedural) steps. + */ +predicate localInstructionTaint(Instruction i1, Instruction i2) { + localTaint(DataFlow::instructionNode(i1), DataFlow::instructionNode(i2)) +} + /** * Holds if taint can flow from `e1` to `e2` in zero or more * local (intra-procedural) steps. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll new file mode 100644 index 00000000000..0abfa14023d --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/IRType.qll @@ -0,0 +1,247 @@ +/** + * Minimal, language-neutral type system for the IR. + */ + +private import internal.IRTypeInternal + +private newtype TIRType = + TIRVoidType() or + TIRUnknownType() or + TIRErrorType() { Language::hasErrorType() } or + TIRBooleanType(int byteSize) { Language::hasBooleanType(byteSize) } or + TIRSignedIntegerType(int byteSize) { Language::hasSignedIntegerType(byteSize) } or + TIRUnsignedIntegerType(int byteSize) { Language::hasUnsignedIntegerType(byteSize) } or + TIRFloatingPointType(int byteSize) { Language::hasFloatingPointType(byteSize) } or + TIRAddressType(int byteSize) { Language::hasAddressType(byteSize) } or + TIRFunctionAddressType(int byteSize) { Language::hasFunctionAddressType(byteSize) } or + TIROpaqueType(Language::OpaqueTypeTag tag, int byteSize) { + Language::hasOpaqueType(tag, byteSize) + } + +/** + * The language-neutral type of an IR `Instruction`, `Operand`, or `IRVariable`. + * The interface to `IRType` and its subclasses is the same across all languages for which the IR + * is supported, so analyses that expect to be used for multiple languages should generally use + * `IRType` rather than a language-specific type. + * + * Many types from the language-specific type system will map to a single canonical `IRType`. Two + * types that map to the same `IRType` are considered equivalent by the IR. As an example, in C++, + * all pointer types map to the same instance of `IRAddressType`. + */ +class IRType extends TIRType { + string toString() { none() } + + /** + * Gets a string that uniquely identifies this `IRType`. This string is often the same as the + * result of `IRType.toString()`, but for some types it may be more verbose to ensure uniqueness. + */ + string getIdentityString() { result = toString() } + + /** + * Gets the size of the type, in bytes, if known. + * + * This will hold for all `IRType` objects except `IRUnknownType`. + */ + int getByteSize() { none() } + + /** + * Gets a single instance of `LanguageType` that maps to this `IRType`. + */ + Language::LanguageType getCanonicalLanguageType() { none() } +} + +/** + * An unknown type. Generally used to represent results and operands that access an unknown set of + * memory locations, such as the side effects of a function call. + */ +class IRUnknownType extends IRType, TIRUnknownType { + final override string toString() { result = "unknown" } + + final override int getByteSize() { none() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalUnknownType() + } +} + +/** + * A void type, which has no values. Used to represent the result type of an instruction that does + * not produce a result. + */ +class IRVoidType extends IRType, TIRVoidType { + final override string toString() { result = "void" } + + final override int getByteSize() { result = 0 } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalVoidType() + } +} + +/** + * An error type. Used when an error in the source code prevents the extractor from determining the + * proper type. + */ +class IRErrorType extends IRType, TIRErrorType { + final override string toString() { result = "error" } + + final override int getByteSize() { result = 0 } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalErrorType() + } +} + +private class IRSizedType extends IRType { + int byteSize; + + IRSizedType() { + this = TIRBooleanType(byteSize) or + this = TIRSignedIntegerType(byteSize) or + this = TIRUnsignedIntegerType(byteSize) or + this = TIRFloatingPointType(byteSize) or + this = TIRAddressType(byteSize) or + this = TIRFunctionAddressType(byteSize) or + this = TIROpaqueType(_, byteSize) + } + + final override int getByteSize() { result = byteSize } +} + +/** + * A Boolean type, which can hold the values `true` (non-zero) or `false` (zero). + */ +class IRBooleanType extends IRSizedType, TIRBooleanType { + final override string toString() { result = "bool" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalBooleanType(byteSize) + } +} + +/** + * A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and + * `IRFloatingPointType`. + */ +class IRNumericType extends IRSizedType { + IRNumericType() { + this = TIRSignedIntegerType(byteSize) or + this = TIRUnsignedIntegerType(byteSize) or + this = TIRFloatingPointType(byteSize) + } +} + +/** + * A signed two's-complement integer. Also used to represent enums whose underlying type is a signed + * integer, as well as character types whose representation is signed. + */ +class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType { + final override string toString() { result = "int" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalSignedIntegerType(byteSize) + } +} + +/** + * An unsigned two's-complement integer. Also used to represent enums whose underlying type is an + * unsigned integer, as well as character types whose representation is unsigned. + */ +class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType { + final override string toString() { result = "uint" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalUnsignedIntegerType(byteSize) + } +} + +/** + * A floating-point type. + */ +class IRFloatingPointType extends IRNumericType, TIRFloatingPointType { + final override string toString() { result = "float" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalFloatingPointType(byteSize) + } +} + +/** + * An address type, representing the memory address of data. Used to represent pointers, references, + * and lvalues, include those that are garbage collected. + * + * The address of a function is represented by the separate `IRFunctionAddressType`. + */ +class IRAddressType extends IRSizedType, TIRAddressType { + final override string toString() { result = "addr" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalAddressType(byteSize) + } +} + +/** + * An address type, representing the memory address of code. Used to represent function pointers, + * function references, and the target of a direct function call. + */ +class IRFunctionAddressType extends IRSizedType, TIRFunctionAddressType { + final override string toString() { result = "func" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalFunctionAddressType(byteSize) + } +} + +/** + * A type with known size that does not fit any of the other kinds of type. Used to represent + * classes, structs, unions, fixed-size arrays, pointers-to-member, and more. + */ +class IROpaqueType extends IRSizedType, TIROpaqueType { + Language::OpaqueTypeTag tag; + + IROpaqueType() { this = TIROpaqueType(tag, byteSize) } + + final override string toString() { + result = "opaque" + byteSize.toString() + "{" + tag.toString() + "}" + } + + final override string getIdentityString() { + result = "opaque" + byteSize.toString() + "{" + Language::getOpaqueTagIdentityString(tag) + "}" + } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalOpaqueType(tag, byteSize) + } + + /** + * Gets the "tag" that differentiates this type from other incompatible opaque types that have the + * same size. + */ + final Language::OpaqueTypeTag getTag() { result = tag } +} + +module IRTypeSanity { + query predicate missingCanonicalLanguageType(IRType type, string message) { + not exists(type.getCanonicalLanguageType()) and + message = "Type does not have a canonical `LanguageType`" + } + + query predicate multipleCanonicalLanguageTypes(IRType type, string message) { + strictcount(type.getCanonicalLanguageType()) > 1 and + message = "Type has multiple canonical `LanguageType`s: " + + concat(type.getCanonicalLanguageType().toString(), ", ") + } + + query predicate missingIRType(Language::LanguageType type, string message) { + not exists(type.getIRType()) and + message = "`LanguageType` does not have a corresponding `IRType`." + } + + query predicate multipleIRTypes(Language::LanguageType type, string message) { + strictcount(type.getIRType()) > 1 and + message = "`LanguageType` " + type.getAQlClass() + " has multiple `IRType`s: " + + concat(type.getIRType().toString(), ", ") + } + + import Language::LanguageTypeSanity +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll index d4f1599d78e..a71608ee855 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll @@ -5,6 +5,7 @@ private newtype TMemoryAccessKind = TBufferMayMemoryAccess() or TEscapedMemoryAccess() or TEscapedMayMemoryAccess() or + TNonLocalMayMemoryAccess() or TPhiMemoryAccess() or TUnmodeledMemoryAccess() or TChiTotalMemoryAccess() or @@ -80,6 +81,14 @@ class EscapedMayMemoryAccess extends MemoryAccessKind, TEscapedMayMemoryAccess { override string toString() { result = "escaped(may)" } } +/** + * The operand or result may access all memory whose address has escaped, other than data on the + * stack frame of the current function. + */ +class NonLocalMayMemoryAccess extends MemoryAccessKind, TNonLocalMayMemoryAccess { + override string toString() { result = "nonlocal(may)" } +} + /** * The operand is a Phi operand, which accesses the same memory as its * definition. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll index 0c1009b4fac..eb07be7f30c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/Opcode.qll @@ -57,6 +57,7 @@ private newtype TOpcode = TUnmodeledDefinition() or TUnmodeledUse() or TAliasedDefinition() or + TAliasedUse() or TPhi() or TBuiltIn() or TVarArgsStart() or @@ -393,6 +394,10 @@ module Opcode { final override string toString() { result = "AliasedDefinition" } } + class AliasedUse extends Opcode, TAliasedUse { + final override string toString() { result = "AliasedUse" } + } + class Phi extends Opcode, TPhi { final override string toString() { result = "Phi" } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll index 278040f8ab8..badd48552a5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll @@ -5,6 +5,7 @@ import IRVariable import Operand private import internal.IRImports as Imports import Imports::EdgeKind +import Imports::IRType import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll index 3921472dc8e..de66a3c99dc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRSanity.qll @@ -1,2 +1,3 @@ private import IR import InstructionSanity +import IRTypeSanity diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll index a7ca4593ac4..1a607f82c29 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll @@ -5,6 +5,7 @@ import Imports::TempVariableTag private import Imports::IRUtilities private import Imports::TTempVariableTag private import Imports::TIRVariable +private import Imports::IRType IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { result.getVariable() = var and @@ -24,7 +25,17 @@ abstract class IRVariable extends TIRVariable { /** * Gets the type of the variable. */ - abstract Language::Type getType(); + final Language::Type getType() { getLanguageType().hasType(result, false) } + + /** + * Gets the language-neutral type of the variable. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the variable. + */ + abstract Language::LanguageType getLanguageType(); /** * Gets the AST node that declared this variable, or that introduced this @@ -59,7 +70,7 @@ abstract class IRVariable extends TIRVariable { */ class IRUserVariable extends IRVariable, TIRUserVariable { Language::Variable var; - Language::Type type; + Language::LanguageType type; IRUserVariable() { this = TIRUserVariable(var, type, func) } @@ -71,7 +82,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable { result = getVariable().toString() + " " + getVariable().getLocation().toString() } - final override Language::Type getType() { result = type } + final override Language::LanguageType getLanguageType() { result = type } /** * Gets the original user-declared variable. @@ -110,11 +121,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable { Language::AST ast; TempVariableTag tag; - Language::Type type; + Language::LanguageType type; IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) } - final override Language::Type getType() { result = type } + final override Language::LanguageType getLanguageType() { result = type } final override Language::AST getAST() { result = ast } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll index 6d5e697150e..049983c9126 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll @@ -5,6 +5,7 @@ import IRVariable import Operand private import internal.InstructionImports as Imports import Imports::EdgeKind +import Imports::IRType import Imports::MemoryAccessKind import Imports::Opcode private import Imports::OperandTag @@ -49,7 +50,8 @@ module InstructionSanity { ( opcode instanceof ReadSideEffectOpcode or opcode instanceof Opcode::InlineAsm or - opcode instanceof Opcode::CallSideEffect + opcode instanceof Opcode::CallSideEffect or + opcode instanceof Opcode::AliasedUse ) and tag instanceof SideEffectOperandTag ) @@ -113,10 +115,12 @@ module InstructionSanity { } query predicate missingOperandType(Operand operand, string message) { - exists(Language::Function func | + exists(Language::Function func, Instruction use | not exists(operand.getType()) and - func = operand.getUse().getEnclosingFunction() and - message = "Operand missing type in function '" + Language::getIdentityString(func) + "'." + use = operand.getUse() and + func = use.getEnclosingFunction() and + message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + + "' missing type in function '" + Language::getIdentityString(func) + "'." ) } @@ -260,6 +264,7 @@ module InstructionSanity { ) { exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | not useOperand.getUse() instanceof UnmodeledUseInstruction and + not defInstr instanceof UnmodeledDefinitionInstruction and pointOfEvaluation(useOperand, useBlock, useIndex) and defInstr = useOperand.getAnyDef() and ( @@ -321,7 +326,7 @@ class Instruction extends Construction::TInstruction { } private string getResultPrefix() { - if getResultType() instanceof Language::VoidType + if getResultIRType() instanceof IRVoidType then result = "v" else if hasMemoryResult() @@ -353,23 +358,6 @@ class Instruction extends Construction::TInstruction { ) } - bindingset[type] - private string getValueCategoryString(string type) { - if isGLValue() then result = "glval<" + type + ">" else result = type - } - - string getResultTypeString() { - exists(string valcat | - valcat = getValueCategoryString(getResultType().toString()) and - if - getResultType() instanceof Language::UnknownType and - not isGLValue() and - exists(getResultSize()) - then result = valcat + "[" + getResultSize().toString() + "]" - else result = valcat - ) - } - /** * Gets a human-readable string that uniquely identifies this instruction * within the function. This string is used to refer to this instruction when @@ -389,7 +377,9 @@ class Instruction extends Construction::TInstruction { * * Example: `r1_1(int*)` */ - final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" } + final string getResultString() { + result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + } /** * Gets a string describing the operands of this instruction, suitable for @@ -457,6 +447,16 @@ class Instruction extends Construction::TInstruction { result = Construction::getInstructionUnconvertedResultExpression(this) } + final Language::LanguageType getResultLanguageType() { + result = Construction::getInstructionResultType(this) + } + + /** + * Gets the type of the result produced by this instruction. If the instruction does not produce + * a result, its result type will be `IRVoidType`. + */ + final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + /** * Gets the type of the result produced by this instruction. If the * instruction does not produce a result, its result type will be `VoidType`. @@ -464,7 +464,16 @@ class Instruction extends Construction::TInstruction { * If `isGLValue()` holds, then the result type of this instruction should be * thought of as "pointer to `getResultType()`". */ - final Language::Type getResultType() { Construction::instructionHasType(this, result, _) } + final Language::Type getResultType() { + exists(Language::LanguageType resultType | + resultType = getResultLanguageType() and + ( + resultType.hasUnspecifiedType(result, _) + or + not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType + ) + ) + } /** * Holds if the result produced by this instruction is a glvalue. If this @@ -484,7 +493,7 @@ class Instruction extends Construction::TInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::instructionHasType(this, _, true) } + final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -493,16 +502,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { - if isGLValue() - then - // a glvalue is always pointer-sized. - result = Language::getPointerSize() - else - if getResultType() instanceof Language::UnknownType - then result = Construction::getInstructionResultSize(this) - else result = Language::getTypeSize(getResultType()) - } + final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -1384,7 +1384,7 @@ class CatchInstruction extends Instruction { * An instruction that catches an exception of a specific type. */ class CatchByTypeInstruction extends CatchInstruction { - Language::Type exceptionType; + Language::LanguageType exceptionType; CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and @@ -1396,7 +1396,7 @@ class CatchByTypeInstruction extends CatchInstruction { /** * Gets the type of exception to be caught. */ - final Language::Type getExceptionType() { result = exceptionType } + final Language::LanguageType getExceptionType() { result = exceptionType } } /** @@ -1423,6 +1423,13 @@ class AliasedDefinitionInstruction extends Instruction { final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess } } +/** + * An instruction that consumes all escaped memory on exit from the function. + */ +class AliasedUseInstruction extends Instruction { + AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } +} + class UnmodeledUseInstruction extends Instruction { UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll index a23fb081afe..07dd107bf12 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll @@ -1,9 +1,10 @@ private import internal.IRInternal -import Instruction -import IRBlock +private import Instruction +private import IRBlock private import internal.OperandImports as Imports -import Imports::MemoryAccessKind -import Imports::Overlap +private import Imports::MemoryAccessKind +private import Imports::IRType +private import Imports::Overlap private import Imports::OperandTag cached @@ -143,22 +144,40 @@ class Operand extends TOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - Language::Type getType() { result = getAnyDef().getResultType() } + Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + + /** + * Gets the language-neutral type of the value consumed by this operand. This is usually the same + * as the result type of the definition instruction consumed by this operand. For register + * operands, this is always the case. For some memory operands, the operand type may be different + * from the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the value consumed by this operand. This is usually the same as the + * result type of the definition instruction consumed by this operand. For register operands, + * this is always the case. For some memory operands, the operand type may be different from + * the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final Language::Type getType() { getLanguageType().hasType(result, _) } /** * Holds if the value consumed by this operand is a glvalue. If this * holds, the value of the operand represents the address of a location, * and the type of the location is given by `getType()`. If this does * not hold, the value of the operand represents a value whose type is - * given by `getResultType()`. + * given by `getType()`. */ - predicate isGLValue() { getAnyDef().isGLValue() } + final predicate isGLValue() { getLanguageType().hasType(_, true) } /** * Gets the size of the value consumed by this operand, in bytes. If the operand does not have * a known constant size, this predicate does not hold. */ - int getSize() { result = Language::getTypeSize(getType()) } + final int getSize() { result = getLanguageType().getByteSize() } } /** @@ -170,11 +189,6 @@ class MemoryOperand extends Operand { this = TPhiOperand(_, _, _, _) } - override predicate isGLValue() { - // A `MemoryOperand` can never be a glvalue - none() - } - /** * Gets the kind of memory access performed by the operand. */ @@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; - final override Language::Type getType() { + final override Language::LanguageType getLanguageType() { result = Construction::getInstructionOperandType(useInstr, tag) } } @@ -381,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand { class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; - final override int getSize() { - if getType() instanceof Language::UnknownType - then result = Construction::getInstructionOperandSize(useInstr, tag) - else result = Language::getTypeSize(getType()) - } - override MemoryAccessKind getMemoryAccess() { + useInstr instanceof AliasedUseInstruction and + result instanceof NonLocalMayMemoryAccess + or useInstr instanceof CallSideEffectInstruction and result instanceof EscapedMayMemoryAccess or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll index df6c5be7728..79e5a2b6713 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll @@ -1,5 +1,5 @@ private import internal.ValueNumberingInternal -private import cpp +private import internal.ValueNumberingImports private import IR /** @@ -23,31 +23,32 @@ newtype TValueNumber = initializeParameterValueNumber(_, irFunc, var) } or TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or - TConstantValueNumber(IRFunction irFunc, Type type, string value) { + TConstantValueNumber(IRFunction irFunc, IRType type, string value) { constantValueNumber(_, irFunc, type, value) } or - TStringConstantValueNumber(IRFunction irFunc, Type type, string value) { + TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) { stringConstantValueNumber(_, irFunc, type, value) } or - TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) { + TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) { fieldAddressValueNumber(_, irFunc, field, objectAddress) } or TBinaryValueNumber( - IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand + IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand ) { binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand) } or TPointerArithmeticValueNumber( - IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand ) { pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand) } or - TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) { + TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) { unaryValueNumber(_, irFunc, opcode, type, operand) } or TInheritanceConversionValueNumber( - IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand + IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass, + ValueNumber operand ) { inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand) } or @@ -59,7 +60,7 @@ newtype TValueNumber = class ValueNumber extends TValueNumber { final string toString() { result = getExampleInstruction().getResultId() } - final Location getLocation() { result = getExampleInstruction().getLocation() } + final Language::Location getLocation() { result = getExampleInstruction().getLocation() } /** * Gets the instructions that have been assigned this value number. This will always produce at @@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF } private predicate constantValueNumber( - ConstantInstruction instr, IRFunction irFunc, Type type, string value + ConstantInstruction instr, IRFunction irFunc, IRType type, string value ) { instr.getEnclosingIRFunction() = irFunc and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getValue() = value } private predicate stringConstantValueNumber( - StringConstantInstruction instr, IRFunction irFunc, Type type, string value + StringConstantInstruction instr, IRFunction irFunc, IRType type, string value ) { instr.getEnclosingIRFunction() = irFunc and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getValue().getValue() = value } private predicate fieldAddressValueNumber( - FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress + FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress ) { instr.getEnclosingIRFunction() = irFunc and instr.getField() = field and @@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber( } private predicate binaryValueNumber( - BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, + BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand ) { instr.getEnclosingIRFunction() = irFunc and not instr instanceof PointerArithmeticInstruction and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and valueNumber(instr.getLeft()) = leftOperand and valueNumber(instr.getRight()) = rightOperand } private predicate pointerArithmeticValueNumber( - PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize, - ValueNumber leftOperand, ValueNumber rightOperand + PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, + int elementSize, ValueNumber leftOperand, ValueNumber rightOperand ) { instr.getEnclosingIRFunction() = irFunc and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getElementSize() = elementSize and valueNumber(instr.getLeft()) = leftOperand and valueNumber(instr.getRight()) = rightOperand } private predicate unaryValueNumber( - UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand + UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand ) { instr.getEnclosingIRFunction() = irFunc and not instr instanceof InheritanceConversionInstruction and not instr instanceof CopyInstruction and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and valueNumber(instr.getUnary()) = operand } private predicate inheritanceConversionValueNumber( - InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass, - Class derivedClass, ValueNumber operand + InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, + Language::Class baseClass, Language::Class derivedClass, ValueNumber operand ) { instr.getEnclosingIRFunction() = irFunc and instr.getOpcode() = opcode and @@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber( */ private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) { instr.getEnclosingIRFunction() = irFunc and - not instr.getResultType() instanceof VoidType and + not instr.getResultIRType() instanceof IRVoidType and not numberableInstruction(instr) } @@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) { initializeThisValueNumber(instr, irFunc) and result = TInitializeThisValueNumber(irFunc) or - exists(Type type, string value | + exists(IRType type, string value | constantValueNumber(instr, irFunc, type, value) and result = TConstantValueNumber(irFunc, type, value) ) or - exists(Type type, string value | + exists(IRType type, string value | stringConstantValueNumber(instr, irFunc, type, value) and result = TStringConstantValueNumber(irFunc, type, value) ) or - exists(Field field, ValueNumber objectAddress | + exists(Language::Field field, ValueNumber objectAddress | fieldAddressValueNumber(instr, irFunc, field, objectAddress) and result = TFieldAddressValueNumber(irFunc, field, objectAddress) ) or - exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand | + exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand | binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand) ) or - exists(Opcode opcode, Type type, ValueNumber operand | + exists(Opcode opcode, IRType type, ValueNumber operand | unaryValueNumber(instr, irFunc, opcode, type, operand) and result = TUnaryValueNumber(irFunc, opcode, type, operand) ) or - exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand | + exists( + Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand + | inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand) ) or exists( - Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand + Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand | pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand, rightOperand) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll new file mode 100644 index 00000000000..0282f6887f1 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.ir.internal.Overlap +import semmle.code.cpp.ir.internal.IRCppLanguage as Language diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll index 95ddffb2274..955d422cf9a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll @@ -1,6 +1,6 @@ -private import cpp import AliasAnalysis import semmle.code.cpp.ir.internal.Overlap +private import semmle.code.cpp.ir.internal.IRCppLanguage as Language private import semmle.code.cpp.Print private import semmle.code.cpp.ir.implementation.unaliased_ssa.IR private import semmle.code.cpp.ir.internal.IntegerConstant as Ints @@ -10,31 +10,42 @@ private import semmle.code.cpp.ir.implementation.internal.OperandTag private class IntValue = Ints::IntValue; private predicate hasResultMemoryAccess( - Instruction instr, IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset + Instruction instr, IRVariable var, IRType type, Language::LanguageType languageType, + IntValue startBitOffset, IntValue endBitOffset ) { resultPointsTo(instr.getResultAddress(), var, startBitOffset) and - type = instr.getResultType() and - if exists(instr.getResultSize()) - then endBitOffset = Ints::add(startBitOffset, Ints::mul(instr.getResultSize(), 8)) + languageType = instr.getResultLanguageType() and + type = languageType.getIRType() and + if exists(type.getByteSize()) + then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8)) else endBitOffset = Ints::unknown() } private predicate hasOperandMemoryAccess( - MemoryOperand operand, IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset + MemoryOperand operand, IRVariable var, IRType type, Language::LanguageType languageType, + IntValue startBitOffset, IntValue endBitOffset ) { resultPointsTo(operand.getAddressOperand().getAnyDef(), var, startBitOffset) and - type = operand.getType() and - if exists(operand.getSize()) - then endBitOffset = Ints::add(startBitOffset, Ints::mul(operand.getSize(), 8)) + languageType = operand.getLanguageType() and + type = languageType.getIRType() and + if exists(type.getByteSize()) + then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8)) else endBitOffset = Ints::unknown() } private newtype TMemoryLocation = - TVariableMemoryLocation(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset) { - hasResultMemoryAccess(_, var, type, startBitOffset, endBitOffset) or - hasOperandMemoryAccess(_, var, type, startBitOffset, endBitOffset) + TVariableMemoryLocation( + IRVariable var, IRType type, Language::LanguageType languageType, IntValue startBitOffset, + IntValue endBitOffset + ) { + ( + hasResultMemoryAccess(_, var, type, _, startBitOffset, endBitOffset) or + hasOperandMemoryAccess(_, var, type, _, startBitOffset, endBitOffset) + ) and + languageType = type.getCanonicalLanguageType() } or TUnknownMemoryLocation(IRFunction irFunc) or + TUnknownNonLocalMemoryLocation(IRFunction irFunc) or TUnknownVirtualVariable(IRFunction irFunc) /** @@ -49,9 +60,11 @@ abstract class MemoryLocation extends TMemoryLocation { abstract VirtualVariable getVirtualVariable(); - abstract Type getType(); + abstract Language::LanguageType getType(); abstract string getUniqueId(); + + final IRType getIRType() { result = getType().getIRType() } } abstract class VirtualVariable extends MemoryLocation { } @@ -62,20 +75,34 @@ abstract class VirtualVariable extends MemoryLocation { } */ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation { IRVariable var; - Type type; + IRType type; + Language::LanguageType languageType; IntValue startBitOffset; IntValue endBitOffset; VariableMemoryLocation() { - this = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset) + this = TVariableMemoryLocation(var, type, languageType, startBitOffset, endBitOffset) } final override string toString() { result = var.toString() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" + - type.toString() + ">" + type.toString() + ", " + languageType.toString() + ">" } - final override Type getType() { result = type } + final override Language::LanguageType getType() { + if + strictcount(Language::LanguageType accessType | + hasResultMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset) or + hasOperandMemoryAccess(_, var, type, accessType, startBitOffset, endBitOffset) + ) = 1 + then + // All of the accesses have the same `LanguageType`, so just use that. + hasResultMemoryAccess(_, var, type, result, startBitOffset, endBitOffset) or + hasOperandMemoryAccess(_, var, type, result, startBitOffset, endBitOffset) + else + // There is no single type for all accesses, so just use the canonical one for this `IRType`. + result = type.getCanonicalLanguageType() + } final IntValue getStartBitOffset() { result = startBitOffset } @@ -85,13 +112,14 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation { final override string getUniqueId() { result = var.getUniqueId() + Interval::getIntervalString(startBitOffset, endBitOffset) + "<" + - getTypeIdentityString(type) + ">" + type.getIdentityString() + ">" } final override VirtualVariable getVirtualVariable() { if variableAddressEscapes(var) then result = TUnknownVirtualVariable(var.getEnclosingIRFunction()) - else result = TVariableMemoryLocation(var, var.getType(), 0, var.getType().getSize() * 8) + else + result = TVariableMemoryLocation(var, var.getIRType(), _, 0, var.getIRType().getByteSize() * 8) } /** @@ -99,7 +127,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation { */ final predicate coversEntireVariable() { startBitOffset = 0 and - endBitOffset = var.getType().getSize() * 8 + endBitOffset = var.getIRType().getByteSize() * 8 } } @@ -111,7 +139,7 @@ class VariableMemoryLocation extends TVariableMemoryLocation, MemoryLocation { class VariableVirtualVariable extends VariableMemoryLocation, VirtualVariable { VariableVirtualVariable() { not variableAddressEscapes(var) and - type = var.getType() and + type = var.getIRType() and coversEntireVariable() } } @@ -128,11 +156,33 @@ class UnknownMemoryLocation extends TUnknownMemoryLocation, MemoryLocation { final override VirtualVariable getVirtualVariable() { result = TUnknownVirtualVariable(irFunc) } - final override Type getType() { result instanceof UnknownType } + final override Language::LanguageType getType() { + result = any(IRUnknownType type).getCanonicalLanguageType() + } final override string getUniqueId() { result = "{Unknown}" } } +/** + * An access to memory that is not known to be confined to a specific `IRVariable`, but is known to + * not access memory on the current function's stack frame. + */ +class UnknownNonLocalMemoryLocation extends TUnknownNonLocalMemoryLocation, MemoryLocation { + IRFunction irFunc; + + UnknownNonLocalMemoryLocation() { this = TUnknownNonLocalMemoryLocation(irFunc) } + + final override string toString() { result = "{UnknownNonLocal}" } + + final override VirtualVariable getVirtualVariable() { result = TUnknownVirtualVariable(irFunc) } + + final override Language::LanguageType getType() { + result = any(IRUnknownType type).getCanonicalLanguageType() + } + + final override string getUniqueId() { result = "{UnknownNonLocal}" } +} + /** * An access to all aliased memory. */ @@ -143,7 +193,9 @@ class UnknownVirtualVariable extends TUnknownVirtualVariable, VirtualVariable { final override string toString() { result = "{AllAliased}" } - final override Type getType() { result instanceof UnknownType } + final override Language::LanguageType getType() { + result = any(IRUnknownType type).getCanonicalLanguageType() + } final override string getUniqueId() { result = " " + toString() } @@ -163,6 +215,13 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) { def instanceof UnknownMemoryLocation and result instanceof MayPartiallyOverlap or + // An UnknownNonLocalMemoryLocation may partially overlap any location within the same virtual + // variable, except a local variable. + def.getVirtualVariable() = use.getVirtualVariable() and + def instanceof UnknownNonLocalMemoryLocation and + result instanceof MayPartiallyOverlap and + not use.(VariableMemoryLocation).getVariable() instanceof IRAutomaticVariable + or exists(VariableMemoryLocation defVariableLocation | defVariableLocation = def and ( @@ -171,13 +230,20 @@ Overlap getOverlap(MemoryLocation def, MemoryLocation use) { (use instanceof UnknownMemoryLocation or use instanceof UnknownVirtualVariable) and result instanceof MayPartiallyOverlap or + // A VariableMemoryLocation that is not a local variable may partially overlap an unknown + // non-local location within the same virtual variable. + def.getVirtualVariable() = use.getVirtualVariable() and + use instanceof UnknownNonLocalMemoryLocation and + result instanceof MayPartiallyOverlap and + not defVariableLocation.getVariable() instanceof IRAutomaticVariable + or // A VariableMemoryLocation overlaps another location within the same variable based on the relationship // of the two offset intervals. exists(Overlap intervalOverlap | intervalOverlap = getVariableMemoryLocationOverlap(def, use) and if intervalOverlap instanceof MustExactlyOverlap then - if def.getType() = use.getType() + if def.getIRType() = use.getIRType() then // The def and use types match, so it's an exact overlap. result instanceof MustExactlyOverlap @@ -282,11 +348,11 @@ MemoryLocation getResultMemoryLocation(Instruction instr) { ( ( kind.usesAddressOperand() and - if hasResultMemoryAccess(instr, _, _, _, _) + if hasResultMemoryAccess(instr, _, _, _, _, _) then - exists(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset | - hasResultMemoryAccess(instr, var, type, startBitOffset, endBitOffset) and - result = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset) + exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset | + hasResultMemoryAccess(instr, var, type, _, startBitOffset, endBitOffset) and + result = TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset) ) else result = TUnknownMemoryLocation(instr.getEnclosingIRFunction()) ) @@ -296,6 +362,9 @@ MemoryLocation getResultMemoryLocation(Instruction instr) { or kind instanceof EscapedMayMemoryAccess and result = TUnknownMemoryLocation(instr.getEnclosingIRFunction()) + or + kind instanceof NonLocalMayMemoryAccess and + result = TUnknownNonLocalMemoryLocation(instr.getEnclosingIRFunction()) ) ) } @@ -306,11 +375,11 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { ( ( kind.usesAddressOperand() and - if hasOperandMemoryAccess(operand, _, _, _, _) + if hasOperandMemoryAccess(operand, _, _, _, _, _) then - exists(IRVariable var, Type type, IntValue startBitOffset, IntValue endBitOffset | - hasOperandMemoryAccess(operand, var, type, startBitOffset, endBitOffset) and - result = TVariableMemoryLocation(var, type, startBitOffset, endBitOffset) + exists(IRVariable var, IRType type, IntValue startBitOffset, IntValue endBitOffset | + hasOperandMemoryAccess(operand, var, type, _, startBitOffset, endBitOffset) and + result = TVariableMemoryLocation(var, type, _, startBitOffset, endBitOffset) ) else result = TUnknownMemoryLocation(operand.getEnclosingIRFunction()) ) @@ -320,6 +389,9 @@ MemoryLocation getOperandMemoryLocation(MemoryOperand operand) { or kind instanceof EscapedMayMemoryAccess and result = TUnknownMemoryLocation(operand.getEnclosingIRFunction()) + or + kind instanceof NonLocalMayMemoryAccess and + result = TUnknownNonLocalMemoryLocation(operand.getEnclosingIRFunction()) ) ) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll index 74f7b4a2b64..42d6e7db693 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRImports.qll @@ -1,2 +1,3 @@ import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll index 1f0f5eaccde..8c60565defc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/IRVariableImports.qll @@ -1,3 +1,4 @@ +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag import semmle.code.cpp.ir.internal.IRUtilities as IRUtilities import semmle.code.cpp.ir.internal.TempVariableTag as TTempVariableTag diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll index 0138af075dc..a75f70dbe2f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/InstructionImports.qll @@ -1,4 +1,5 @@ import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind import semmle.code.cpp.ir.implementation.Opcode as Opcode import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll index e626662ccf2..3c781579cee 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/OperandImports.qll @@ -1,3 +1,4 @@ import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.internal.Overlap as Overlap import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll index a5c09c6401b..23121523a6b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll @@ -4,34 +4,52 @@ private import Alias private import SSAConstruction private import DebugSSA +bindingset[offset] +private string getKeySuffixForOffset(int offset) { + if offset % 2 = 0 then result = "" else result = "_Chi" +} + +bindingset[offset] +private int getIndexForOffset(int offset) { result = offset / 2 } + /** * Property provide that dumps the memory access of each result. Useful for debugging SSA * construction. */ class PropertyProvider extends IRPropertyProvider { override string getInstructionProperty(Instruction instruction, string key) { - exists(MemoryLocation location | - location = getResultMemoryLocation(instruction) and - ( - key = "ResultMemoryLocation" and result = location.toString() - or - key = "ResultVirtualVariable" and result = location.getVirtualVariable().toString() + key = "ResultMemoryLocation" and + result = strictconcat(MemoryLocation loc | + loc = getResultMemoryLocation(instruction) + | + loc.toString(), "," ) - ) or - exists(MemoryLocation location | - location = getOperandMemoryLocation(instruction.getAnOperand()) and - ( - key = "OperandMemoryAccess" and result = location.toString() - or - key = "OperandVirtualVariable" and result = location.getVirtualVariable().toString() + key = "ResultVirtualVariable" and + result = strictconcat(MemoryLocation loc | + loc = getResultMemoryLocation(instruction) + | + loc.getVirtualVariable().toString(), "," ) - ) or - exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex | - hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and - defBlock.getInstruction(defIndex) = instruction and - key = "DefinitionRank[" + useLocation.toString() + "]" and + key = "OperandMemoryLocation" and + result = strictconcat(MemoryLocation loc | + loc = getOperandMemoryLocation(instruction.getAnOperand()) + | + loc.toString(), "," + ) + or + key = "OperandVirtualVariable" and + result = strictconcat(MemoryLocation loc | + loc = getOperandMemoryLocation(instruction.getAnOperand()) + | + loc.getVirtualVariable().toString(), "," + ) + or + exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset | + hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and + defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and + key = "DefinitionRank" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + "]" and result = defRank.toString() ) or @@ -41,10 +59,11 @@ class PropertyProvider extends IRPropertyProvider { result = useRank.toString() ) or - exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex | - hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and - defBlock.getInstruction(defIndex) = instruction and - key = "DefinitionReachesUse[" + useLocation.toString() + "]" and + exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset | + hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and + defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and + key = "DefinitionReachesUse" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + + "]" and result = strictconcat(IRBlock useBlock, int useRank, int useIndex | exists(Instruction useInstruction | hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 3010825d50b..7152dec5c4a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -1,8 +1,5 @@ import SSAConstructionInternal -private import cpp -private import semmle.code.cpp.ir.implementation.Opcode -private import semmle.code.cpp.ir.implementation.internal.OperandTag -private import semmle.code.cpp.ir.internal.Overlap +private import SSAConstructionImports private import NewIR private class OldBlock = Reachability::ReachableBlock; @@ -18,7 +15,7 @@ private module Cached { } cached - predicate functionHasIR(Function func) { + predicate functionHasIR(Language::Function func) { exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func) } @@ -42,7 +39,7 @@ private module Cached { not oldInstruction instanceof OldIR::PhiInstruction and hasChiNode(_, oldInstruction) } or - Unreached(Function function) { + Unreached(Language::Function function) { exists(OldInstruction oldInstruction | function = oldInstruction.getEnclosingFunction() and Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) @@ -50,12 +47,14 @@ private module Cached { } cached - predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, Type type) { + predicate hasTempVariable( + Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type + ) { exists(OldIR::IRTempVariable var | var.getEnclosingFunction() = func and var.getAST() = ast and var.getTag() = tag and - var.getType() = type + var.getLanguageType() = type ) } @@ -135,24 +134,12 @@ private module Cached { } cached - Type getInstructionOperandType(Instruction instr, TypedOperandTag tag) { + Language::LanguageType getInstructionOperandType(Instruction instr, TypedOperandTag tag) { exists(OldInstruction oldInstruction, OldIR::TypedOperand oldOperand | oldInstruction = getOldInstruction(instr) and oldOperand = oldInstruction.getAnOperand() and tag = oldOperand.getOperandTag() and - result = oldOperand.getType() - ) - } - - cached - int getInstructionOperandSize(Instruction instr, SideEffectOperandTag tag) { - exists(OldInstruction oldInstruction, OldIR::SideEffectOperand oldOperand | - oldInstruction = getOldInstruction(instr) and - oldOperand = oldInstruction.getAnOperand() and - tag = oldOperand.getOperandTag() and - // Only return a result for operands that need an explicit result size. - oldOperand.getType() instanceof UnknownType and - result = oldOperand.getSize() + result = oldOperand.getLanguageType() ) } @@ -196,20 +183,21 @@ private module Cached { } cached - Expr getInstructionConvertedResultExpression(Instruction instruction) { + Language::Expr getInstructionConvertedResultExpression(Instruction instruction) { result = getOldInstruction(instruction).getConvertedResultExpression() } cached - Expr getInstructionUnconvertedResultExpression(Instruction instruction) { + Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) { result = getOldInstruction(instruction).getUnconvertedResultExpression() } - /** + /* * This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node, * that node is its successor in the new successor relation, and the Chi node's successors are * the new instructions generated from the successors of the old instruction */ + cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { if hasChiNode(_, getOldInstruction(instruction)) @@ -252,7 +240,7 @@ private module Cached { } cached - Locatable getInstructionAST(Instruction instruction) { + Language::AST getInstructionAST(Instruction instruction) { exists(OldInstruction oldInstruction | instruction = WrappedInstruction(oldInstruction) or @@ -270,29 +258,25 @@ private module Cached { } cached - predicate instructionHasType(Instruction instruction, Type type, boolean isGLValue) { + Language::LanguageType getInstructionResultType(Instruction instruction) { exists(OldInstruction oldInstruction | instruction = WrappedInstruction(oldInstruction) and - type = oldInstruction.getResultType() and - if oldInstruction.isGLValue() then isGLValue = true else isGLValue = false + result = oldInstruction.getResultLanguageType() ) or exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar | instruction = Chi(oldInstruction) and hasChiNode(vvar, oldInstruction) and - type = vvar.getType() and - isGLValue = false + result = vvar.getType() ) or exists(Alias::MemoryLocation location | instruction = Phi(_, location) and - type = location.getType() and - isGLValue = false + result = location.getType() ) or instruction = Unreached(_) and - type instanceof VoidType and - isGLValue = false + result = Language::getVoidType() } cached @@ -338,7 +322,7 @@ private module Cached { } cached - Field getInstructionField(Instruction instruction) { + Language::Field getInstructionField(Instruction instruction) { result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField() } @@ -348,7 +332,7 @@ private module Cached { } cached - Function getInstructionFunction(Instruction instruction) { + Language::Function getInstructionFunction(Instruction instruction) { result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol() } @@ -358,19 +342,19 @@ private module Cached { } cached - StringLiteral getInstructionStringLiteral(Instruction instruction) { + Language::StringLiteral getInstructionStringLiteral(Instruction instruction) { result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue() } cached - BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { + Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { result = getOldInstruction(instruction) .(OldIR::BuiltInOperationInstruction) .getBuiltInOperation() } cached - Type getInstructionExceptionType(Instruction instruction) { + Language::LanguageType getInstructionExceptionType(Instruction instruction) { result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() } @@ -380,14 +364,9 @@ private module Cached { } cached - int getInstructionResultSize(Instruction instruction) { - // Only return a result for instructions that needed an explicit result size. - instruction.getResultType() instanceof UnknownType and - result = getOldInstruction(instruction).getResultSize() - } - - cached - predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) { + predicate getInstructionInheritance( + Instruction instruction, Language::Class baseClass, Language::Class derivedClass + ) { exists(OldIR::InheritanceConversionInstruction oldInstr | oldInstr = getOldInstruction(instruction) and baseClass = oldInstr.getBaseClass() and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll new file mode 100644 index 00000000000..00f12020a29 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionImports.qll @@ -0,0 +1,3 @@ +import semmle.code.cpp.ir.implementation.Opcode +import semmle.code.cpp.ir.implementation.internal.OperandTag +import semmle.code.cpp.ir.internal.Overlap diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll index 5eebc0b9516..c0922aff891 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstructionInternal.qll @@ -2,4 +2,5 @@ import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as OldIR import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.ReachableBlock as Reachability import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.reachability.Dominance as Dominance import semmle.code.cpp.ir.implementation.aliased_ssa.IR as NewIR +import semmle.code.cpp.ir.internal.IRCppLanguage as Language import AliasedSSA as Alias diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRTypeInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRTypeInternal.qll new file mode 100644 index 00000000000..bd6c2f4c151 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/IRTypeInternal.qll @@ -0,0 +1 @@ +import semmle.code.cpp.ir.internal.IRCppLanguage as Language diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll index 908a208b83a..fea67ef5ecd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll @@ -2,11 +2,11 @@ private import TIRVariableInternal private import Imports::TempVariableTag newtype TIRVariable = - TIRUserVariable(Language::Variable var, Language::Type type, Language::Function func) { + TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Function func) { Construction::hasUserVariable(func, var, type) } or TIRTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::Type type + Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type ) { Construction::hasTempVariable(func, ast, tag, type) } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll index 278040f8ab8..badd48552a5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IR.qll @@ -5,6 +5,7 @@ import IRVariable import Operand private import internal.IRImports as Imports import Imports::EdgeKind +import Imports::IRType import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll index 3921472dc8e..de66a3c99dc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRSanity.qll @@ -1,2 +1,3 @@ private import IR import InstructionSanity +import IRTypeSanity diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll index a7ca4593ac4..1a607f82c29 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/IRVariable.qll @@ -5,6 +5,7 @@ import Imports::TempVariableTag private import Imports::IRUtilities private import Imports::TTempVariableTag private import Imports::TIRVariable +private import Imports::IRType IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { result.getVariable() = var and @@ -24,7 +25,17 @@ abstract class IRVariable extends TIRVariable { /** * Gets the type of the variable. */ - abstract Language::Type getType(); + final Language::Type getType() { getLanguageType().hasType(result, false) } + + /** + * Gets the language-neutral type of the variable. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the variable. + */ + abstract Language::LanguageType getLanguageType(); /** * Gets the AST node that declared this variable, or that introduced this @@ -59,7 +70,7 @@ abstract class IRVariable extends TIRVariable { */ class IRUserVariable extends IRVariable, TIRUserVariable { Language::Variable var; - Language::Type type; + Language::LanguageType type; IRUserVariable() { this = TIRUserVariable(var, type, func) } @@ -71,7 +82,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable { result = getVariable().toString() + " " + getVariable().getLocation().toString() } - final override Language::Type getType() { result = type } + final override Language::LanguageType getLanguageType() { result = type } /** * Gets the original user-declared variable. @@ -110,11 +121,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable { Language::AST ast; TempVariableTag tag; - Language::Type type; + Language::LanguageType type; IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) } - final override Language::Type getType() { result = type } + final override Language::LanguageType getLanguageType() { result = type } final override Language::AST getAST() { result = ast } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll index 6d5e697150e..049983c9126 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Instruction.qll @@ -5,6 +5,7 @@ import IRVariable import Operand private import internal.InstructionImports as Imports import Imports::EdgeKind +import Imports::IRType import Imports::MemoryAccessKind import Imports::Opcode private import Imports::OperandTag @@ -49,7 +50,8 @@ module InstructionSanity { ( opcode instanceof ReadSideEffectOpcode or opcode instanceof Opcode::InlineAsm or - opcode instanceof Opcode::CallSideEffect + opcode instanceof Opcode::CallSideEffect or + opcode instanceof Opcode::AliasedUse ) and tag instanceof SideEffectOperandTag ) @@ -113,10 +115,12 @@ module InstructionSanity { } query predicate missingOperandType(Operand operand, string message) { - exists(Language::Function func | + exists(Language::Function func, Instruction use | not exists(operand.getType()) and - func = operand.getUse().getEnclosingFunction() and - message = "Operand missing type in function '" + Language::getIdentityString(func) + "'." + use = operand.getUse() and + func = use.getEnclosingFunction() and + message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + + "' missing type in function '" + Language::getIdentityString(func) + "'." ) } @@ -260,6 +264,7 @@ module InstructionSanity { ) { exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | not useOperand.getUse() instanceof UnmodeledUseInstruction and + not defInstr instanceof UnmodeledDefinitionInstruction and pointOfEvaluation(useOperand, useBlock, useIndex) and defInstr = useOperand.getAnyDef() and ( @@ -321,7 +326,7 @@ class Instruction extends Construction::TInstruction { } private string getResultPrefix() { - if getResultType() instanceof Language::VoidType + if getResultIRType() instanceof IRVoidType then result = "v" else if hasMemoryResult() @@ -353,23 +358,6 @@ class Instruction extends Construction::TInstruction { ) } - bindingset[type] - private string getValueCategoryString(string type) { - if isGLValue() then result = "glval<" + type + ">" else result = type - } - - string getResultTypeString() { - exists(string valcat | - valcat = getValueCategoryString(getResultType().toString()) and - if - getResultType() instanceof Language::UnknownType and - not isGLValue() and - exists(getResultSize()) - then result = valcat + "[" + getResultSize().toString() + "]" - else result = valcat - ) - } - /** * Gets a human-readable string that uniquely identifies this instruction * within the function. This string is used to refer to this instruction when @@ -389,7 +377,9 @@ class Instruction extends Construction::TInstruction { * * Example: `r1_1(int*)` */ - final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" } + final string getResultString() { + result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + } /** * Gets a string describing the operands of this instruction, suitable for @@ -457,6 +447,16 @@ class Instruction extends Construction::TInstruction { result = Construction::getInstructionUnconvertedResultExpression(this) } + final Language::LanguageType getResultLanguageType() { + result = Construction::getInstructionResultType(this) + } + + /** + * Gets the type of the result produced by this instruction. If the instruction does not produce + * a result, its result type will be `IRVoidType`. + */ + final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + /** * Gets the type of the result produced by this instruction. If the * instruction does not produce a result, its result type will be `VoidType`. @@ -464,7 +464,16 @@ class Instruction extends Construction::TInstruction { * If `isGLValue()` holds, then the result type of this instruction should be * thought of as "pointer to `getResultType()`". */ - final Language::Type getResultType() { Construction::instructionHasType(this, result, _) } + final Language::Type getResultType() { + exists(Language::LanguageType resultType | + resultType = getResultLanguageType() and + ( + resultType.hasUnspecifiedType(result, _) + or + not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType + ) + ) + } /** * Holds if the result produced by this instruction is a glvalue. If this @@ -484,7 +493,7 @@ class Instruction extends Construction::TInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::instructionHasType(this, _, true) } + final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -493,16 +502,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { - if isGLValue() - then - // a glvalue is always pointer-sized. - result = Language::getPointerSize() - else - if getResultType() instanceof Language::UnknownType - then result = Construction::getInstructionResultSize(this) - else result = Language::getTypeSize(getResultType()) - } + final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -1384,7 +1384,7 @@ class CatchInstruction extends Instruction { * An instruction that catches an exception of a specific type. */ class CatchByTypeInstruction extends CatchInstruction { - Language::Type exceptionType; + Language::LanguageType exceptionType; CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and @@ -1396,7 +1396,7 @@ class CatchByTypeInstruction extends CatchInstruction { /** * Gets the type of exception to be caught. */ - final Language::Type getExceptionType() { result = exceptionType } + final Language::LanguageType getExceptionType() { result = exceptionType } } /** @@ -1423,6 +1423,13 @@ class AliasedDefinitionInstruction extends Instruction { final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess } } +/** + * An instruction that consumes all escaped memory on exit from the function. + */ +class AliasedUseInstruction extends Instruction { + AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } +} + class UnmodeledUseInstruction extends Instruction { UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll index a23fb081afe..07dd107bf12 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/Operand.qll @@ -1,9 +1,10 @@ private import internal.IRInternal -import Instruction -import IRBlock +private import Instruction +private import IRBlock private import internal.OperandImports as Imports -import Imports::MemoryAccessKind -import Imports::Overlap +private import Imports::MemoryAccessKind +private import Imports::IRType +private import Imports::Overlap private import Imports::OperandTag cached @@ -143,22 +144,40 @@ class Operand extends TOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - Language::Type getType() { result = getAnyDef().getResultType() } + Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + + /** + * Gets the language-neutral type of the value consumed by this operand. This is usually the same + * as the result type of the definition instruction consumed by this operand. For register + * operands, this is always the case. For some memory operands, the operand type may be different + * from the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the value consumed by this operand. This is usually the same as the + * result type of the definition instruction consumed by this operand. For register operands, + * this is always the case. For some memory operands, the operand type may be different from + * the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final Language::Type getType() { getLanguageType().hasType(result, _) } /** * Holds if the value consumed by this operand is a glvalue. If this * holds, the value of the operand represents the address of a location, * and the type of the location is given by `getType()`. If this does * not hold, the value of the operand represents a value whose type is - * given by `getResultType()`. + * given by `getType()`. */ - predicate isGLValue() { getAnyDef().isGLValue() } + final predicate isGLValue() { getLanguageType().hasType(_, true) } /** * Gets the size of the value consumed by this operand, in bytes. If the operand does not have * a known constant size, this predicate does not hold. */ - int getSize() { result = Language::getTypeSize(getType()) } + final int getSize() { result = getLanguageType().getByteSize() } } /** @@ -170,11 +189,6 @@ class MemoryOperand extends Operand { this = TPhiOperand(_, _, _, _) } - override predicate isGLValue() { - // A `MemoryOperand` can never be a glvalue - none() - } - /** * Gets the kind of memory access performed by the operand. */ @@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; - final override Language::Type getType() { + final override Language::LanguageType getLanguageType() { result = Construction::getInstructionOperandType(useInstr, tag) } } @@ -381,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand { class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; - final override int getSize() { - if getType() instanceof Language::UnknownType - then result = Construction::getInstructionOperandSize(useInstr, tag) - else result = Language::getTypeSize(getType()) - } - override MemoryAccessKind getMemoryAccess() { + useInstr instanceof AliasedUseInstruction and + result instanceof NonLocalMayMemoryAccess + or useInstr instanceof CallSideEffectInstruction and result instanceof EscapedMayMemoryAccess or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll index df6c5be7728..79e5a2b6713 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll @@ -1,5 +1,5 @@ private import internal.ValueNumberingInternal -private import cpp +private import internal.ValueNumberingImports private import IR /** @@ -23,31 +23,32 @@ newtype TValueNumber = initializeParameterValueNumber(_, irFunc, var) } or TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or - TConstantValueNumber(IRFunction irFunc, Type type, string value) { + TConstantValueNumber(IRFunction irFunc, IRType type, string value) { constantValueNumber(_, irFunc, type, value) } or - TStringConstantValueNumber(IRFunction irFunc, Type type, string value) { + TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) { stringConstantValueNumber(_, irFunc, type, value) } or - TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) { + TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) { fieldAddressValueNumber(_, irFunc, field, objectAddress) } or TBinaryValueNumber( - IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand + IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand ) { binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand) } or TPointerArithmeticValueNumber( - IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand ) { pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand) } or - TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) { + TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) { unaryValueNumber(_, irFunc, opcode, type, operand) } or TInheritanceConversionValueNumber( - IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand + IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass, + ValueNumber operand ) { inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand) } or @@ -59,7 +60,7 @@ newtype TValueNumber = class ValueNumber extends TValueNumber { final string toString() { result = getExampleInstruction().getResultId() } - final Location getLocation() { result = getExampleInstruction().getLocation() } + final Language::Location getLocation() { result = getExampleInstruction().getLocation() } /** * Gets the instructions that have been assigned this value number. This will always produce at @@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF } private predicate constantValueNumber( - ConstantInstruction instr, IRFunction irFunc, Type type, string value + ConstantInstruction instr, IRFunction irFunc, IRType type, string value ) { instr.getEnclosingIRFunction() = irFunc and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getValue() = value } private predicate stringConstantValueNumber( - StringConstantInstruction instr, IRFunction irFunc, Type type, string value + StringConstantInstruction instr, IRFunction irFunc, IRType type, string value ) { instr.getEnclosingIRFunction() = irFunc and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getValue().getValue() = value } private predicate fieldAddressValueNumber( - FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress + FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress ) { instr.getEnclosingIRFunction() = irFunc and instr.getField() = field and @@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber( } private predicate binaryValueNumber( - BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, + BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand ) { instr.getEnclosingIRFunction() = irFunc and not instr instanceof PointerArithmeticInstruction and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and valueNumber(instr.getLeft()) = leftOperand and valueNumber(instr.getRight()) = rightOperand } private predicate pointerArithmeticValueNumber( - PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize, - ValueNumber leftOperand, ValueNumber rightOperand + PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, + int elementSize, ValueNumber leftOperand, ValueNumber rightOperand ) { instr.getEnclosingIRFunction() = irFunc and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getElementSize() = elementSize and valueNumber(instr.getLeft()) = leftOperand and valueNumber(instr.getRight()) = rightOperand } private predicate unaryValueNumber( - UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand + UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand ) { instr.getEnclosingIRFunction() = irFunc and not instr instanceof InheritanceConversionInstruction and not instr instanceof CopyInstruction and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and valueNumber(instr.getUnary()) = operand } private predicate inheritanceConversionValueNumber( - InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass, - Class derivedClass, ValueNumber operand + InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, + Language::Class baseClass, Language::Class derivedClass, ValueNumber operand ) { instr.getEnclosingIRFunction() = irFunc and instr.getOpcode() = opcode and @@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber( */ private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) { instr.getEnclosingIRFunction() = irFunc and - not instr.getResultType() instanceof VoidType and + not instr.getResultIRType() instanceof IRVoidType and not numberableInstruction(instr) } @@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) { initializeThisValueNumber(instr, irFunc) and result = TInitializeThisValueNumber(irFunc) or - exists(Type type, string value | + exists(IRType type, string value | constantValueNumber(instr, irFunc, type, value) and result = TConstantValueNumber(irFunc, type, value) ) or - exists(Type type, string value | + exists(IRType type, string value | stringConstantValueNumber(instr, irFunc, type, value) and result = TStringConstantValueNumber(irFunc, type, value) ) or - exists(Field field, ValueNumber objectAddress | + exists(Language::Field field, ValueNumber objectAddress | fieldAddressValueNumber(instr, irFunc, field, objectAddress) and result = TFieldAddressValueNumber(irFunc, field, objectAddress) ) or - exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand | + exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand | binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand) ) or - exists(Opcode opcode, Type type, ValueNumber operand | + exists(Opcode opcode, IRType type, ValueNumber operand | unaryValueNumber(instr, irFunc, opcode, type, operand) and result = TUnaryValueNumber(irFunc, opcode, type, operand) ) or - exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand | + exists( + Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand + | inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand) ) or exists( - Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand + Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand | pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand, rightOperand) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll new file mode 100644 index 00000000000..0282f6887f1 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.ir.internal.Overlap +import semmle.code.cpp.ir.internal.IRCppLanguage as Language diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll index d0f70f61498..3ec6766399e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll @@ -1,6 +1,8 @@ private import cpp import semmle.code.cpp.ir.implementation.raw.IR private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.internal.CppType +private import semmle.code.cpp.ir.internal.Overlap private import semmle.code.cpp.ir.internal.TempVariableTag private import InstructionTag private import TranslatedCondition @@ -25,16 +27,16 @@ private module Cached { cached newtype TInstruction = MkInstruction(TranslatedElement element, InstructionTag tag) { - element.hasInstruction(_, tag, _, _) + element.hasInstruction(_, tag, _) } cached - predicate hasUserVariable(Function func, Variable var, Type type) { + predicate hasUserVariable(Function func, Variable var, CppType type) { getTranslatedFunction(func).hasUserVariable(var, type) } cached - predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, Type type) { + predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) { exists(TranslatedElement element | element.getAST() = ast and func = element.getFunction() and @@ -85,22 +87,16 @@ private module Cached { } cached - Type getInstructionOperandType(Instruction instruction, TypedOperandTag tag) { + CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) { // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as // the result type of the load. - result = instruction.(LoadInstruction).getResultType() + result = instruction.(LoadInstruction).getResultLanguageType() or not instruction instanceof LoadInstruction and result = getInstructionTranslatedElement(instruction) .getInstructionOperandType(getInstructionTag(instruction), tag) } - cached - int getInstructionOperandSize(Instruction instruction, SideEffectOperandTag tag) { - result = getInstructionTranslatedElement(instruction) - .getInstructionOperandSize(getInstructionTag(instruction), tag) - } - cached Instruction getPhiOperandDefinition( PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap @@ -220,15 +216,15 @@ private module Cached { } cached - predicate instructionHasType(Instruction instruction, Type type, boolean isGLValue) { + CppType getInstructionResultType(Instruction instruction) { getInstructionTranslatedElement(instruction) - .hasInstruction(_, getInstructionTag(instruction), type, isGLValue) + .hasInstruction(_, getInstructionTag(instruction), result) } cached Opcode getInstructionOpcode(Instruction instruction) { getInstructionTranslatedElement(instruction) - .hasInstruction(result, getInstructionTag(instruction), _, _) + .hasInstruction(result, getInstructionTag(instruction), _) } cached @@ -283,7 +279,7 @@ private module Cached { } cached - Type getInstructionExceptionType(Instruction instruction) { + CppType getInstructionExceptionType(Instruction instruction) { result = getInstructionTranslatedElement(instruction) .getInstructionExceptionType(getInstructionTag(instruction)) } @@ -310,6 +306,11 @@ private module Cached { ) } + cached + predicate needsUnknownOpaqueType(int byteSize) { + exists(TranslatedElement element | element.needsUnknownOpaqueType(byteSize)) + } + cached int getInstructionResultSize(Instruction instruction) { exists(TranslatedElement element, InstructionTag tag | diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll index 74f7b4a2b64..42d6e7db693 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRImports.qll @@ -1,2 +1,3 @@ import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll index 1f0f5eaccde..8c60565defc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/IRVariableImports.qll @@ -1,3 +1,4 @@ +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag import semmle.code.cpp.ir.internal.IRUtilities as IRUtilities import semmle.code.cpp.ir.internal.TempVariableTag as TTempVariableTag diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll index 0138af075dc..a75f70dbe2f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll @@ -1,4 +1,5 @@ import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind import semmle.code.cpp.ir.implementation.Opcode as Opcode import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll index c18ff827992..7b0d33546cf 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/InstructionTag.qll @@ -26,6 +26,7 @@ newtype TInstructionTag = UnmodeledDefinitionTag() or UnmodeledUseTag() or AliasedDefinitionTag() or + AliasedUseTag() or SwitchBranchTag() or CallTargetTag() or CallTag() or @@ -119,6 +120,8 @@ string getInstructionTagId(TInstructionTag tag) { or tag = AliasedDefinitionTag() and result = "AliasedDef" or + tag = AliasedUseTag() and result = "AliasedUse" + or tag = SwitchBranchTag() and result = "SwitchBranch" or tag = CallTargetTag() and result = "CallTarget" diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll index e626662ccf2..3c781579cee 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/OperandImports.qll @@ -1,3 +1,4 @@ import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.internal.Overlap as Overlap import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll index fd8f15a6eb3..f6cd98bcdb4 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll @@ -1,6 +1,7 @@ private import cpp private import semmle.code.cpp.ir.implementation.Opcode private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.models.interfaces.SideEffect private import InstructionTag private import TranslatedElement @@ -33,13 +34,10 @@ abstract class TranslatedCall extends TranslatedExpr { else result = getFirstCallTargetInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = CallTag() and opcode instanceof Opcode::Call and - resultType = getCallResultType() and - isGLValue = false + resultType = getTypeForPRValue(getCallResultType()) or hasSideEffect() and tag = CallSideEffectTag() and @@ -47,13 +45,12 @@ abstract class TranslatedCall extends TranslatedExpr { if hasWriteSideEffect() then ( opcode instanceof Opcode::CallSideEffect and - resultType instanceof UnknownType + resultType = getUnknownType() ) else ( opcode instanceof Opcode::CallReadSideEffect and - resultType instanceof VoidType + resultType = getVoidType() ) - ) and - isGLValue = false + ) } override Instruction getChildSuccessor(TranslatedElement child) { @@ -118,11 +115,11 @@ abstract class TranslatedCall extends TranslatedExpr { result = getEnclosingFunction().getUnmodeledDefinitionInstruction() } - final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { + final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { tag = CallSideEffectTag() and hasSideEffect() and operandTag instanceof SideEffectOperandTag and - result instanceof UnknownType + result = getUnknownType() } final override Instruction getResult() { result = getInstruction(CallTag()) } @@ -224,18 +221,12 @@ abstract class TranslatedDirectCall extends TranslatedCall { final override Instruction getCallTargetResult() { result = getInstruction(CallTargetTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { - TranslatedCall.super.hasInstruction(opcode, tag, resultType, isGLValue) + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + TranslatedCall.super.hasInstruction(opcode, tag, resultType) or tag = CallTargetTag() and opcode instanceof Opcode::FunctionAddress and - // The database does not contain a `FunctionType` for a function unless - // its address was taken, so we'll just use glval instead of - // glval. - resultType instanceof UnknownType and - isGLValue = true + resultType = getFunctionGLValueType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -253,7 +244,7 @@ abstract class TranslatedDirectCall extends TranslatedCall { abstract class TranslatedCallExpr extends TranslatedNonConstantExpr, TranslatedCall { override Call expr; - final override Type getCallResultType() { result = getResultType() } + final override Type getCallResultType() { result = expr.getType() } final override predicate hasArguments() { exists(expr.getArgument(0)) } @@ -349,9 +340,7 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects { ) } - override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type t, boolean isGLValue) { - none() - } + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { none() } override Instruction getFirstInstruction() { result = getChild(0).getFirstInstruction() } @@ -359,7 +348,9 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects { override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() } - override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() } + override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { + none() + } /** * Gets the `TranslatedFunction` containing this expression. @@ -406,34 +397,30 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } - override predicate hasInstruction(Opcode opcode, InstructionTag tag, Type t, boolean isGLValue) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { isWrite() and hasSpecificWriteSideEffect(opcode) and tag = OnlyInstructionTag() and ( opcode instanceof BufferAccessOpcode and - t instanceof UnknownType + type = getUnknownType() or not opcode instanceof BufferAccessOpcode and - ( - t = arg.getUnspecifiedType().(DerivedType).getBaseType() and - not t instanceof VoidType - or - arg.getUnspecifiedType().(DerivedType).getBaseType() instanceof VoidType and - t instanceof UnknownType + exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() | + if baseType instanceof VoidType + then type = getUnknownType() + else type = getTypeForPRValueOrUnknown(baseType) ) or index = -1 and not arg.getUnspecifiedType() instanceof DerivedType and - t = arg.getUnspecifiedType() - ) and - isGLValue = false + type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType()) + ) or not isWrite() and hasSpecificReadSideEffect(opcode) and tag = OnlyInstructionTag() and - t instanceof VoidType and - isGLValue = false + type = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -459,15 +446,27 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff .getFullyConverted()).getResult() } - override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { - tag instanceof OnlyInstructionTag and - result = arg.getType().getUnspecifiedType().(DerivedType).getBaseType() and - operandTag instanceof SideEffectOperandTag - or - tag instanceof OnlyInstructionTag and - result = arg.getType().getUnspecifiedType() and - not result instanceof DerivedType and - operandTag instanceof SideEffectOperandTag + override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { + if hasSpecificReadSideEffect(any(Opcode::BufferReadSideEffect op)) + then + result = getUnknownType() and + tag instanceof OnlyInstructionTag and + operandTag instanceof SideEffectOperandTag + else + exists(Type operandType | + tag instanceof OnlyInstructionTag and + operandType = arg.getType().getUnspecifiedType().(DerivedType).getBaseType() and + operandTag instanceof SideEffectOperandTag + or + tag instanceof OnlyInstructionTag and + operandType = arg.getType().getUnspecifiedType() and + not operandType instanceof DerivedType and + operandTag instanceof SideEffectOperandTag + | + // If the type we select is an incomplete type (e.g. a forward-declared `struct`), there will + // not be a `CppType` that represents that type. In that case, fall back to `UnknownCppType`. + result = getTypeForPRValueOrUnknown(operandType) + ) } predicate hasSpecificWriteSideEffect(Opcode op) { @@ -517,7 +516,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff ) or not call.getTarget() instanceof SideEffectFunction and - op instanceof Opcode::IndirectReadSideEffect + op instanceof Opcode::BufferReadSideEffect } override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll index 6c501ef4284..e2fc86db7ce 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCondition.qll @@ -1,6 +1,7 @@ private import cpp private import semmle.code.cpp.ir.implementation.Opcode private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.internal.CppType private import InstructionTag private import TranslatedElement private import TranslatedExpr @@ -37,9 +38,7 @@ abstract class TranslatedFlexibleCondition extends TranslatedCondition, Conditio final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -105,9 +104,7 @@ abstract class TranslatedBinaryLogicalOperation extends TranslatedNativeConditio result = getLeftOperand().getFirstInstruction() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -163,13 +160,10 @@ class TranslatedValueCondition extends TranslatedCondition, TTranslatedValueCond override Instruction getFirstInstruction() { result = getValueExpr().getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = ValueConditionConditionalBranchTag() and opcode instanceof Opcode::ConditionalBranch and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() } override Instruction getChildSuccessor(TranslatedElement child) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll index 1efe8cf9f78..294a539ec31 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedDeclarationEntry.qll @@ -1,7 +1,8 @@ private import cpp private import semmle.code.cpp.ir.implementation.Opcode -private import semmle.code.cpp.ir.internal.IRUtilities private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.internal.CppType +private import semmle.code.cpp.ir.internal.IRUtilities private import InstructionTag private import TranslatedElement private import TranslatedExpr @@ -54,19 +55,15 @@ abstract class TranslatedVariableDeclaration extends TranslatedElement, Initiali result = getInstruction(InitializerVariableAddressTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = InitializerVariableAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getVariableType(getVariable()) and - isGLValue = true + resultType = getTypeForGLValue(getVariableType(getVariable())) or hasUninitializedInstruction() and tag = InitializerStoreTag() and opcode instanceof Opcode::Uninitialized and - resultType = getVariableType(getVariable()) and - isGLValue = false + resultType = getTypeForPRValue(getVariableType(getVariable())) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 8ba27e9a9d6..2bffcdcc8ae 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -3,6 +3,7 @@ import semmle.code.cpp.ir.implementation.raw.IR private import semmle.code.cpp.ir.IRConfiguration private import semmle.code.cpp.ir.implementation.Opcode private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.ir.internal.TempVariableTag private import InstructionTag private import TranslatedCondition @@ -12,11 +13,6 @@ private import TranslatedExpr private import IRConstruction private import semmle.code.cpp.models.interfaces.SideEffect -/** - * Gets the built-in `int` type. - */ -Type getIntType() { result.(IntType).isImplicitlySigned() } - /** * Gets the "real" parent of `expr`. This predicate treats conversions as if * they were explicit nodes in the expression tree, rather than as implicit @@ -54,6 +50,9 @@ private predicate ignoreExprAndDescendants(Expr expr) { // constant value. isIRConstant(getRealParent(expr)) or + // Ignore descendants of `__assume` expressions, since we translated these to `NoOp`. + getRealParent(expr) instanceof AssumeExpr + or // The `DestructorCall` node for a `DestructorFieldDestruction` has a `FieldAccess` // node as its qualifier, but that `FieldAccess` does not have a child of its own. // We'll ignore that `FieldAccess`, and supply the receiver as part of the calling @@ -67,8 +66,8 @@ private predicate ignoreExprAndDescendants(Expr expr) { ) or // Do not translate input/output variables in GNU asm statements - getRealParent(expr) instanceof AsmStmt - or + // getRealParent(expr) instanceof AsmStmt + // or ignoreExprAndDescendants(getRealParent(expr)) // recursive case or // We do not yet translate destructors properly, so for now we ignore any @@ -542,9 +541,7 @@ abstract class TranslatedElement extends TTranslatedElement { * If the instruction does not return a result, `resultType` should be * `VoidType`. */ - abstract predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ); + abstract predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType); /** * Gets the `Function` that contains this element. @@ -584,7 +581,7 @@ abstract class TranslatedElement extends TTranslatedElement { * `tag` must be unique for each variable generated from the same AST node * (not just from the same `TranslatedElement`). */ - predicate hasTempVariable(TempVariableTag tag, Type type) { none() } + predicate hasTempVariable(TempVariableTag tag, CppType type) { none() } /** * If the instruction specified by `tag` is a `FunctionInstruction`, gets the @@ -629,6 +626,8 @@ abstract class TranslatedElement extends TTranslatedElement { */ int getInstructionResultSize(InstructionTag tag) { none() } + predicate needsUnknownOpaqueType(int byteSize) { none() } + /** * If the instruction specified by `tag` is a `StringConstantInstruction`, * gets the `StringLiteral` for that instruction. @@ -644,7 +643,7 @@ abstract class TranslatedElement extends TTranslatedElement { * If the instruction specified by `tag` is a `CatchByTypeInstruction`, * gets the type of the exception to be caught. */ - Type getInstructionExceptionType(InstructionTag tag) { none() } + CppType getInstructionExceptionType(InstructionTag tag) { none() } /** * If the instruction specified by `tag` is an `InheritanceConversionInstruction`, @@ -663,7 +662,7 @@ abstract class TranslatedElement extends TTranslatedElement { /** * Gets the type of the memory operand specified by `operandTag` on the the instruction specified by `tag`. */ - Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() } + CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() } /** * Gets the size of the memory operand specified by `operandTag` on the the instruction specified by `tag`. diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll index 7b9417b570d..c9806cd03c8 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll @@ -1,6 +1,8 @@ private import cpp +private import semmle.code.cpp.ir.implementation.IRType private import semmle.code.cpp.ir.implementation.Opcode private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.ir.internal.TempVariableTag private import InstructionTag private import TranslatedCondition @@ -51,10 +53,21 @@ abstract class TranslatedExpr extends TranslatedElement { */ abstract predicate producesExprResult(); + final CppType getResultType() { + if isResultGLValue() + then result = getTypeForGLValue(expr.getType()) + else result = getTypeForPRValue(expr.getType()) + } + /** - * Gets the type of the result produced by this expression. + * Holds if the result of this `TranslatedExpr` is a glvalue. */ - final Type getResultType() { result = expr.getUnspecifiedType() } + predicate isResultGLValue() { + // This implementation is overridden in `TranslatedCoreExpr` to mark them + // as glvalues if they have loads on them. It's not overridden in + // `TranslatedResultCopy` since result copies never have loads. + expr.isGLValueCategory() + } final override Locatable getAST() { result = expr } @@ -82,6 +95,22 @@ abstract class TranslatedExpr extends TranslatedElement { abstract class TranslatedCoreExpr extends TranslatedExpr { final override string toString() { result = expr.toString() } + /** + * Holds if the result of this `TranslatedExpr` is a glvalue. + */ + override predicate isResultGLValue() { + super.isResultGLValue() + or + // If this TranslatedExpr doesn't produce the result, then it must represent + // a glvalue that is then loaded by a TranslatedLoad. + hasLoad() + } + + final predicate hasLoad() { + expr.hasLValueToRValueConversion() and + not ignoreLoad(expr) + } + final override predicate producesExprResult() { // If there's no load, then this is the only TranslatedExpr for this // expression. @@ -89,30 +118,6 @@ abstract class TranslatedCoreExpr extends TranslatedExpr { // If there's a result copy, then this expression's result is the copy. not exprNeedsCopyIfNotLoaded(expr) } - - private predicate hasLoad() { - expr.hasLValueToRValueConversion() and - not ignoreLoad(expr) - } - - /** - * Returns `true` if the result of this `TranslatedExpr` is a glvalue, or - * `false` if the result is a prvalue. - * - * This predicate returns a `boolean` value instead of just a being a plain - * predicate because all of the subclass predicates that call it require a - * `boolean` value. - */ - final boolean isResultGLValue() { - if - expr.isGLValueCategory() - or - // If this TranslatedExpr doesn't produce the result, then it must represent - // a glvalue that is then loaded by a TranslatedLoad. - hasLoad() - then result = true - else result = false - } } class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, @@ -123,38 +128,32 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { ( tag = ConditionValueTrueTempAddressTag() or tag = ConditionValueFalseTempAddressTag() or tag = ConditionValueResultTempAddressTag() ) and opcode instanceof Opcode::VariableAddress and - resultType = getResultType() and - isGLValue = true + resultType = getTypeForGLValue(expr.getType()) or ( tag = ConditionValueTrueConstantTag() or tag = ConditionValueFalseConstantTag() ) and opcode instanceof Opcode::Constant and - resultType = getResultType() and - isGLValue = isResultGLValue() + resultType = getResultType() or ( tag = ConditionValueTrueStoreTag() or tag = ConditionValueFalseStoreTag() ) and opcode instanceof Opcode::Store and - resultType = getResultType() and - isGLValue = isResultGLValue() + resultType = getResultType() or tag = ConditionValueResultLoadTag() and opcode instanceof Opcode::Load and - resultType = getResultType() and - isGLValue = isResultGLValue() + resultType = getResultType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -215,9 +214,9 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, ) } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CppType type) { tag = ConditionValueTempVar() and - type = getResultType() + type = getTypeForPRValue(expr.getType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -265,13 +264,10 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad { override TranslatedElement getChild(int id) { id = 0 and result = getOperand() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = LoadTag() and opcode instanceof Opcode::Load and - resultType = expr.getUnspecifiedType() and - if expr.isGLValueCategory() then isGLValue = true else isGLValue = false + resultType = getResultType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -318,13 +314,10 @@ class TranslatedResultCopy extends TranslatedExpr, TTranslatedResultCopy { override TranslatedElement getChild(int id) { id = 0 and result = getOperand() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = ResultCopyTag() and opcode instanceof Opcode::CopyValue and - resultType = getOperand().getResultType() and - isGLValue = getOperand().isResultGLValue() + resultType = getOperand().getResultType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -372,9 +365,7 @@ class TranslatedCommaExpr extends TranslatedNonConstantExpr { child = getRightOperand() and result = getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -389,6 +380,10 @@ class TranslatedCommaExpr extends TranslatedNonConstantExpr { } } +private int getElementSize(Type type) { + result = max(type.getUnspecifiedType().(PointerType).getBaseType().getSize()) +} + abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { override CrementOperation expr; @@ -397,9 +392,9 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { final override string getInstructionConstantValue(InstructionTag tag) { tag = CrementConstantTag() and exists(Type resultType | - resultType = getResultType() and + resultType = expr.getUnspecifiedType() and ( - resultType instanceof IntegralType and result = "1" + resultType instanceof IntegralOrEnumType and result = "1" or resultType instanceof FloatingPointType and result = "1.0" or @@ -408,38 +403,34 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { ) } - private Type getConstantType() { + private CppType getConstantType() { exists(Type resultType | - resultType = getResultType() and + resultType = expr.getUnspecifiedType() and ( - resultType instanceof ArithmeticType and result = resultType + resultType instanceof ArithmeticType and + result = getTypeForPRValue(expr.getType()) or resultType instanceof PointerType and result = getIntType() ) ) } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { - isGLValue = false and - ( - tag = CrementLoadTag() and - opcode instanceof Opcode::Load and - resultType = getResultType() - or - tag = CrementConstantTag() and - opcode instanceof Opcode::Constant and - resultType = getConstantType() - or - tag = CrementOpTag() and - opcode = getOpcode() and - resultType = getResultType() - or - tag = CrementStoreTag() and - opcode instanceof Opcode::Store and - resultType = getResultType() - ) + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + tag = CrementLoadTag() and + opcode instanceof Opcode::Load and + resultType = getTypeForPRValue(expr.getType()) + or + tag = CrementConstantTag() and + opcode instanceof Opcode::Constant and + resultType = getConstantType() + or + tag = CrementOpTag() and + opcode = getOpcode() and + resultType = getTypeForPRValue(expr.getType()) + or + tag = CrementStoreTag() and + opcode instanceof Opcode::Store and + resultType = getTypeForPRValue(expr.getType()) } final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -500,7 +491,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { getOpcode() instanceof Opcode::PointerAdd or getOpcode() instanceof Opcode::PointerSub ) and - result = max(getResultType().(PointerType).getBaseType().getSize()) + result = getElementSize(expr.getType()) } final TranslatedExpr getOperand() { @@ -509,7 +500,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { final Opcode getOpcode() { exists(Type resultType | - resultType = getResultType() and + resultType = expr.getUnspecifiedType() and ( ( expr instanceof IncrementOperation and @@ -590,13 +581,10 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr { override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::PointerAdd and - resultType = getResultType() and - isGLValue = true + resultType = getTypeForGLValue(expr.getType()) } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -612,7 +600,7 @@ class TranslatedArrayExpr extends TranslatedNonConstantExpr { override int getInstructionElementSize(InstructionTag tag) { tag = OnlyInstructionTag() and - result = max(getResultType().getSize()) + result = max(expr.getUnspecifiedType().getSize()) } private TranslatedExpr getBaseOperand() { @@ -635,9 +623,7 @@ abstract class TranslatedTransparentExpr extends TranslatedNonConstantExpr { child = getOperand() and result = getParent().getChildSuccessor(this) } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -688,13 +674,10 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr { final override TranslatedElement getChild(int id) { none() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::CopyValue and - resultType = expr.getUnspecifiedType() and - isGLValue = false + resultType = getResultType() } final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } @@ -755,13 +738,10 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess { override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::VariableAddress and - resultType = getResultType() and - isGLValue = true + resultType = getTypeForGLValue(expr.getType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -781,13 +761,10 @@ class TranslatedFieldAccess extends TranslatedVariableAccess { result = getQualifier().getResult() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::FieldAddress and - resultType = getResultType() and - isGLValue = true + resultType = getTypeForGLValue(expr.getType()) } override Field getInstructionField(InstructionTag tag) { @@ -811,13 +788,10 @@ class TranslatedFunctionAccess extends TranslatedNonConstantExpr { kind instanceof GotoEdge } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::FunctionAddress and - resultType = expr.getUnspecifiedType() and - isGLValue = true + resultType = getResultType() } override Function getInstructionFunction(InstructionTag tag) { @@ -859,15 +833,10 @@ abstract class TranslatedConstantExpr extends TranslatedCoreExpr, TTranslatedVal none() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode = getOpcode() and - resultType = getResultType() and - if expr.isGLValueCategory() or expr.hasLValueToRValueConversion() - then isGLValue = true - else isGLValue = false + resultType = getResultType() } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -913,13 +882,10 @@ abstract class TranslatedSingleInstructionExpr extends TranslatedNonConstantExpr */ abstract Opcode getOpcode(); - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { opcode = getOpcode() and tag = OnlyInstructionTag() and - resultType = getResultType() and - isGLValue = isResultGLValue() + resultType = getResultType() } final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } @@ -993,13 +959,10 @@ abstract class TranslatedSingleInstructionConversion extends TranslatedConversio child = getOperand() and result = getInstruction(OnlyInstructionTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode = getOpcode() and - resultType = getResultType() and - isGLValue = isResultGLValue() + resultType = getResultType() } override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } @@ -1044,7 +1007,7 @@ class TranslatedDynamicCast extends TranslatedSingleInstructionConversion { override Opcode getOpcode() { exists(Type resultType | - resultType = getResultType() and + resultType = expr.getUnspecifiedType() and if resultType instanceof PointerType then if resultType.(PointerType).getBaseType() instanceof VoidType @@ -1102,19 +1065,14 @@ class TranslatedBoolConversion extends TranslatedConversion { child = getOperand() and result = getInstruction(BoolConversionConstantTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { - isGLValue = false and - ( - tag = BoolConversionConstantTag() and - opcode instanceof Opcode::Constant and - resultType = getOperand().getResultType() - or - tag = BoolConversionCompareTag() and - opcode instanceof Opcode::CompareNE and - resultType instanceof BoolType - ) + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + tag = BoolConversionConstantTag() and + opcode instanceof Opcode::Constant and + resultType = getOperand().getResultType() + or + tag = BoolConversionCompareTag() and + opcode instanceof Opcode::CompareNE and + resultType = getBoolType() } override Instruction getResult() { result = getInstruction(BoolConversionCompareTag()) } @@ -1245,7 +1203,7 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr { opcode instanceof Opcode::PointerSub or opcode instanceof Opcode::PointerDiff ) and - result = max(getPointerOperand().getResultType().(PointerType).getBaseType().getSize()) + result = getElementSize(getPointerOperand().getExpr().getType()) ) } @@ -1330,13 +1288,10 @@ class TranslatedAssignExpr extends TranslatedAssignment { result = getInstruction(AssignmentStoreTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = AssignmentStoreTag() and opcode instanceof Opcode::Store and - resultType = getResultType() and - isGLValue = false + resultType = getTypeForPRValue(expr.getType()) // Always a prvalue } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -1413,14 +1368,16 @@ class TranslatedAssignOperation extends TranslatedAssignment { // anyway. If we really want to model this case perfectly, we'll need the // extractor to tell us what the promoted type of the left operand would // be. - result = getLeftOperand().getResultType() + result = getLeftOperand().getExpr().getType() else // The right operand has already been converted to the type of the op. - result = getRightOperand().getResultType() + result = getRightOperand().getExpr().getType() } private predicate leftOperandNeedsConversion() { - getConvertedLeftOperandType() != getLeftOperand().getResultType() + getConvertedLeftOperandType().getUnspecifiedType() != getLeftOperand() + .getExpr() + .getUnspecifiedType() } private Opcode getOpcode() { @@ -1449,32 +1406,27 @@ class TranslatedAssignOperation extends TranslatedAssignment { expr instanceof AssignPointerSubExpr and result instanceof Opcode::PointerSub } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { - isGLValue = false and + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + tag = AssignOperationLoadTag() and + opcode instanceof Opcode::Load and + resultType = getTypeForPRValue(getLeftOperand().getExpr().getType()) + or + tag = AssignOperationOpTag() and + opcode = getOpcode() and + resultType = getTypeForPRValue(getConvertedLeftOperandType()) + or + tag = AssignmentStoreTag() and + opcode instanceof Opcode::Store and + resultType = getTypeForPRValue(expr.getType()) // Always a prvalue + or + leftOperandNeedsConversion() and + opcode instanceof Opcode::Convert and ( - tag = AssignOperationLoadTag() and - opcode instanceof Opcode::Load and - resultType = getLeftOperand().getResultType() + tag = AssignOperationConvertLeftTag() and + resultType = getTypeForPRValue(getConvertedLeftOperandType()) or - tag = AssignOperationOpTag() and - opcode = getOpcode() and - resultType = getConvertedLeftOperandType() - or - tag = AssignmentStoreTag() and - opcode instanceof Opcode::Store and - resultType = getResultType() - or - leftOperandNeedsConversion() and - opcode instanceof Opcode::Convert and - ( - tag = AssignOperationConvertLeftTag() and - resultType = getConvertedLeftOperandType() - or - tag = AssignOperationConvertResultTag() and - resultType = getLeftOperand().getResultType() - ) + tag = AssignOperationConvertResultTag() and + resultType = getTypeForPRValue(getLeftOperand().getExpr().getType()) ) } @@ -1484,7 +1436,7 @@ class TranslatedAssignOperation extends TranslatedAssignment { opcode = getOpcode() and (opcode instanceof Opcode::PointerAdd or opcode instanceof Opcode::PointerSub) ) and - result = max(getResultType().(PointerType).getBaseType().getSize()) + result = getElementSize(expr.getType()) } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -1566,13 +1518,10 @@ class TranslatedConstantAllocationSize extends TranslatedAllocationSize { final override Instruction getFirstInstruction() { result = getInstruction(AllocationSizeTag()) } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = AllocationSizeTag() and opcode instanceof Opcode::Constant and - resultType = expr.getAllocator().getParameter(0).getUnspecifiedType() and - isGLValue = false + resultType = getTypeForPRValue(expr.getAllocator().getParameter(0).getType()) } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -1605,11 +1554,8 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize { final override Instruction getFirstInstruction() { result = getExtent().getFirstInstruction() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { - isGLValue = false and - resultType = expr.getAllocator().getParameter(0).getUnspecifiedType() and + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + resultType = getTypeForPRValue(expr.getAllocator().getParameter(0).getType()) and ( // Convert the extent to `size_t`, because the AST doesn't do this already. tag = AllocationExtentConvertTag() and opcode instanceof Opcode::Convert @@ -1681,7 +1627,7 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect tag = CallTargetTag() and result = expr.getAllocator() } - final override Type getCallResultType() { result = expr.getAllocator().getUnspecifiedType() } + final override Type getCallResultType() { result = expr.getAllocator().getType() } final override TranslatedExpr getQualifier() { none() } @@ -1736,13 +1682,10 @@ class TranslatedDestructorFieldDestruction extends TranslatedNonConstantExpr, St final override TranslatedElement getChild(int id) { id = 0 and result = getDestructorCall() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::FieldAddress and - resultType = expr.getTarget().getUnspecifiedType() and - isGLValue = true + resultType = getTypeForGLValue(expr.getTarget().getType()) } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -1789,9 +1732,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont override Instruction getFirstInstruction() { result = getCondition().getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { not resultIsVoid() and ( ( @@ -1802,8 +1743,11 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont tag = ConditionValueResultTempAddressTag() ) and opcode instanceof Opcode::VariableAddress and - resultType = getResultType() and - isGLValue = true + ( + if expr.isGLValueCategory() + then resultType = getTypeForGLValue(any(UnknownType t)) // glvalue to a glvalue + else resultType = getTypeForGLValue(expr.getType()) // glvalue to the result type + ) or ( not thenIsVoid() and tag = ConditionValueTrueStoreTag() @@ -1811,13 +1755,11 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont not elseIsVoid() and tag = ConditionValueFalseStoreTag() ) and opcode instanceof Opcode::Store and - resultType = getResultType() and - isGLValue = false + resultType = getResultType() or tag = ConditionValueResultLoadTag() and opcode instanceof Opcode::Load and - resultType = getResultType() and - isGLValue = isResultGLValue() + resultType = getResultType() ) } @@ -1885,7 +1827,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont ) } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CppType type) { not resultIsVoid() and tag = ConditionValueTempVar() and type = getResultType() @@ -1945,7 +1887,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont } private predicate thenIsVoid() { - getThen().getResultType() instanceof VoidType + getThen().getResultType().getIRType() instanceof IRVoidType or // A `ThrowExpr.getType()` incorrectly returns the type of exception being // thrown, rather than `void`. Handle that case here. @@ -1953,14 +1895,14 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont } private predicate elseIsVoid() { - getElse().getResultType() instanceof VoidType + getElse().getResultType().getIRType() instanceof IRVoidType or // A `ThrowExpr.getType()` incorrectly returns the type of exception being // thrown, rather than `void`. Handle that case here. expr.getElse() instanceof ThrowExpr } - private predicate resultIsVoid() { getResultType() instanceof VoidType } + private predicate resultIsVoid() { getResultType().getIRType() instanceof IRVoidType } } /** @@ -1969,13 +1911,10 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont abstract class TranslatedThrowExpr extends TranslatedNonConstantExpr { override ThrowExpr expr; - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = ThrowTag() and opcode = getThrowOpcode() and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -2002,15 +1941,12 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex result = getInstruction(InitializerVariableAddressTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { - TranslatedThrowExpr.super.hasInstruction(opcode, tag, resultType, isGLValue) + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + TranslatedThrowExpr.super.hasInstruction(opcode, tag, resultType) or tag = InitializerVariableAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getExceptionType() and - isGLValue = true + resultType = getTypeForGLValue(getExceptionType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -2031,9 +1967,9 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex result = getIRTempVariable(expr, ThrowTempVar()) } - final override predicate hasTempVariable(TempVariableTag tag, Type type) { + final override predicate hasTempVariable(TempVariableTag tag, CppType type) { tag = ThrowTempVar() and - type = getExceptionType() + type = getTypeForPRValue(getExceptionType()) } final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -2047,10 +1983,10 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex ) } - final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { + final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { tag = ThrowTag() and operandTag instanceof LoadOperandTag and - result = getExceptionType() + result = getTypeForPRValue(getExceptionType()) } override Instruction getTargetAddress() { @@ -2065,7 +2001,7 @@ class TranslatedThrowValueExpr extends TranslatedThrowExpr, InitializationContex final override Opcode getThrowOpcode() { result instanceof Opcode::ThrowValue } - private Type getExceptionType() { result = expr.getUnspecifiedType() } + private Type getExceptionType() { result = expr.getType() } } /** @@ -2123,13 +2059,10 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr { ) } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode = getOpcode() and - resultType = getResultType() and - isGLValue = isResultGLValue() + resultType = getResultType() } final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -2196,13 +2129,10 @@ abstract class TranslatedNewOrNewArrayExpr extends TranslatedNonConstantExpr, In id = 1 and result = getInitialization() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::Convert and - resultType = getResultType() and - isGLValue = false + resultType = getResultType() } final override Instruction getFirstInstruction() { @@ -2363,9 +2293,7 @@ class TranslatedConditionDeclExpr extends TranslatedNonConstantExpr { child = getConditionExpr() and result = getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -2414,23 +2342,18 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont result = getInstruction(LoadTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = InitializerVariableAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getResultType() and - isGLValue = true + resultType = getTypeForGLValue(expr.getType()) or tag = InitializerStoreTag() and opcode instanceof Opcode::Uninitialized and - resultType = getResultType() and - isGLValue = false + resultType = getTypeForPRValue(expr.getType()) or tag = LoadTag() and opcode instanceof Opcode::Load and - resultType = getResultType() and - isGLValue = false + resultType = getTypeForPRValue(expr.getType()) } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -2456,16 +2379,16 @@ class TranslatedLambdaExpr extends TranslatedNonConstantExpr, InitializationCont result = getTempVariable(LambdaTempVar()) } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CppType type) { tag = LambdaTempVar() and - type = getResultType() + type = getTypeForPRValue(expr.getType()) } final override Instruction getTargetAddress() { result = getInstruction(InitializerVariableAddressTag()) } - final override Type getTargetType() { result = getResultType() } + final override Type getTargetType() { result = expr.getType() } private predicate hasInitializer() { exists(getInitialization()) } @@ -2496,13 +2419,10 @@ class TranslatedStmtExpr extends TranslatedNonConstantExpr { result = getInstruction(OnlyInstructionTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { opcode instanceof Opcode::CopyValue and tag instanceof OnlyInstructionTag and - resultType = expr.getType() and - isGLValue = false + resultType = getResultType() } override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) } @@ -2589,3 +2509,26 @@ private predicate exprImmediatelyDiscarded(Expr expr) { or exists(ForStmt for | for.getUpdate() = expr) } + +/** + * The IR translation of an `__assume` expression. We currently translate these as `NoOp`. In the + * future, we will probably want to do something better. At a minimum, we can model `__assume(0)` as + * `Unreached`. + */ +class TranslatedAssumeExpr extends TranslatedSingleInstructionExpr { + override AssumeExpr expr; + + final override Opcode getOpcode() { result instanceof Opcode::NoOp } + + final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } + + final override TranslatedElement getChild(int id) { none() } + + final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { + tag = OnlyInstructionTag() and + result = getParent().getChildSuccessor(this) and + kind instanceof GotoEdge + } + + final override Instruction getChildSuccessor(TranslatedElement child) { none() } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll index a035f8b9b31..5b437f45c54 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll @@ -1,6 +1,7 @@ private import cpp import semmle.code.cpp.ir.implementation.raw.IR private import semmle.code.cpp.ir.implementation.Opcode +private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.ir.internal.IRUtilities private import semmle.code.cpp.ir.implementation.internal.OperandTag private import semmle.code.cpp.ir.internal.TempVariableTag @@ -95,6 +96,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { result = getInstruction(UnmodeledUseTag()) or tag = UnmodeledUseTag() and + result = getInstruction(AliasedUseTag()) + or + tag = AliasedUseTag() and result = getInstruction(ExitFunctionTag()) ) } @@ -115,55 +119,46 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { or ( child = getDestructorDestructionList() and - if getReturnType() instanceof VoidType - then result = getInstruction(ReturnTag()) - else result = getInstruction(ReturnValueAddressTag()) + if hasReturnValue() + then result = getInstruction(ReturnValueAddressTag()) + else result = getInstruction(ReturnTag()) ) } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { ( tag = EnterFunctionTag() and opcode instanceof Opcode::EnterFunction and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() or tag = UnmodeledDefinitionTag() and opcode instanceof Opcode::UnmodeledDefinition and - resultType instanceof UnknownType and - isGLValue = false + resultType = getUnknownType() or tag = AliasedDefinitionTag() and opcode instanceof Opcode::AliasedDefinition and - resultType instanceof UnknownType and - isGLValue = false + resultType = getUnknownType() or tag = InitializeThisTag() and opcode instanceof Opcode::InitializeThis and - resultType = getThisType() and - isGLValue = true + resultType = getTypeForGLValue(getThisType()) or tag = ReturnValueAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getReturnType() and - not resultType instanceof VoidType and - isGLValue = true + resultType = getTypeForGLValue(getReturnType()) and + hasReturnValue() or ( tag = ReturnTag() and - resultType instanceof VoidType and - isGLValue = false and - if getReturnType() instanceof VoidType - then opcode instanceof Opcode::ReturnVoid - else opcode instanceof Opcode::ReturnValue + resultType = getVoidType() and + if hasReturnValue() + then opcode instanceof Opcode::ReturnValue + else opcode instanceof Opcode::ReturnVoid ) or tag = UnwindTag() and opcode instanceof Opcode::Unwind and - resultType instanceof VoidType and - isGLValue = false and + resultType = getVoidType() and ( // Only generate the `Unwind` instruction if there is any exception // handling present in the function. @@ -173,13 +168,15 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { or tag = UnmodeledUseTag() and opcode instanceof Opcode::UnmodeledUse and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() + or + tag = AliasedUseTag() and + opcode instanceof Opcode::AliasedUse and + resultType = getVoidType() or tag = ExitFunctionTag() and opcode instanceof Opcode::ExitFunction and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() ) } @@ -197,8 +194,12 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { operandTag instanceof UnmodeledUseOperandTag and result = getUnmodeledDefinitionInstruction() or + tag = AliasedUseTag() and + operandTag instanceof SideEffectOperandTag and + result = getUnmodeledDefinitionInstruction() + or tag = ReturnTag() and - not getReturnType() instanceof VoidType and + hasReturnValue() and ( operandTag instanceof AddressOperandTag and result = getInstruction(ReturnValueAddressTag()) @@ -208,11 +209,15 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { ) } - final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { + final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { tag = ReturnTag() and - not getReturnType() instanceof VoidType and + hasReturnValue() and operandTag instanceof LoadOperandTag and - result = getReturnType() + result = getTypeForPRValue(getReturnType()) + or + tag = AliasedUseTag() and + operandTag instanceof SideEffectOperandTag and + result = getUnknownType() } final override IRVariable getInstructionVariable(InstructionTag tag) { @@ -220,10 +225,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { result = getReturnVariable() } - final override predicate hasTempVariable(TempVariableTag tag, Type type) { + final override predicate hasTempVariable(TempVariableTag tag, CppType type) { tag = ReturnValueTempVar() and - type = getReturnType() and - not type instanceof VoidType + hasReturnValue() and + type = getTypeForPRValue(getReturnType()) } /** @@ -241,6 +246,11 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { result = getIRTempVariable(func, ReturnValueTempVar()) } + /** + * Holds if the function has a non-`void` return type. + */ + final predicate hasReturnValue() { not func.getUnspecifiedType() instanceof VoidType } + /** * Gets the single `UnmodeledDefinition` instruction for this function. */ @@ -271,7 +281,7 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { * parameters and local variables, plus any global variables or static data members that are * directly accessed by the function. */ - final predicate hasUserVariable(Variable var, Type type) { + final predicate hasUserVariable(Variable var, CppType type) { ( ( var instanceof GlobalOrNamespaceVariable @@ -287,10 +297,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { or var.(Parameter).getCatchBlock().getEnclosingFunction() = func ) and - type = getVariableType(var) + type = getTypeForPRValue(getVariableType(var)) } - final private Type getReturnType() { result = func.getUnspecifiedType() } + final Type getReturnType() { result = func.getType() } } /** @@ -335,18 +345,14 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter { final override Instruction getChildSuccessor(TranslatedElement child) { none() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = InitializerVariableAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getVariableType(param) and - isGLValue = true + resultType = getTypeForGLValue(getVariableType(param)) or tag = InitializerStoreTag() and opcode instanceof Opcode::InitializeParameter and - resultType = getVariableType(param) and - isGLValue = false + resultType = getTypeForPRValue(getVariableType(param)) } final override IRVariable getInstructionVariable(InstructionTag tag) { @@ -404,9 +410,7 @@ class TranslatedConstructorInitList extends TranslatedElement, InitializationCon else result = getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -469,9 +473,7 @@ class TranslatedDestructorDestructionList extends TranslatedElement, else result = getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll index 01c29d422f4..21ad11513bd 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll @@ -1,6 +1,7 @@ private import cpp private import semmle.code.cpp.ir.implementation.Opcode private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.internal.CppType private import InstructionTag private import TranslatedElement private import TranslatedExpr @@ -79,9 +80,7 @@ abstract class TranslatedListInitialization extends TranslatedInitialization, In ) } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -150,13 +149,10 @@ class TranslatedSimpleDirectInitialization extends TranslatedDirectInitializatio not expr instanceof StringLiteral } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = InitializerStoreTag() and opcode instanceof Opcode::Store and - resultType = getContext().getTargetType() and - isGLValue = false + resultType = getTypeForPRValue(getContext().getTargetType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -188,20 +184,16 @@ class TranslatedSimpleDirectInitialization extends TranslatedDirectInitializatio 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, CppType resultType) { // Load the string literal to make it a prvalue of type `char[len]` tag = InitializerLoadStringTag() and opcode instanceof Opcode::Load and - resultType = getInitializer().getResultType() and - isGLValue = false + resultType = getTypeForPRValue(expr.getType()) or // Store the string into the target. tag = InitializerStoreTag() and opcode instanceof Opcode::Store and - resultType = getInitializer().getResultType() and - isGLValue = false + resultType = getTypeForPRValue(expr.getType()) or exists(int startIndex, int elementCount | // If the initializer string isn't large enough to fill the target, then @@ -213,26 +205,22 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati // space in the target array. tag = ZeroPadStringConstantTag() and opcode instanceof Opcode::Constant and - resultType instanceof UnknownType and - isGLValue = false + resultType = getUnknownOpaqueType(elementCount * getElementType().getSize()) or // The index of the first element to be zero initialized. tag = ZeroPadStringElementIndexTag() and opcode instanceof Opcode::Constant and - resultType = getIntType() and - isGLValue = false + resultType = getIntType() or // Compute the address of the first element to be zero initialized. tag = ZeroPadStringElementAddressTag() and opcode instanceof Opcode::PointerAdd and - resultType = getElementType() and - isGLValue = true + resultType = getTypeForGLValue(getElementType()) or // Store the constant zero into the remainder of the string. tag = ZeroPadStringStoreTag() and opcode instanceof Opcode::Store and - resultType instanceof UnknownType and - isGLValue = false + resultType = getUnknownOpaqueType(elementCount * getElementType().getSize()) ) ) } @@ -326,6 +314,13 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati ) } + override predicate needsUnknownOpaqueType(int byteSize) { + exists(int elementCount | + zeroInitRange(_, elementCount) and + byteSize = elementCount * getElementType().getSize() + ) + } + override int getInstructionResultSize(InstructionTag tag) { exists(int elementCount | zeroInitRange(_, elementCount) and @@ -338,7 +333,7 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati } private Type getElementType() { - result = getContext().getTargetType().(ArrayType).getBaseType().getUnspecifiedType() + result = getContext().getTargetType().getUnspecifiedType().(ArrayType).getBaseType() } /** @@ -348,7 +343,7 @@ class TranslatedStringLiteralInitialization extends TranslatedDirectInitializati private predicate zeroInitRange(int startIndex, int elementCount) { exists(int targetCount | startIndex = expr.getUnspecifiedType().(ArrayType).getArraySize() and - targetCount = getContext().getTargetType().(ArrayType).getArraySize() and + targetCount = getContext().getTargetType().getUnspecifiedType().(ArrayType).getArraySize() and elementCount = targetCount - startIndex and elementCount > 0 ) @@ -359,9 +354,7 @@ 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, CppType resultType) { none() } @@ -412,13 +405,10 @@ abstract class TranslatedFieldInitialization extends TranslatedElement { */ final int getOrder() { result = field.getInitializationOrder() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = getFieldAddressTag() and opcode instanceof Opcode::FieldAddress and - resultType = field.getUnspecifiedType() and - isGLValue = true + resultType = getTypeForGLValue(field.getType()) } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -481,20 +471,16 @@ class TranslatedFieldValueInitialization extends TranslatedFieldInitialization, TTranslatedFieldValueInitialization { TranslatedFieldValueInitialization() { this = TTranslatedFieldValueInitialization(ast, field) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { - TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType, isGLValue) + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + TranslatedFieldInitialization.super.hasInstruction(opcode, tag, resultType) or tag = getFieldDefaultValueTag() and opcode instanceof Opcode::Constant and - resultType = field.getUnspecifiedType() and - isGLValue = false + resultType = getTypeForPRValue(field.getType()) or tag = getFieldDefaultValueStoreTag() and opcode instanceof Opcode::Store and - resultType = field.getUnspecifiedType() and - isGLValue = false + resultType = getTypeForPRValue(field.getUnspecifiedType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -557,18 +543,14 @@ abstract class TranslatedElementInitialization extends TranslatedElement { final override Instruction getFirstInstruction() { result = getInstruction(getElementIndexTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = getElementIndexTag() and opcode instanceof Opcode::Constant and - resultType = getIntType() and - isGLValue = false + resultType = getIntType() or tag = getElementAddressTag() and opcode instanceof Opcode::PointerAdd and - resultType = getElementType() and - isGLValue = true + resultType = getTypeForGLValue(getElementType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -606,7 +588,7 @@ abstract class TranslatedElementInitialization extends TranslatedElement { final ArrayOrVectorAggregateLiteral getInitList() { result = initList } - final Type getElementType() { result = initList.getElementType().getUnspecifiedType() } + final Type getElementType() { result = initList.getElementType() } } /** @@ -659,20 +641,16 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati this = TTranslatedElementValueInitialization(initList, elementIndex, elementCount) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { - TranslatedElementInitialization.super.hasInstruction(opcode, tag, resultType, isGLValue) + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { + TranslatedElementInitialization.super.hasInstruction(opcode, tag, resultType) or tag = getElementDefaultValueTag() and opcode instanceof Opcode::Constant and - resultType = getDefaultValueType() and - isGLValue = false + resultType = getDefaultValueType() or tag = getElementDefaultValueStoreTag() and opcode instanceof Opcode::Store and - resultType = getDefaultValueType() and - isGLValue = false + resultType = getDefaultValueType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -726,6 +704,10 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati override int getElementIndex() { result = elementIndex } + override predicate needsUnknownOpaqueType(int byteSize) { + elementCount != 0 and byteSize = elementCount * getElementType().getSize() + } + private InstructionTag getElementDefaultValueTag() { result = InitializerElementDefaultValueTag() } @@ -734,8 +716,10 @@ class TranslatedElementValueInitialization extends TranslatedElementInitializati result = InitializerElementDefaultValueStoreTag() } - private Type getDefaultValueType() { - if elementCount = 1 then result = getElementType() else result instanceof UnknownType + private CppType getDefaultValueType() { + if elementCount = 1 + then result = getTypeForPRValue(getElementType()) + else result = getUnknownOpaqueType(elementCount * getElementType().getSize()) } } @@ -766,13 +750,10 @@ abstract class TranslatedStructorCallFromStructor extends TranslatedElement, Str abstract class TranslatedBaseStructorCall extends TranslatedStructorCallFromStructor { final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::ConvertToBase and - resultType = call.getTarget().getDeclaringType().getUnspecifiedType() and - isGLValue = true + resultType = getTypeForGLValue(call.getTarget().getDeclaringType()) } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -822,9 +803,7 @@ class TranslatedConstructorDelegationInit extends TranslatedConstructorCallFromC result = getStructorCall().getFirstInstruction() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll index d84e9fc1c4e..62cbba8604b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/raw/internal/TranslatedStmt.qll @@ -1,6 +1,7 @@ private import cpp private import semmle.code.cpp.ir.internal.IRUtilities private import semmle.code.cpp.ir.implementation.internal.OperandTag +private import semmle.code.cpp.ir.internal.CppType private import semmle.code.cpp.ir.internal.TempVariableTag private import InstructionTag private import TranslatedCondition @@ -35,13 +36,10 @@ class TranslatedEmptyStmt extends TranslatedStmt { override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::NoOp and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -63,9 +61,7 @@ class TranslatedDeclStmt extends TranslatedStmt { override TranslatedElement getChild(int id) { result = getDeclarationEntry(id) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -112,9 +108,7 @@ class TranslatedExprStmt extends TranslatedStmt { override TranslatedElement getChild(int id) { id = 0 and result = getExpr() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -145,13 +139,10 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, InitializationCont result = getInstruction(InitializerVariableAddressTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = InitializerVariableAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getEnclosingFunction().getReturnVariable().getType() and - isGLValue = true + resultType = getTypeForGLValue(getEnclosingFunction().getReturnType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -174,7 +165,7 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, InitializationCont result = getInstruction(InitializerVariableAddressTag()) } - override Type getTargetType() { result = getEnclosingFunction().getReturnVariable().getType() } + override Type getTargetType() { result = getEnclosingFunction().getReturnType() } TranslatedInitialization getInitialization() { result = getTranslatedInitialization(stmt.getExpr().getFullyConverted()) @@ -188,13 +179,10 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt { override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::NoOp and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -218,9 +206,7 @@ class TranslatedTryStmt extends TranslatedStmt { result = getHandler(id - 1) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -262,14 +248,11 @@ class TranslatedBlock extends TranslatedStmt { override TranslatedElement getChild(int id) { result = getStmt(id) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { isEmpty() and opcode instanceof Opcode::NoOp and tag = OnlyInstructionTag() and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() } override Instruction getFirstInstruction() { @@ -330,13 +313,10 @@ abstract class TranslatedHandler extends TranslatedStmt { class TranslatedCatchByTypeHandler extends TranslatedHandler { TranslatedCatchByTypeHandler() { exists(stmt.getParameter()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = CatchTag() and opcode instanceof Opcode::CatchByType and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() } override TranslatedElement getChild(int id) { @@ -362,9 +342,9 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler { ) } - override Type getInstructionExceptionType(InstructionTag tag) { + override CppType getInstructionExceptionType(InstructionTag tag) { tag = CatchTag() and - result = stmt.getParameter().getType() + result = getTypeForPRValue(stmt.getParameter().getType()) } private TranslatedParameter getParameter() { @@ -378,13 +358,10 @@ class TranslatedCatchByTypeHandler extends TranslatedHandler { class TranslatedCatchAnyHandler extends TranslatedHandler { TranslatedCatchAnyHandler() { not exists(stmt.getParameter()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = CatchTag() and opcode instanceof Opcode::CatchAny and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -436,9 +413,7 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext { result = getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } } @@ -466,9 +441,7 @@ abstract class TranslatedLoop extends TranslatedStmt, ConditionContext { id = 1 and result = getBody() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -597,9 +570,7 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext { result = getCondition().getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { none() } @@ -649,13 +620,10 @@ class TranslatedJumpStmt extends TranslatedStmt { override TranslatedElement getChild(int id) { none() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::NoOp and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -693,13 +661,10 @@ class TranslatedSwitchStmt extends TranslatedStmt { id = 1 and result = getBody() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = SwitchBranchTag() and opcode instanceof Opcode::Switch and - resultType instanceof VoidType and - isGLValue = false + resultType = getVoidType() } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -727,37 +692,20 @@ class TranslatedSwitchStmt extends TranslatedStmt { class TranslatedAsmStmt extends TranslatedStmt { override AsmStmt stmt; - override TranslatedElement getChild(int id) { none() } + override TranslatedExpr getChild(int id) { + result = getTranslatedExpr(stmt.getChild(id).(Expr).getFullyConverted()) + } override Instruction getFirstInstruction() { - if exists(stmt.getChild(0)) - then result = getInstruction(AsmInputTag(0)) + if exists(getChild(0)) + then result = getChild(0).getFirstInstruction() else result = getInstruction(AsmTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isGLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) { tag = AsmTag() and opcode instanceof Opcode::InlineAsm and - resultType instanceof UnknownType and - isGLValue = false - or - exists(int index, VariableAccess va | - tag = AsmInputTag(index) and - stmt.getChild(index) = va and - opcode instanceof Opcode::VariableAddress and - resultType = va.getType().getUnspecifiedType() and - isGLValue = true - ) - } - - override IRVariable getInstructionVariable(InstructionTag tag) { - exists(int index | - tag = AsmInputTag(index) and - result = getIRUserVariable(stmt.getEnclosingFunction(), - stmt.getChild(index).(VariableAccess).getTarget()) - ) + resultType = getUnknownType() } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -768,29 +716,28 @@ class TranslatedAsmStmt extends TranslatedStmt { exists(int index | tag = AsmTag() and operandTag = asmOperand(index) and - result = getInstruction(AsmInputTag(index)) + result = getChild(index).getResult() ) } - final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { + final override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { tag = AsmTag() and operandTag instanceof SideEffectOperandTag and - result instanceof UnknownType + result = getUnknownType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { tag = AsmTag() and result = getParent().getChildSuccessor(this) and kind instanceof GotoEdge - or + } + + override Instruction getChildSuccessor(TranslatedElement child) { exists(int index | - tag = AsmInputTag(index) and - kind instanceof GotoEdge and - if exists(stmt.getChild(index + 1)) - then result = getInstruction(AsmInputTag(index + 1)) + child = getChild(index) and + if exists(getChild(index + 1)) + then result = getChild(index + 1).getFirstInstruction() else result = getInstruction(AsmTag()) ) } - - override Instruction getChildSuccessor(TranslatedElement child) { none() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll index 278040f8ab8..badd48552a5 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll @@ -5,6 +5,7 @@ import IRVariable import Operand private import internal.IRImports as Imports import Imports::EdgeKind +import Imports::IRType import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll index 3921472dc8e..de66a3c99dc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRSanity.qll @@ -1,2 +1,3 @@ private import IR import InstructionSanity +import IRTypeSanity diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll index a7ca4593ac4..1a607f82c29 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -5,6 +5,7 @@ import Imports::TempVariableTag private import Imports::IRUtilities private import Imports::TTempVariableTag private import Imports::TIRVariable +private import Imports::IRType IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { result.getVariable() = var and @@ -24,7 +25,17 @@ abstract class IRVariable extends TIRVariable { /** * Gets the type of the variable. */ - abstract Language::Type getType(); + final Language::Type getType() { getLanguageType().hasType(result, false) } + + /** + * Gets the language-neutral type of the variable. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the variable. + */ + abstract Language::LanguageType getLanguageType(); /** * Gets the AST node that declared this variable, or that introduced this @@ -59,7 +70,7 @@ abstract class IRVariable extends TIRVariable { */ class IRUserVariable extends IRVariable, TIRUserVariable { Language::Variable var; - Language::Type type; + Language::LanguageType type; IRUserVariable() { this = TIRUserVariable(var, type, func) } @@ -71,7 +82,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable { result = getVariable().toString() + " " + getVariable().getLocation().toString() } - final override Language::Type getType() { result = type } + final override Language::LanguageType getLanguageType() { result = type } /** * Gets the original user-declared variable. @@ -110,11 +121,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable { Language::AST ast; TempVariableTag tag; - Language::Type type; + Language::LanguageType type; IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) } - final override Language::Type getType() { result = type } + final override Language::LanguageType getLanguageType() { result = type } final override Language::AST getAST() { result = ast } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll index 6d5e697150e..049983c9126 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll @@ -5,6 +5,7 @@ import IRVariable import Operand private import internal.InstructionImports as Imports import Imports::EdgeKind +import Imports::IRType import Imports::MemoryAccessKind import Imports::Opcode private import Imports::OperandTag @@ -49,7 +50,8 @@ module InstructionSanity { ( opcode instanceof ReadSideEffectOpcode or opcode instanceof Opcode::InlineAsm or - opcode instanceof Opcode::CallSideEffect + opcode instanceof Opcode::CallSideEffect or + opcode instanceof Opcode::AliasedUse ) and tag instanceof SideEffectOperandTag ) @@ -113,10 +115,12 @@ module InstructionSanity { } query predicate missingOperandType(Operand operand, string message) { - exists(Language::Function func | + exists(Language::Function func, Instruction use | not exists(operand.getType()) and - func = operand.getUse().getEnclosingFunction() and - message = "Operand missing type in function '" + Language::getIdentityString(func) + "'." + use = operand.getUse() and + func = use.getEnclosingFunction() and + message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + + "' missing type in function '" + Language::getIdentityString(func) + "'." ) } @@ -260,6 +264,7 @@ module InstructionSanity { ) { exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | not useOperand.getUse() instanceof UnmodeledUseInstruction and + not defInstr instanceof UnmodeledDefinitionInstruction and pointOfEvaluation(useOperand, useBlock, useIndex) and defInstr = useOperand.getAnyDef() and ( @@ -321,7 +326,7 @@ class Instruction extends Construction::TInstruction { } private string getResultPrefix() { - if getResultType() instanceof Language::VoidType + if getResultIRType() instanceof IRVoidType then result = "v" else if hasMemoryResult() @@ -353,23 +358,6 @@ class Instruction extends Construction::TInstruction { ) } - bindingset[type] - private string getValueCategoryString(string type) { - if isGLValue() then result = "glval<" + type + ">" else result = type - } - - string getResultTypeString() { - exists(string valcat | - valcat = getValueCategoryString(getResultType().toString()) and - if - getResultType() instanceof Language::UnknownType and - not isGLValue() and - exists(getResultSize()) - then result = valcat + "[" + getResultSize().toString() + "]" - else result = valcat - ) - } - /** * Gets a human-readable string that uniquely identifies this instruction * within the function. This string is used to refer to this instruction when @@ -389,7 +377,9 @@ class Instruction extends Construction::TInstruction { * * Example: `r1_1(int*)` */ - final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" } + final string getResultString() { + result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + } /** * Gets a string describing the operands of this instruction, suitable for @@ -457,6 +447,16 @@ class Instruction extends Construction::TInstruction { result = Construction::getInstructionUnconvertedResultExpression(this) } + final Language::LanguageType getResultLanguageType() { + result = Construction::getInstructionResultType(this) + } + + /** + * Gets the type of the result produced by this instruction. If the instruction does not produce + * a result, its result type will be `IRVoidType`. + */ + final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + /** * Gets the type of the result produced by this instruction. If the * instruction does not produce a result, its result type will be `VoidType`. @@ -464,7 +464,16 @@ class Instruction extends Construction::TInstruction { * If `isGLValue()` holds, then the result type of this instruction should be * thought of as "pointer to `getResultType()`". */ - final Language::Type getResultType() { Construction::instructionHasType(this, result, _) } + final Language::Type getResultType() { + exists(Language::LanguageType resultType | + resultType = getResultLanguageType() and + ( + resultType.hasUnspecifiedType(result, _) + or + not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType + ) + ) + } /** * Holds if the result produced by this instruction is a glvalue. If this @@ -484,7 +493,7 @@ class Instruction extends Construction::TInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::instructionHasType(this, _, true) } + final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -493,16 +502,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { - if isGLValue() - then - // a glvalue is always pointer-sized. - result = Language::getPointerSize() - else - if getResultType() instanceof Language::UnknownType - then result = Construction::getInstructionResultSize(this) - else result = Language::getTypeSize(getResultType()) - } + final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -1384,7 +1384,7 @@ class CatchInstruction extends Instruction { * An instruction that catches an exception of a specific type. */ class CatchByTypeInstruction extends CatchInstruction { - Language::Type exceptionType; + Language::LanguageType exceptionType; CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and @@ -1396,7 +1396,7 @@ class CatchByTypeInstruction extends CatchInstruction { /** * Gets the type of exception to be caught. */ - final Language::Type getExceptionType() { result = exceptionType } + final Language::LanguageType getExceptionType() { result = exceptionType } } /** @@ -1423,6 +1423,13 @@ class AliasedDefinitionInstruction extends Instruction { final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess } } +/** + * An instruction that consumes all escaped memory on exit from the function. + */ +class AliasedUseInstruction extends Instruction { + AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } +} + class UnmodeledUseInstruction extends Instruction { UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll index a23fb081afe..07dd107bf12 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll @@ -1,9 +1,10 @@ private import internal.IRInternal -import Instruction -import IRBlock +private import Instruction +private import IRBlock private import internal.OperandImports as Imports -import Imports::MemoryAccessKind -import Imports::Overlap +private import Imports::MemoryAccessKind +private import Imports::IRType +private import Imports::Overlap private import Imports::OperandTag cached @@ -143,22 +144,40 @@ class Operand extends TOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - Language::Type getType() { result = getAnyDef().getResultType() } + Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + + /** + * Gets the language-neutral type of the value consumed by this operand. This is usually the same + * as the result type of the definition instruction consumed by this operand. For register + * operands, this is always the case. For some memory operands, the operand type may be different + * from the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the value consumed by this operand. This is usually the same as the + * result type of the definition instruction consumed by this operand. For register operands, + * this is always the case. For some memory operands, the operand type may be different from + * the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final Language::Type getType() { getLanguageType().hasType(result, _) } /** * Holds if the value consumed by this operand is a glvalue. If this * holds, the value of the operand represents the address of a location, * and the type of the location is given by `getType()`. If this does * not hold, the value of the operand represents a value whose type is - * given by `getResultType()`. + * given by `getType()`. */ - predicate isGLValue() { getAnyDef().isGLValue() } + final predicate isGLValue() { getLanguageType().hasType(_, true) } /** * Gets the size of the value consumed by this operand, in bytes. If the operand does not have * a known constant size, this predicate does not hold. */ - int getSize() { result = Language::getTypeSize(getType()) } + final int getSize() { result = getLanguageType().getByteSize() } } /** @@ -170,11 +189,6 @@ class MemoryOperand extends Operand { this = TPhiOperand(_, _, _, _) } - override predicate isGLValue() { - // A `MemoryOperand` can never be a glvalue - none() - } - /** * Gets the kind of memory access performed by the operand. */ @@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; - final override Language::Type getType() { + final override Language::LanguageType getLanguageType() { result = Construction::getInstructionOperandType(useInstr, tag) } } @@ -381,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand { class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; - final override int getSize() { - if getType() instanceof Language::UnknownType - then result = Construction::getInstructionOperandSize(useInstr, tag) - else result = Language::getTypeSize(getType()) - } - override MemoryAccessKind getMemoryAccess() { + useInstr instanceof AliasedUseInstruction and + result instanceof NonLocalMayMemoryAccess + or useInstr instanceof CallSideEffectInstruction and result instanceof EscapedMayMemoryAccess or diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll index df6c5be7728..79e5a2b6713 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll @@ -1,5 +1,5 @@ private import internal.ValueNumberingInternal -private import cpp +private import internal.ValueNumberingImports private import IR /** @@ -23,31 +23,32 @@ newtype TValueNumber = initializeParameterValueNumber(_, irFunc, var) } or TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or - TConstantValueNumber(IRFunction irFunc, Type type, string value) { + TConstantValueNumber(IRFunction irFunc, IRType type, string value) { constantValueNumber(_, irFunc, type, value) } or - TStringConstantValueNumber(IRFunction irFunc, Type type, string value) { + TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) { stringConstantValueNumber(_, irFunc, type, value) } or - TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) { + TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) { fieldAddressValueNumber(_, irFunc, field, objectAddress) } or TBinaryValueNumber( - IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand + IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand ) { binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand) } or TPointerArithmeticValueNumber( - IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand ) { pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand) } or - TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) { + TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) { unaryValueNumber(_, irFunc, opcode, type, operand) } or TInheritanceConversionValueNumber( - IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand + IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass, + ValueNumber operand ) { inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand) } or @@ -59,7 +60,7 @@ newtype TValueNumber = class ValueNumber extends TValueNumber { final string toString() { result = getExampleInstruction().getResultId() } - final Location getLocation() { result = getExampleInstruction().getLocation() } + final Language::Location getLocation() { result = getExampleInstruction().getLocation() } /** * Gets the instructions that have been assigned this value number. This will always produce at @@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF } private predicate constantValueNumber( - ConstantInstruction instr, IRFunction irFunc, Type type, string value + ConstantInstruction instr, IRFunction irFunc, IRType type, string value ) { instr.getEnclosingIRFunction() = irFunc and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getValue() = value } private predicate stringConstantValueNumber( - StringConstantInstruction instr, IRFunction irFunc, Type type, string value + StringConstantInstruction instr, IRFunction irFunc, IRType type, string value ) { instr.getEnclosingIRFunction() = irFunc and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getValue().getValue() = value } private predicate fieldAddressValueNumber( - FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress + FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress ) { instr.getEnclosingIRFunction() = irFunc and instr.getField() = field and @@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber( } private predicate binaryValueNumber( - BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, + BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand ) { instr.getEnclosingIRFunction() = irFunc and not instr instanceof PointerArithmeticInstruction and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and valueNumber(instr.getLeft()) = leftOperand and valueNumber(instr.getRight()) = rightOperand } private predicate pointerArithmeticValueNumber( - PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize, - ValueNumber leftOperand, ValueNumber rightOperand + PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, + int elementSize, ValueNumber leftOperand, ValueNumber rightOperand ) { instr.getEnclosingIRFunction() = irFunc and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getElementSize() = elementSize and valueNumber(instr.getLeft()) = leftOperand and valueNumber(instr.getRight()) = rightOperand } private predicate unaryValueNumber( - UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand + UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand ) { instr.getEnclosingIRFunction() = irFunc and not instr instanceof InheritanceConversionInstruction and not instr instanceof CopyInstruction and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and valueNumber(instr.getUnary()) = operand } private predicate inheritanceConversionValueNumber( - InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass, - Class derivedClass, ValueNumber operand + InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, + Language::Class baseClass, Language::Class derivedClass, ValueNumber operand ) { instr.getEnclosingIRFunction() = irFunc and instr.getOpcode() = opcode and @@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber( */ private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) { instr.getEnclosingIRFunction() = irFunc and - not instr.getResultType() instanceof VoidType and + not instr.getResultIRType() instanceof IRVoidType and not numberableInstruction(instr) } @@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) { initializeThisValueNumber(instr, irFunc) and result = TInitializeThisValueNumber(irFunc) or - exists(Type type, string value | + exists(IRType type, string value | constantValueNumber(instr, irFunc, type, value) and result = TConstantValueNumber(irFunc, type, value) ) or - exists(Type type, string value | + exists(IRType type, string value | stringConstantValueNumber(instr, irFunc, type, value) and result = TStringConstantValueNumber(irFunc, type, value) ) or - exists(Field field, ValueNumber objectAddress | + exists(Language::Field field, ValueNumber objectAddress | fieldAddressValueNumber(instr, irFunc, field, objectAddress) and result = TFieldAddressValueNumber(irFunc, field, objectAddress) ) or - exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand | + exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand | binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand) ) or - exists(Opcode opcode, Type type, ValueNumber operand | + exists(Opcode opcode, IRType type, ValueNumber operand | unaryValueNumber(instr, irFunc, opcode, type, operand) and result = TUnaryValueNumber(irFunc, opcode, type, operand) ) or - exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand | + exists( + Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand + | inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand) ) or exists( - Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand + Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand | pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand, rightOperand) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll new file mode 100644 index 00000000000..0282f6887f1 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll @@ -0,0 +1,2 @@ +import semmle.code.cpp.ir.internal.Overlap +import semmle.code.cpp.ir.internal.IRCppLanguage as Language diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll index 74f7b4a2b64..42d6e7db693 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRImports.qll @@ -1,2 +1,3 @@ import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll index 1f0f5eaccde..8c60565defc 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll @@ -1,3 +1,4 @@ +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.implementation.TempVariableTag as TempVariableTag import semmle.code.cpp.ir.internal.IRUtilities as IRUtilities import semmle.code.cpp.ir.internal.TempVariableTag as TTempVariableTag diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll index 0138af075dc..a75f70dbe2f 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll @@ -1,4 +1,5 @@ import semmle.code.cpp.ir.implementation.EdgeKind as EdgeKind +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind import semmle.code.cpp.ir.implementation.Opcode as Opcode import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll index e626662ccf2..3c781579cee 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/OperandImports.qll @@ -1,3 +1,4 @@ import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind +import semmle.code.cpp.ir.implementation.IRType as IRType import semmle.code.cpp.ir.internal.Overlap as Overlap import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll index a5c09c6401b..23121523a6b 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll @@ -4,34 +4,52 @@ private import Alias private import SSAConstruction private import DebugSSA +bindingset[offset] +private string getKeySuffixForOffset(int offset) { + if offset % 2 = 0 then result = "" else result = "_Chi" +} + +bindingset[offset] +private int getIndexForOffset(int offset) { result = offset / 2 } + /** * Property provide that dumps the memory access of each result. Useful for debugging SSA * construction. */ class PropertyProvider extends IRPropertyProvider { override string getInstructionProperty(Instruction instruction, string key) { - exists(MemoryLocation location | - location = getResultMemoryLocation(instruction) and - ( - key = "ResultMemoryLocation" and result = location.toString() - or - key = "ResultVirtualVariable" and result = location.getVirtualVariable().toString() + key = "ResultMemoryLocation" and + result = strictconcat(MemoryLocation loc | + loc = getResultMemoryLocation(instruction) + | + loc.toString(), "," ) - ) or - exists(MemoryLocation location | - location = getOperandMemoryLocation(instruction.getAnOperand()) and - ( - key = "OperandMemoryAccess" and result = location.toString() - or - key = "OperandVirtualVariable" and result = location.getVirtualVariable().toString() + key = "ResultVirtualVariable" and + result = strictconcat(MemoryLocation loc | + loc = getResultMemoryLocation(instruction) + | + loc.getVirtualVariable().toString(), "," ) - ) or - exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex | - hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and - defBlock.getInstruction(defIndex) = instruction and - key = "DefinitionRank[" + useLocation.toString() + "]" and + key = "OperandMemoryLocation" and + result = strictconcat(MemoryLocation loc | + loc = getOperandMemoryLocation(instruction.getAnOperand()) + | + loc.toString(), "," + ) + or + key = "OperandVirtualVariable" and + result = strictconcat(MemoryLocation loc | + loc = getOperandMemoryLocation(instruction.getAnOperand()) + | + loc.getVirtualVariable().toString(), "," + ) + or + exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset | + hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and + defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and + key = "DefinitionRank" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + "]" and result = defRank.toString() ) or @@ -41,10 +59,11 @@ class PropertyProvider extends IRPropertyProvider { result = useRank.toString() ) or - exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex | - hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and - defBlock.getInstruction(defIndex) = instruction and - key = "DefinitionReachesUse[" + useLocation.toString() + "]" and + exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset | + hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and + defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and + key = "DefinitionReachesUse" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + + "]" and result = strictconcat(IRBlock useBlock, int useRank, int useIndex | exists(Instruction useInstruction | hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 3010825d50b..7152dec5c4a 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -1,8 +1,5 @@ import SSAConstructionInternal -private import cpp -private import semmle.code.cpp.ir.implementation.Opcode -private import semmle.code.cpp.ir.implementation.internal.OperandTag -private import semmle.code.cpp.ir.internal.Overlap +private import SSAConstructionImports private import NewIR private class OldBlock = Reachability::ReachableBlock; @@ -18,7 +15,7 @@ private module Cached { } cached - predicate functionHasIR(Function func) { + predicate functionHasIR(Language::Function func) { exists(OldIR::IRFunction irFunc | irFunc.getFunction() = func) } @@ -42,7 +39,7 @@ private module Cached { not oldInstruction instanceof OldIR::PhiInstruction and hasChiNode(_, oldInstruction) } or - Unreached(Function function) { + Unreached(Language::Function function) { exists(OldInstruction oldInstruction | function = oldInstruction.getEnclosingFunction() and Reachability::isInfeasibleInstructionSuccessor(oldInstruction, _) @@ -50,12 +47,14 @@ private module Cached { } cached - predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, Type type) { + predicate hasTempVariable( + Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type + ) { exists(OldIR::IRTempVariable var | var.getEnclosingFunction() = func and var.getAST() = ast and var.getTag() = tag and - var.getType() = type + var.getLanguageType() = type ) } @@ -135,24 +134,12 @@ private module Cached { } cached - Type getInstructionOperandType(Instruction instr, TypedOperandTag tag) { + Language::LanguageType getInstructionOperandType(Instruction instr, TypedOperandTag tag) { exists(OldInstruction oldInstruction, OldIR::TypedOperand oldOperand | oldInstruction = getOldInstruction(instr) and oldOperand = oldInstruction.getAnOperand() and tag = oldOperand.getOperandTag() and - result = oldOperand.getType() - ) - } - - cached - int getInstructionOperandSize(Instruction instr, SideEffectOperandTag tag) { - exists(OldInstruction oldInstruction, OldIR::SideEffectOperand oldOperand | - oldInstruction = getOldInstruction(instr) and - oldOperand = oldInstruction.getAnOperand() and - tag = oldOperand.getOperandTag() and - // Only return a result for operands that need an explicit result size. - oldOperand.getType() instanceof UnknownType and - result = oldOperand.getSize() + result = oldOperand.getLanguageType() ) } @@ -196,20 +183,21 @@ private module Cached { } cached - Expr getInstructionConvertedResultExpression(Instruction instruction) { + Language::Expr getInstructionConvertedResultExpression(Instruction instruction) { result = getOldInstruction(instruction).getConvertedResultExpression() } cached - Expr getInstructionUnconvertedResultExpression(Instruction instruction) { + Language::Expr getInstructionUnconvertedResultExpression(Instruction instruction) { result = getOldInstruction(instruction).getUnconvertedResultExpression() } - /** + /* * This adds Chi nodes to the instruction successor relation; if an instruction has a Chi node, * that node is its successor in the new successor relation, and the Chi node's successors are * the new instructions generated from the successors of the old instruction */ + cached Instruction getInstructionSuccessor(Instruction instruction, EdgeKind kind) { if hasChiNode(_, getOldInstruction(instruction)) @@ -252,7 +240,7 @@ private module Cached { } cached - Locatable getInstructionAST(Instruction instruction) { + Language::AST getInstructionAST(Instruction instruction) { exists(OldInstruction oldInstruction | instruction = WrappedInstruction(oldInstruction) or @@ -270,29 +258,25 @@ private module Cached { } cached - predicate instructionHasType(Instruction instruction, Type type, boolean isGLValue) { + Language::LanguageType getInstructionResultType(Instruction instruction) { exists(OldInstruction oldInstruction | instruction = WrappedInstruction(oldInstruction) and - type = oldInstruction.getResultType() and - if oldInstruction.isGLValue() then isGLValue = true else isGLValue = false + result = oldInstruction.getResultLanguageType() ) or exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar | instruction = Chi(oldInstruction) and hasChiNode(vvar, oldInstruction) and - type = vvar.getType() and - isGLValue = false + result = vvar.getType() ) or exists(Alias::MemoryLocation location | instruction = Phi(_, location) and - type = location.getType() and - isGLValue = false + result = location.getType() ) or instruction = Unreached(_) and - type instanceof VoidType and - isGLValue = false + result = Language::getVoidType() } cached @@ -338,7 +322,7 @@ private module Cached { } cached - Field getInstructionField(Instruction instruction) { + Language::Field getInstructionField(Instruction instruction) { result = getOldInstruction(instruction).(OldIR::FieldInstruction).getField() } @@ -348,7 +332,7 @@ private module Cached { } cached - Function getInstructionFunction(Instruction instruction) { + Language::Function getInstructionFunction(Instruction instruction) { result = getOldInstruction(instruction).(OldIR::FunctionInstruction).getFunctionSymbol() } @@ -358,19 +342,19 @@ private module Cached { } cached - StringLiteral getInstructionStringLiteral(Instruction instruction) { + Language::StringLiteral getInstructionStringLiteral(Instruction instruction) { result = getOldInstruction(instruction).(OldIR::StringConstantInstruction).getValue() } cached - BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { + Language::BuiltInOperation getInstructionBuiltInOperation(Instruction instruction) { result = getOldInstruction(instruction) .(OldIR::BuiltInOperationInstruction) .getBuiltInOperation() } cached - Type getInstructionExceptionType(Instruction instruction) { + Language::LanguageType getInstructionExceptionType(Instruction instruction) { result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() } @@ -380,14 +364,9 @@ private module Cached { } cached - int getInstructionResultSize(Instruction instruction) { - // Only return a result for instructions that needed an explicit result size. - instruction.getResultType() instanceof UnknownType and - result = getOldInstruction(instruction).getResultSize() - } - - cached - predicate getInstructionInheritance(Instruction instruction, Class baseClass, Class derivedClass) { + predicate getInstructionInheritance( + Instruction instruction, Language::Class baseClass, Language::Class derivedClass + ) { exists(OldIR::InheritanceConversionInstruction oldInstr | oldInstr = getOldInstruction(instruction) and baseClass = oldInstr.getBaseClass() and diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll new file mode 100644 index 00000000000..00f12020a29 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll @@ -0,0 +1,3 @@ +import semmle.code.cpp.ir.implementation.Opcode +import semmle.code.cpp.ir.implementation.internal.OperandTag +import semmle.code.cpp.ir.internal.Overlap diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll index dc19a8130d9..4cfbdfe831e 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll @@ -2,4 +2,5 @@ import semmle.code.cpp.ir.implementation.raw.IR as OldIR import semmle.code.cpp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability import semmle.code.cpp.ir.implementation.raw.internal.reachability.Dominance as Dominance import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as NewIR +import semmle.code.cpp.ir.internal.IRCppLanguage as Language import SimpleSSA as Alias diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index 7cca5855a20..5925631b67c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -1,24 +1,20 @@ import AliasAnalysis -private import cpp -private import semmle.code.cpp.ir.implementation.raw.IR -private import semmle.code.cpp.ir.internal.IntegerConstant as Ints -private import semmle.code.cpp.ir.implementation.internal.OperandTag -private import semmle.code.cpp.ir.internal.Overlap +private import SimpleSSAImports private class IntValue = Ints::IntValue; private predicate hasResultMemoryAccess( - Instruction instr, IRVariable var, Type type, IntValue bitOffset + Instruction instr, IRVariable var, Language::LanguageType type, IntValue bitOffset ) { resultPointsTo(instr.getResultAddressOperand().getAnyDef(), var, bitOffset) and - type = instr.getResultType() + type = instr.getResultLanguageType() } private predicate hasOperandMemoryAccess( - MemoryOperand operand, IRVariable var, Type type, IntValue bitOffset + MemoryOperand operand, IRVariable var, Language::LanguageType type, IntValue bitOffset ) { resultPointsTo(operand.getAddressOperand().getAnyDef(), var, bitOffset) and - type = operand.getType() + type = operand.getLanguageType() } /** @@ -30,17 +26,17 @@ private predicate isVariableModeled(IRVariable var) { not variableAddressEscapes(var) and // There's no need to check for the right size. An `IRVariable` never has an `UnknownType`, so the test for // `type = var.getType()` is sufficient. - forall(Instruction instr, Type type, IntValue bitOffset | + forall(Instruction instr, Language::LanguageType type, IntValue bitOffset | hasResultMemoryAccess(instr, var, type, bitOffset) | bitOffset = 0 and - type = var.getType() + type.getIRType() = var.getIRType() ) and - forall(MemoryOperand operand, Type type, IntValue bitOffset | + forall(MemoryOperand operand, Language::LanguageType type, IntValue bitOffset | hasOperandMemoryAccess(operand, var, type, bitOffset) | bitOffset = 0 and - type = var.getType() + type.getIRType() = var.getIRType() ) } @@ -59,7 +55,7 @@ class MemoryLocation extends TMemoryLocation { final VirtualVariable getVirtualVariable() { result = this } - final Type getType() { result = var.getType() } + final Language::LanguageType getType() { result = var.getLanguageType() } final string getUniqueId() { result = var.getUniqueId() } } diff --git a/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll new file mode 100644 index 00000000000..8f160f5a456 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll @@ -0,0 +1,5 @@ +import semmle.code.cpp.ir.implementation.raw.IR +import semmle.code.cpp.ir.internal.IntegerConstant as Ints +import semmle.code.cpp.ir.implementation.internal.OperandTag +import semmle.code.cpp.ir.internal.IRCppLanguage as Language +import semmle.code.cpp.ir.internal.Overlap diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll new file mode 100644 index 00000000000..88184d90345 --- /dev/null +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll @@ -0,0 +1,507 @@ +private import cpp +private import semmle.code.cpp.Print +private import semmle.code.cpp.ir.implementation.IRType +private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction + +private int getPointerSize() { result = max(any(NullPointerType t).getSize()) } + +/** + * Works around an extractor bug where a function reference gets a size of one byte. + */ +private int getTypeSizeWorkaround(Type type) { + exists(Type unspecifiedType | + unspecifiedType = type.getUnspecifiedType() and + ( + unspecifiedType instanceof FunctionReferenceType and + result = getPointerSize() + or + exists(PointerToMemberType ptmType | + ptmType = unspecifiedType and + ( + if ptmType.getBaseType().getUnspecifiedType() instanceof RoutineType + then result = getPointerSize() * 2 + else result = getPointerSize() + ) + ) + or + exists(ArrayType arrayType | + // Treat `T[]` as `T*`. + arrayType = unspecifiedType and + not arrayType.hasArraySize() and + result = getPointerSize() + ) + ) + ) +} + +private int getTypeSize(Type type) { + if exists(getTypeSizeWorkaround(type)) + then result = getTypeSizeWorkaround(type) + else result = type.getSize() +} + +/** + * Holds if an `IRErrorType` should exist. + */ +predicate hasErrorType() { exists(ErroneousType t) } + +/** + * Holds if an `IRBooleanType` with the specified `byteSize` should exist. + */ +predicate hasBooleanType(int byteSize) { byteSize = getTypeSize(any(BoolType type)) } + +private predicate isSigned(IntegralOrEnumType type) { + type.(IntegralType).isSigned() + or + exists(Enum enumType | + // If the enum has an explicit underlying type, we'll determine signedness from that. If not, + // we'll assume unsigned. The actual rules for the implicit underlying type of an enum vary + // between compilers, so we'll need an extractor change to get this 100% right. Until then, + // unsigned is a reasonable default. + enumType = type.getUnspecifiedType() and + enumType.getExplicitUnderlyingType().getUnspecifiedType().(IntegralType).isSigned() + ) +} + +private predicate isSignedIntegerType(IntegralOrEnumType type) { + isSigned(type) and not type instanceof BoolType +} + +private predicate isUnsignedIntegerType(IntegralOrEnumType type) { + not isSigned(type) and not type instanceof BoolType +} + +/** + * Holds if an `IRSignedIntegerType` with the specified `byteSize` should exist. + */ +predicate hasSignedIntegerType(int byteSize) { + byteSize = any(IntegralOrEnumType type | isSignedIntegerType(type)).getSize() +} + +/** + * Holds if an `IRUnsignedIntegerType` with the specified `byteSize` should exist. + */ +predicate hasUnsignedIntegerType(int byteSize) { + byteSize = any(IntegralOrEnumType type | isUnsignedIntegerType(type)).getSize() +} + +/** + * Holds if an `IRFloatingPointType` with the specified `byteSize` should exist. + */ +predicate hasFloatingPointType(int byteSize) { byteSize = any(FloatingPointType type).getSize() } + +private predicate isPointerIshType(Type type) { + type instanceof PointerType + or + type instanceof ReferenceType + or + type instanceof NullPointerType + or + // Treat `T[]` as a pointer. The only place we should see these is as the type of a parameter. If + // the corresponding decayed `T*` type is available, we'll use that, but if it's not available, + // we're stuck with `T[]`. Just treat it as a pointer. + type instanceof ArrayType and not exists(type.getSize()) +} + +/** + * Holds if an `IRAddressType` with the specified `byteSize` should exist. + */ +predicate hasAddressType(int byteSize) { + // This covers all pointers, all references, and because it also looks at `NullPointerType`, it + // should always return a result that makes sense for arbitrary glvalues as well. + byteSize = any(Type type | isPointerIshType(type)).getSize() +} + +/** + * Holds if an `IRFunctionAddressType` with the specified `byteSize` should exist. + */ +predicate hasFunctionAddressType(int byteSize) { + byteSize = getPointerSize() or // Covers function lvalues + byteSize = getTypeSize(any(FunctionPointerIshType type)) +} + +private predicate isOpaqueType(Type type) { + exists(type.getSize()) and // Only include complete types + ( + type instanceof ArrayType or + type instanceof Class or + type instanceof GNUVectorType + ) + or + type instanceof PointerToMemberType // PTMs are missing size info +} + +/** + * Holds if an `IROpaqueType` with the specified `tag` and `byteSize` should exist. + */ +predicate hasOpaqueType(Type tag, int byteSize) { + isOpaqueType(tag) and byteSize = getTypeSize(tag) + or + tag instanceof UnknownType and IRConstruction::needsUnknownOpaqueType(byteSize) +} + +/** + * Gets the `IRType` that represents a prvalue of the specified `Type`. + */ +private IRType getIRTypeForPRValue(Type type) { + exists(Type unspecifiedType | unspecifiedType = type.getUnspecifiedType() | + isOpaqueType(unspecifiedType) and + exists(IROpaqueType opaqueType | opaqueType = result | + opaqueType.getByteSize() = getTypeSize(type) and + opaqueType.getTag() = unspecifiedType + ) + or + unspecifiedType instanceof BoolType and result.(IRBooleanType).getByteSize() = type.getSize() + or + isSignedIntegerType(unspecifiedType) and + result.(IRSignedIntegerType).getByteSize() = type.getSize() + or + isUnsignedIntegerType(unspecifiedType) and + result.(IRUnsignedIntegerType).getByteSize() = type.getSize() + or + unspecifiedType instanceof FloatingPointType and + result.(IRFloatingPointType).getByteSize() = type.getSize() + or + isPointerIshType(unspecifiedType) and result.(IRAddressType).getByteSize() = getTypeSize(type) + or + unspecifiedType instanceof FunctionPointerIshType and + result.(IRFunctionAddressType).getByteSize() = getTypeSize(type) + or + unspecifiedType instanceof VoidType and result instanceof IRVoidType + or + unspecifiedType instanceof ErroneousType and result instanceof IRErrorType + or + unspecifiedType instanceof UnknownType and result instanceof IRUnknownType + ) +} + +private newtype TCppType = + TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or + TFunctionGLValueType() or + TGLValueAddressType(Type type) or + TUnknownOpaqueType(int byteSize) { IRConstruction::needsUnknownOpaqueType(byteSize) } or + TUnknownType() + +/** + * The C++ type of an IR entity. + * This cannot just be `Type` for a couple reasons: + * - Some types needed by the IR might not exist in the database (e.g. `RoutineType`s for functions + * that are always called directly) + * - Some types needed by the IR are not representable in the C++ type system (e.g. the result type + * of a `VariableAddress` where the variable is of reference type) + */ +class CppType extends TCppType { + string toString() { none() } + + /** Gets a string used in IR dumps */ + string getDumpString() { result = toString() } + + /** Gets the size of the type in bytes, if known. */ + final int getByteSize() { result = getIRType().getByteSize() } + + /** + * Gets the `IRType` that represents this `CppType`. Many different `CppType`s can map to a single + * `IRType`. + */ + IRType getIRType() { none() } + + /** + * Holds if the `CppType` represents a prvalue of type `Type` (if `isGLValue` is `false`), or if + * it represents a glvalue of type `Type` (if `isGLValue` is `true`). + */ + predicate hasType(Type type, boolean isGLValue) { none() } + + final predicate hasUnspecifiedType(Type type, boolean isGLValue) { + exists(Type specifiedType | + hasType(specifiedType, isGLValue) and + type = specifiedType.getUnspecifiedType() + ) + } +} + +/** + * A `CppType` that wraps an existing `Type` (either as a prvalue or a glvalue). + */ +private class CppWrappedType extends CppType { + Type ctype; + + CppWrappedType() { + this = TPRValueType(ctype) or + this = TGLValueAddressType(ctype) + } +} + +/** + * A `CppType` that represents a prvalue of an existing `Type`. + */ +private class CppPRValueType extends CppWrappedType, TPRValueType { + final override string toString() { result = ctype.toString() } + + final override string getDumpString() { result = ctype.getUnspecifiedType().toString() } + + final override IRType getIRType() { result = getIRTypeForPRValue(ctype) } + + final override predicate hasType(Type type, boolean isGLValue) { + type = ctype and + isGLValue = false + } +} + +/** + * A `CppType` that has unknown type but a known size. Generally to represent synthesized types that + * occur in certain cases during IR construction, such as the type of a zero-initialized segment of + * a partially-initialized array. + */ +private class CppUnknownOpaqueType extends CppType, TUnknownOpaqueType { + int byteSize; + + CppUnknownOpaqueType() { this = TUnknownOpaqueType(byteSize) } + + final override string toString() { result = "unknown[" + byteSize.toString() + "]" } + + final override IROpaqueType getIRType() { + result.getByteSize() = byteSize and result.getTag() instanceof UnknownType + } + + override predicate hasType(Type type, boolean isGLValue) { + type instanceof UnknownType and isGLValue = false + } +} + +/** + * A `CppType` that represents a glvalue of an existing `Type`. + */ +private class CppGLValueAddressType extends CppWrappedType, TGLValueAddressType { + final override string toString() { result = "glval<" + ctype.toString() + ">" } + + final override string getDumpString() { + result = "glval<" + ctype.getUnspecifiedType().toString() + ">" + } + + final override IRAddressType getIRType() { result.getByteSize() = getPointerSize() } + + final override predicate hasType(Type type, boolean isGLValue) { + type = ctype and + isGLValue = true + } +} + +/** + * A `CppType` that represents a function lvalue. + */ +private class CppFunctionGLValueType extends CppType, TFunctionGLValueType { + final override string toString() { result = "glval" } + + final override IRFunctionAddressType getIRType() { result.getByteSize() = getPointerSize() } + + final override predicate hasType(Type type, boolean isGLValue) { + type instanceof UnknownType and isGLValue = true + } +} + +/** + * A `CppType` that represents an unknown type. + */ +private class CppUnknownType extends CppType, TUnknownType { + final override string toString() { result = any(UnknownType type).toString() } + + final override IRUnknownType getIRType() { any() } + + final override predicate hasType(Type type, boolean isGLValue) { + type instanceof UnknownType and isGLValue = false + } +} + +/** + * Gets the single instance of `CppUnknownType`. + */ +CppUnknownType getUnknownType() { any() } + +/** + * Gets the `CppType` that represents a prvalue of type `void`. + */ +CppPRValueType getVoidType() { exists(VoidType voidType | result.hasType(voidType, false)) } + +/** + * Gets the `CppType` that represents a prvalue of type `type`. + */ +CppType getTypeForPRValue(Type type) { + if type instanceof UnknownType + then result instanceof CppUnknownType + else result.hasType(type, false) +} + +/** + * Gets the `CppType` that represents a prvalue of type `type`, if such a `CppType` exists. + * Otherwise, gets `CppUnknownType`. + */ +CppType getTypeForPRValueOrUnknown(Type type) { + result = getTypeForPRValue(type) + or + not exists(getTypeForPRValue(type)) and result = getUnknownType() +} + +/** + * Gets the `CppType` that represents a glvalue of type `type`. + */ +CppType getTypeForGLValue(Type type) { result.hasType(type, true) } + +/** + * Gets the `CppType` that represents a prvalue of type `int`. + */ +CppPRValueType getIntType() { + exists(IntType type | + type.isImplicitlySigned() and + result.hasType(type, false) + ) +} + +/** + * Gets the `CppType` that represents a prvalue of type `bool`. + */ +CppPRValueType getBoolType() { exists(BoolType type | result.hasType(type, false)) } + +/** + * Gets the `CppType` that represents a glvalue of function type. + */ +CppFunctionGLValueType getFunctionGLValueType() { any() } + +/** + * Gets the `CppType` that represents a opaque of unknown type with size `byteSize`. + */ +CppUnknownOpaqueType getUnknownOpaqueType(int byteSize) { result.getByteSize() = byteSize } + +/** + * Gets the `CppType` that is the canonical type for an `IRBooleanType` with the specified + * `byteSize`. + */ +CppWrappedType getCanonicalBooleanType(int byteSize) { + exists(BoolType type | result = TPRValueType(type) and byteSize = type.getSize()) +} + +/** + * Compute the sorting priority of an `IntegralType` based on its signedness. + */ +private int getSignPriority(IntegralType type) { + // Explicitly unsigned types sort first. Explicitly signed types sort last. Types with no explicit + // signedness sort in between. This lets us always choose `int` over `signed int`, while also + // choosing `unsigned char`+`char` when `char` is signed, and `unsigned char`+`signed char` when + // `char` is unsigned. + if type.isExplicitlyUnsigned() + then result = 2 + else + if type.isExplicitlySigned() + then result = 0 + else result = 1 +} + +/** + * Gets the sort priority of an `IntegralType` based on its kind. + */ +private int getKindPriority(IntegralType type) { + // `CharType` sorts lower so that we prefer the plain integer types when they have the same size + // as a `CharType`. + if type instanceof CharType then result = 0 else result = 1 +} + +/** + * Gets the `CppType` that is the canonical type for an `IRSignedIntegerType` with the specified + * `byteSize`. + */ +CppPRValueType getCanonicalSignedIntegerType(int byteSize) { + result = TPRValueType(max(IntegralType type | + type.isSigned() and type.getSize() = byteSize + | + type order by getKindPriority(type), getSignPriority(type), type.toString() desc + )) +} + +/** + * Gets the `CppType` that is the canonical type for an `IRUnsignedIntegerType` with the specified + * `byteSize`. + */ +CppPRValueType getCanonicalUnsignedIntegerType(int byteSize) { + result = TPRValueType(max(IntegralType type | + type.isUnsigned() and type.getSize() = byteSize + | + type order by getKindPriority(type), getSignPriority(type), type.toString() desc + )) +} + +/** + * Gets the `CppType` that is the canonical type for an `IRFloatingPointType` with the specified + * `byteSize`. + */ +CppPRValueType getCanonicalFloatingPointType(int byteSize) { + result = TPRValueType(max(FloatingPointType type | + type.getSize() = byteSize + | + type order by type.toString() desc + )) +} + +/** + * Gets the `CppType` that is the canonical type for an `IRAddressType` with the specified + * `byteSize`. + */ +CppPRValueType getCanonicalAddressType(int byteSize) { + // We just use `NullPointerType`, since it should be unique. + exists(NullPointerType type | + type.getSize() = byteSize and + result = TPRValueType(type) + ) +} + +/** + * Gets the `CppType` that is the canonical type for an `IRFunctionAddressType` with the specified + * `byteSize`. + */ +CppFunctionGLValueType getCanonicalFunctionAddressType(int byteSize) { + result.getByteSize() = byteSize +} + +/** + * Gets the `CppType` that is the canonical type for `IRErrorType`. + */ +CppPRValueType getCanonicalErrorType() { result = TPRValueType(any(ErroneousType type)) } + +/** + * Gets the `CppType` that is the canonical type for `IRUnknownType`. + */ +CppUnknownType getCanonicalUnknownType() { any() } + +/** + * Gets the `CppType` that is the canonical type for `IRVoidType`. + */ +CppPRValueType getCanonicalVoidType() { result = TPRValueType(any(VoidType type)) } + +/** + * Gets the `CppType` that is the canonical type for an `IROpaqueType` with the specified `tag` and + * `byteSize`. + */ +CppType getCanonicalOpaqueType(Type tag, int byteSize) { + isOpaqueType(tag) and + result = TPRValueType(tag.getUnspecifiedType()) and + getTypeSize(tag) = byteSize + or + tag instanceof UnknownType and result = getUnknownOpaqueType(byteSize) +} + +/** + * Gets a string that uniquely identifies an `IROpaqueType` tag. This may be different from the usual + * `toString()` of the tag in order to ensure uniqueness. + */ +string getOpaqueTagIdentityString(Type tag) { + hasOpaqueType(tag, _) and + result = getTypeIdentityString(tag) +} + +module LanguageTypeSanity { + query predicate missingCppType(Type type, string message) { + not exists(getTypeForPRValue(type)) and + exists(type.getSize()) and + // `ProxyClass`es have a size, but only appear in uninstantiated templates + not type instanceof ProxyClass and + message = "Type does not have an associated `CppType`." + } +} diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll index cda661bd216..613958a0d4c 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/IRCppLanguage.qll @@ -1,6 +1,13 @@ private import cpp as Cpp private import semmle.code.cpp.Print as Print private import IRUtilities +private import semmle.code.cpp.ir.implementation.IRType +private import semmle.code.cpp.ir.implementation.raw.internal.IRConstruction as IRConstruction +import CppType + +class LanguageType = CppType; + +class OpaqueTypeTag = Cpp::Type; class Function = Cpp::Function; diff --git a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll index 50fe6ab9a55..b663f336d0a 100644 --- a/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll +++ b/cpp/ql/src/semmle/code/cpp/rangeanalysis/RangeAnalysis.qll @@ -80,7 +80,7 @@ private module RangeAnalysisCache { cached module RangeAnalysisPublic { /** - * Holds if `b + delta` is a valid bound for `i`. + * Holds if `b + delta` is a valid bound for `i` and this is the best such delta. * - `upper = true` : `i <= b + delta` * - `upper = false` : `i >= b + delta` * @@ -90,11 +90,12 @@ private module RangeAnalysisCache { */ cached predicate boundedInstruction(Instruction i, Bound b, int delta, boolean upper, Reason reason) { - boundedInstruction(i, b, delta, upper, _, _, reason) + boundedInstruction(i, b, delta, upper, _, _, reason) and + bestInstructionBound(i, b, delta, upper) } /** - * Holds if `b + delta` is a valid bound for `op`. + * Holds if `b + delta` is a valid bound for `op` and this is the best such delta. * - `upper = true` : `op <= b + delta` * - `upper = false` : `op >= b + delta` * @@ -104,9 +105,8 @@ private module RangeAnalysisCache { */ cached predicate boundedOperand(Operand op, Bound b, int delta, boolean upper, Reason reason) { - boundedNonPhiOperand(op, b, delta, upper, _, _, reason) - or - boundedPhiOperand(op, b, delta, upper, _, _, reason) + boundedOperandCand(op, b, delta, upper, reason) and + bestOperandBound(op, b, delta, upper) } } @@ -124,6 +124,43 @@ private module RangeAnalysisCache { private import RangeAnalysisCache import RangeAnalysisPublic +/** + * Holds if `b + delta` is a valid bound for `e` and this is the best such delta. + * - `upper = true` : `e <= b + delta` + * - `upper = false` : `e >= b + delta` + */ +private predicate bestInstructionBound(Instruction i, Bound b, int delta, boolean upper) { + delta = min(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = true + or + delta = max(int d | boundedInstruction(i, b, d, upper, _, _, _)) and upper = false +} + +/** + * Holds if `b + delta` is a valid bound for `op`. + * - `upper = true` : `op <= b + delta` + * - `upper = false` : `op >= b + delta` + * + * The reason for the bound is given by `reason` and may be either a condition + * or `NoReason` if the bound was proven directly without the use of a bounding + * condition. + */ +private predicate boundedOperandCand(Operand op, Bound b, int delta, boolean upper, Reason reason) { + boundedNonPhiOperand(op, b, delta, upper, _, _, reason) + or + boundedPhiOperand(op, b, delta, upper, _, _, reason) +} + +/** + * Holds if `b + delta` is a valid bound for `op` and this is the best such delta. + * - `upper = true` : `op <= b + delta` + * - `upper = false` : `op >= b + delta` + */ +private predicate bestOperandBound(Operand op, Bound b, int delta, boolean upper) { + delta = min(int d | boundedOperandCand(op, b, d, upper, _)) and upper = true + or + delta = max(int d | boundedOperandCand(op, b, d, upper, _)) and upper = false +} + /** * Gets a condition that tests whether `vn` equals `bound + delta`. * diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp new file mode 100644 index 00000000000..9c3c8bc4569 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/BarrierGuard.cpp @@ -0,0 +1,68 @@ +int source(); +void sink(int); +bool guarded(int); + +void bg_basic(int source) { + if (guarded(source)) { + sink(source); // no flow + } else { + sink(source); // flow + } +} + +void bg_not(int source) { + if (!guarded(source)) { + sink(source); // flow + } else { + sink(source); // no flow + } +} + +void bg_and(int source, bool arbitrary) { + if (guarded(source) && arbitrary) { + sink(source); // no flow + } else { + sink(source); // flow + } +} + +void bg_or(int source, bool arbitrary) { + if (guarded(source) || arbitrary) { + sink(source); // flow + } else { + sink(source); // flow + } +} + +void bg_return(int source) { + if (!guarded(source)) { + return; + } + sink(source); // no flow +} + +struct XY { + int x, y; +}; + +void bg_stackstruct(XY s1, XY s2) { + s1.x = source(); + if (guarded(s1.x)) { + sink(s1.x); // no flow + } else if (guarded(s1.y)) { + sink(s1.x); // flow + } else if (guarded(s2.y)) { + sink(s1.x); // flow + } +} + +void bg_structptr(XY *p1, XY *p2) { + p1->x = source(); + if (guarded(p1->x)) { + sink(p1->x); // no flow [FALSE POSITIVE in AST] + } else if (guarded(p1->y)) { + sink(p1->x); // flow [NOT DETECTED in IR] + } else if (guarded(p2->x)) { + sink(p1->x); // flow [NOT DETECTED in IR] + } +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll b/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll index 02ee4d45380..a81394640ee 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/DataflowTestCommon.qll @@ -1,6 +1,20 @@ import cpp import semmle.code.cpp.dataflow.DataFlow +/** + * A `BarrierGuard` that stops flow to all occurrences of `x` within statement + * S in `if (guarded(x)) S`. + */ +// This is tested in `BarrierGuard.cpp`. +class TestBarrierGuard extends DataFlow::BarrierGuard { + TestBarrierGuard() { this.(FunctionCall).getTarget().getName() = "guarded" } + + override predicate checks(Expr checked, boolean isTrue) { + checked = this.(FunctionCall).getArgument(0) and + isTrue = true + } +} + /** Common data flow configuration to be used by tests. */ class TestAllocationConfig extends DataFlow::Configuration { TestAllocationConfig() { this = "TestAllocationConfig" } @@ -26,4 +40,6 @@ class TestAllocationConfig extends DataFlow::Configuration { override predicate isBarrier(DataFlow::Node barrier) { barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") } + + override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard } } diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll b/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll index eb5fa14e2e0..490f7e4290a 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/IRDataflowTestCommon.qll @@ -1,5 +1,20 @@ import cpp import semmle.code.cpp.ir.dataflow.DataFlow +import semmle.code.cpp.ir.IR + +/** + * A `BarrierGuard` that stops flow to all occurrences of `x` within statement + * S in `if (guarded(x)) S`. + */ +// This is tested in `BarrierGuard.cpp`. +class TestBarrierGuard extends DataFlow::BarrierGuard { + TestBarrierGuard() { this.(CallInstruction).getStaticCallTarget().getName() = "guarded" } + + override predicate checks(Instruction checked, boolean isTrue) { + checked = this.(CallInstruction).getPositionalArgument(0) and + isTrue = true + } +} /** Common data flow configuration to be used by tests. */ class TestAllocationConfig extends DataFlow::Configuration { @@ -24,4 +39,6 @@ class TestAllocationConfig extends DataFlow::Configuration { override predicate isBarrier(DataFlow::Node barrier) { barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") } + + override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard } } diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected index b9fca1f678f..6527db4b77e 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.expected @@ -1,3 +1,13 @@ +| BarrierGuard.cpp:9:10:9:15 | source | BarrierGuard.cpp:5:19:5:24 | source | +| BarrierGuard.cpp:15:10:15:15 | source | BarrierGuard.cpp:13:17:13:22 | source | +| BarrierGuard.cpp:25:10:25:15 | source | BarrierGuard.cpp:21:17:21:22 | source | +| BarrierGuard.cpp:31:10:31:15 | source | BarrierGuard.cpp:29:16:29:21 | source | +| BarrierGuard.cpp:33:10:33:15 | source | BarrierGuard.cpp:29:16:29:21 | source | +| BarrierGuard.cpp:53:13:53:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source | +| BarrierGuard.cpp:55:13:55:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source | +| BarrierGuard.cpp:62:14:62:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source | +| BarrierGuard.cpp:64:14:64:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source | +| BarrierGuard.cpp:66:14:66:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source | | acrossLinkTargets.cpp:12:8:12:8 | x | acrossLinkTargets.cpp:19:27:19:32 | call to source | | clang.cpp:18:8:18:19 | sourceArray1 | clang.cpp:12:9:12:20 | sourceArray1 | | clang.cpp:22:8:22:20 | & ... | clang.cpp:12:9:12:20 | sourceArray1 | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected index cd7e3dac785..7d0d4e7d72e 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_diff.expected @@ -1,3 +1,6 @@ +| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:62:14:62:14 | AST only | +| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:64:14:64:14 | AST only | +| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:66:14:66:14 | AST only | | clang.cpp:12:9:12:20 | clang.cpp:22:8:22:20 | AST only | | clang.cpp:28:27:28:32 | clang.cpp:29:27:29:28 | AST only | | clang.cpp:28:27:28:32 | clang.cpp:30:27:30:34 | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected index 83ba546480f..9b67a013a58 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test_ir.expected @@ -1,3 +1,10 @@ +| BarrierGuard.cpp:9:10:9:15 | Load: source | BarrierGuard.cpp:5:19:5:24 | InitializeParameter: source | +| BarrierGuard.cpp:15:10:15:15 | Load: source | BarrierGuard.cpp:13:17:13:22 | InitializeParameter: source | +| BarrierGuard.cpp:25:10:25:15 | Load: source | BarrierGuard.cpp:21:17:21:22 | InitializeParameter: source | +| BarrierGuard.cpp:31:10:31:15 | Load: source | BarrierGuard.cpp:29:16:29:21 | InitializeParameter: source | +| BarrierGuard.cpp:33:10:33:15 | Load: source | BarrierGuard.cpp:29:16:29:21 | InitializeParameter: source | +| BarrierGuard.cpp:53:13:53:13 | Load: x | BarrierGuard.cpp:49:10:49:15 | Call: call to source | +| BarrierGuard.cpp:55:13:55:13 | Load: x | BarrierGuard.cpp:49:10:49:15 | Call: call to source | | acrossLinkTargets.cpp:12:8:12:8 | Convert: (int)... | acrossLinkTargets.cpp:19:27:19:32 | Call: call to source | | acrossLinkTargets.cpp:12:8:12:8 | Load: x | acrossLinkTargets.cpp:19:27:19:32 | Call: call to source | | clang.cpp:18:8:18:19 | Convert: (const int *)... | clang.cpp:12:9:12:20 | InitializeParameter: sourceArray1 | diff --git a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected index 5de69a800bc..19a4288a4c4 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintAST.expected +++ b/cpp/ql/test/library-tests/ir/ir/PrintAST.expected @@ -7795,16 +7795,16 @@ ir.cpp: # 1101| 0: [VariableAccess] x # 1101| Type = [IntType] int # 1101| ValueCategory = prvalue(load) -# 1104| [TopLevelFunction] void AsmStmtWithOutputs(unsigned int&, unsigned int&, unsigned int&, unsigned int&) +# 1104| [TopLevelFunction] void AsmStmtWithOutputs(unsigned int&, unsigned int, unsigned int&, unsigned int) # 1104| params: # 1104| 0: [Parameter] a # 1104| Type = [LValueReferenceType] unsigned int & # 1104| 1: [Parameter] b -# 1104| Type = [LValueReferenceType] unsigned int & +# 1104| Type = [IntType] unsigned int # 1104| 2: [Parameter] c # 1104| Type = [LValueReferenceType] unsigned int & # 1104| 3: [Parameter] d -# 1104| Type = [LValueReferenceType] unsigned int & +# 1104| Type = [IntType] unsigned int # 1105| body: [Block] { ... } # 1106| 0: [AsmStmt] asm statement # 1109| 0: [ReferenceDereferenceExpr] (reference dereference) @@ -7813,24 +7813,18 @@ ir.cpp: # 1109| expr: [VariableAccess] a # 1109| Type = [LValueReferenceType] unsigned int & # 1109| ValueCategory = prvalue(load) -# 1109| 1: [ReferenceDereferenceExpr] (reference dereference) +# 1109| 1: [VariableAccess] b # 1109| Type = [IntType] unsigned int # 1109| ValueCategory = lvalue -# 1109| expr: [VariableAccess] b -# 1109| Type = [LValueReferenceType] unsigned int & -# 1109| ValueCategory = prvalue(load) # 1109| 2: [ReferenceDereferenceExpr] (reference dereference) # 1109| Type = [IntType] unsigned int -# 1109| ValueCategory = lvalue +# 1109| ValueCategory = prvalue(load) # 1109| expr: [VariableAccess] c # 1109| Type = [LValueReferenceType] unsigned int & # 1109| ValueCategory = prvalue(load) -# 1109| 3: [ReferenceDereferenceExpr] (reference dereference) +# 1109| 3: [VariableAccess] d # 1109| Type = [IntType] unsigned int -# 1109| ValueCategory = lvalue -# 1109| expr: [VariableAccess] d -# 1109| Type = [LValueReferenceType] unsigned int & -# 1109| ValueCategory = prvalue(load) +# 1109| ValueCategory = prvalue(load) # 1111| 1: [ReturnStmt] return ... # 1113| [TopLevelFunction] void ExternDeclarations() # 1113| params: diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected index 012ff53039b..928f535f9d8 100644 --- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_sanity.expected @@ -14,3 +14,8 @@ containsLoopOfForwardEdges lostReachability backEdgeCountMismatch useNotDominatedByDefinition +missingCanonicalLanguageType +multipleCanonicalLanguageTypes +missingIRType +multipleIRTypes +missingCppType diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp index 08748ecb8d2..7ebd2e22aee 100644 --- a/cpp/ql/test/library-tests/ir/ir/ir.cpp +++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp @@ -1101,12 +1101,12 @@ int AsmStmt(int x) { return x; } -static void AsmStmtWithOutputs(unsigned int& a, unsigned int& b, unsigned int& c, unsigned int& d) +static void AsmStmtWithOutputs(unsigned int& a, unsigned int b, unsigned int& c, unsigned int d) { __asm__ __volatile__ ( "cpuid\n\t" - : "+a" (a), "+b" (b), "+c" (c), "+d" (d) + : "+a" (a), "+b" (b) : "c" (c), "d" (d) ); } diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected index d7c144f7181..0827819e3cf 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -14,12 +14,13 @@ bad_asts.cpp: # 16| r0_10(int) = Constant[1] : # 16| r0_11(int) = Call : func:r0_9, this:r0_8, 0:r0_10 # 16| mu0_12(unknown) = ^CallSideEffect : ~mu0_2 -# 16| v0_13(void) = ^IndirectReadSideEffect[-1] : &:r0_8, ~mu0_2 +# 16| v0_13(void) = ^BufferReadSideEffect[-1] : &:r0_8, ~mu0_2 # 16| mu0_14(S) = ^IndirectMayWriteSideEffect[-1] : &:r0_8 # 17| v0_15(void) = NoOp : # 14| v0_16(void) = ReturnVoid : # 14| v0_17(void) = UnmodeledUse : mu* -# 14| v0_18(void) = ExitFunction : +# 14| v0_18(void) = AliasedUse : ~mu0_2 +# 14| v0_19(void) = ExitFunction : # 22| void Bad::Point::Point() # 22| Block 0 @@ -30,7 +31,8 @@ bad_asts.cpp: # 23| v0_4(void) = NoOp : # 22| v0_5(void) = ReturnVoid : # 22| v0_6(void) = UnmodeledUse : mu* -# 22| v0_7(void) = ExitFunction : +# 22| v0_7(void) = AliasedUse : ~mu0_2 +# 22| v0_8(void) = ExitFunction : # 26| void Bad::CallCopyConstructor(Bad::Point const&) # 26| Block 0 @@ -49,7 +51,8 @@ bad_asts.cpp: # 28| v0_12(void) = NoOp : # 26| v0_13(void) = ReturnVoid : # 26| v0_14(void) = UnmodeledUse : mu* -# 26| v0_15(void) = ExitFunction : +# 26| v0_15(void) = AliasedUse : ~mu0_2 +# 26| v0_16(void) = ExitFunction : # 30| void Bad::errorExpr() # 30| Block 0 @@ -69,7 +72,8 @@ bad_asts.cpp: # 34| v0_13(void) = NoOp : # 30| v0_14(void) = ReturnVoid : # 30| v0_15(void) = UnmodeledUse : mu* -# 30| v0_16(void) = ExitFunction : +# 30| v0_16(void) = AliasedUse : ~mu0_2 +# 30| v0_17(void) = ExitFunction : clang.cpp: # 5| int* globalIntAddress() @@ -84,7 +88,8 @@ clang.cpp: # 5| r0_7(glval) = VariableAddress[#return] : # 5| v0_8(void) = ReturnValue : &:r0_7, ~mu0_2 # 5| v0_9(void) = UnmodeledUse : mu* -# 5| v0_10(void) = ExitFunction : +# 5| v0_10(void) = AliasedUse : ~mu0_2 +# 5| v0_11(void) = ExitFunction : ir.cpp: # 1| void Constants() @@ -179,7 +184,8 @@ ir.cpp: # 41| v0_87(void) = NoOp : # 1| v0_88(void) = ReturnVoid : # 1| v0_89(void) = UnmodeledUse : mu* -# 1| v0_90(void) = ExitFunction : +# 1| v0_90(void) = AliasedUse : ~mu0_2 +# 1| v0_91(void) = ExitFunction : # 43| void Foo() # 43| Block 0 @@ -212,7 +218,8 @@ ir.cpp: # 48| v0_26(void) = NoOp : # 43| v0_27(void) = ReturnVoid : # 43| v0_28(void) = UnmodeledUse : mu* -# 43| v0_29(void) = ExitFunction : +# 43| v0_29(void) = AliasedUse : ~mu0_2 +# 43| v0_30(void) = ExitFunction : # 50| void IntegerOps(int, int) # 50| Block 0 @@ -385,7 +392,8 @@ ir.cpp: # 85| v0_166(void) = NoOp : # 50| v0_167(void) = ReturnVoid : # 50| v0_168(void) = UnmodeledUse : mu* -# 50| v0_169(void) = ExitFunction : +# 50| v0_169(void) = AliasedUse : ~mu0_2 +# 50| v0_170(void) = ExitFunction : # 87| void IntegerCompare(int, int) # 87| Block 0 @@ -443,7 +451,8 @@ ir.cpp: # 96| v0_51(void) = NoOp : # 87| v0_52(void) = ReturnVoid : # 87| v0_53(void) = UnmodeledUse : mu* -# 87| v0_54(void) = ExitFunction : +# 87| v0_54(void) = AliasedUse : ~mu0_2 +# 87| v0_55(void) = ExitFunction : # 98| void IntegerCrement(int) # 98| Block 0 @@ -485,7 +494,8 @@ ir.cpp: # 105| v0_35(void) = NoOp : # 98| v0_36(void) = ReturnVoid : # 98| v0_37(void) = UnmodeledUse : mu* -# 98| v0_38(void) = ExitFunction : +# 98| v0_38(void) = AliasedUse : ~mu0_2 +# 98| v0_39(void) = ExitFunction : # 107| void IntegerCrement_LValue(int) # 107| Block 0 @@ -517,7 +527,8 @@ ir.cpp: # 112| v0_25(void) = NoOp : # 107| v0_26(void) = ReturnVoid : # 107| v0_27(void) = UnmodeledUse : mu* -# 107| v0_28(void) = ExitFunction : +# 107| v0_28(void) = AliasedUse : ~mu0_2 +# 107| v0_29(void) = ExitFunction : # 114| void FloatOps(double, double) # 114| Block 0 @@ -599,7 +610,8 @@ ir.cpp: # 131| v0_75(void) = NoOp : # 114| v0_76(void) = ReturnVoid : # 114| v0_77(void) = UnmodeledUse : mu* -# 114| v0_78(void) = ExitFunction : +# 114| v0_78(void) = AliasedUse : ~mu0_2 +# 114| v0_79(void) = ExitFunction : # 133| void FloatCompare(double, double) # 133| Block 0 @@ -657,7 +669,8 @@ ir.cpp: # 142| v0_51(void) = NoOp : # 133| v0_52(void) = ReturnVoid : # 133| v0_53(void) = UnmodeledUse : mu* -# 133| v0_54(void) = ExitFunction : +# 133| v0_54(void) = AliasedUse : ~mu0_2 +# 133| v0_55(void) = ExitFunction : # 144| void FloatCrement(float) # 144| Block 0 @@ -699,7 +712,8 @@ ir.cpp: # 151| v0_35(void) = NoOp : # 144| v0_36(void) = ReturnVoid : # 144| v0_37(void) = UnmodeledUse : mu* -# 144| v0_38(void) = ExitFunction : +# 144| v0_38(void) = AliasedUse : ~mu0_2 +# 144| v0_39(void) = ExitFunction : # 153| void PointerOps(int*, int) # 153| Block 0 @@ -775,7 +789,8 @@ ir.cpp: # 169| v0_69(void) = NoOp : # 153| v0_70(void) = ReturnVoid : # 153| v0_71(void) = UnmodeledUse : mu* -# 153| v0_72(void) = ExitFunction : +# 153| v0_72(void) = AliasedUse : ~mu0_2 +# 153| v0_73(void) = ExitFunction : # 171| void ArrayAccess(int*, int) # 171| Block 0 @@ -857,7 +872,8 @@ ir.cpp: # 185| v0_75(void) = NoOp : # 171| v0_76(void) = ReturnVoid : # 171| v0_77(void) = UnmodeledUse : mu* -# 171| v0_78(void) = ExitFunction : +# 171| v0_78(void) = AliasedUse : ~mu0_2 +# 171| v0_79(void) = ExitFunction : # 187| void StringLiteral(int) # 187| Block 0 @@ -890,7 +906,8 @@ ir.cpp: # 191| v0_26(void) = NoOp : # 187| v0_27(void) = ReturnVoid : # 187| v0_28(void) = UnmodeledUse : mu* -# 187| v0_29(void) = ExitFunction : +# 187| v0_29(void) = AliasedUse : ~mu0_2 +# 187| v0_30(void) = ExitFunction : # 193| void PointerCompare(int*, int*) # 193| Block 0 @@ -948,7 +965,8 @@ ir.cpp: # 202| v0_51(void) = NoOp : # 193| v0_52(void) = ReturnVoid : # 193| v0_53(void) = UnmodeledUse : mu* -# 193| v0_54(void) = ExitFunction : +# 193| v0_54(void) = AliasedUse : ~mu0_2 +# 193| v0_55(void) = ExitFunction : # 204| void PointerCrement(int*) # 204| Block 0 @@ -990,7 +1008,8 @@ ir.cpp: # 211| v0_35(void) = NoOp : # 204| v0_36(void) = ReturnVoid : # 204| v0_37(void) = UnmodeledUse : mu* -# 204| v0_38(void) = ExitFunction : +# 204| v0_38(void) = AliasedUse : ~mu0_2 +# 204| v0_39(void) = ExitFunction : # 213| void CompoundAssignment() # 213| Block 0 @@ -1034,7 +1053,8 @@ ir.cpp: # 228| v0_37(void) = NoOp : # 213| v0_38(void) = ReturnVoid : # 213| v0_39(void) = UnmodeledUse : mu* -# 213| v0_40(void) = ExitFunction : +# 213| v0_40(void) = AliasedUse : ~mu0_2 +# 213| v0_41(void) = ExitFunction : # 230| void UninitializedVariables() # 230| Block 0 @@ -1050,7 +1070,8 @@ ir.cpp: # 233| v0_9(void) = NoOp : # 230| v0_10(void) = ReturnVoid : # 230| v0_11(void) = UnmodeledUse : mu* -# 230| v0_12(void) = ExitFunction : +# 230| v0_12(void) = AliasedUse : ~mu0_2 +# 230| v0_13(void) = ExitFunction : # 235| int Parameters(int, int) # 235| Block 0 @@ -1071,7 +1092,8 @@ ir.cpp: # 235| r0_14(glval) = VariableAddress[#return] : # 235| v0_15(void) = ReturnValue : &:r0_14, ~mu0_2 # 235| v0_16(void) = UnmodeledUse : mu* -# 235| v0_17(void) = ExitFunction : +# 235| v0_17(void) = AliasedUse : ~mu0_2 +# 235| v0_18(void) = ExitFunction : # 239| void IfStatements(bool, int, int) # 239| Block 0 @@ -1129,7 +1151,8 @@ ir.cpp: # 251| v6_0(void) = NoOp : # 239| v6_1(void) = ReturnVoid : # 239| v6_2(void) = UnmodeledUse : mu* -# 239| v6_3(void) = ExitFunction : +# 239| v6_3(void) = AliasedUse : ~mu0_2 +# 239| v6_4(void) = ExitFunction : # 240| Block 7 # 240| v7_0(void) = NoOp : @@ -1156,7 +1179,8 @@ ir.cpp: # 257| v2_0(void) = NoOp : # 253| v2_1(void) = ReturnVoid : # 253| v2_2(void) = UnmodeledUse : mu* -# 253| v2_3(void) = ExitFunction : +# 253| v2_3(void) = AliasedUse : ~mu0_2 +# 253| v2_4(void) = ExitFunction : # 254| Block 3 # 254| r3_0(glval) = VariableAddress[n] : @@ -1194,7 +1218,8 @@ ir.cpp: # 263| v2_0(void) = NoOp : # 259| v2_1(void) = ReturnVoid : # 259| v2_2(void) = UnmodeledUse : mu* -# 259| v2_3(void) = ExitFunction : +# 259| v2_3(void) = AliasedUse : ~mu0_2 +# 259| v2_4(void) = ExitFunction : # 265| void For_Empty() # 265| Block 0 @@ -1208,7 +1233,8 @@ ir.cpp: # 265| Block 1 # 265| v1_0(void) = ReturnVoid : # 265| v1_1(void) = UnmodeledUse : mu* -# 265| v1_2(void) = ExitFunction : +# 265| v1_2(void) = AliasedUse : ~mu0_2 +# 265| v1_3(void) = ExitFunction : # 268| Block 2 # 268| v2_0(void) = NoOp : @@ -1227,7 +1253,8 @@ ir.cpp: # 272| Block 1 # 272| v1_0(void) = ReturnVoid : # 272| v1_1(void) = UnmodeledUse : mu* -# 272| v1_2(void) = ExitFunction : +# 272| v1_2(void) = AliasedUse : ~mu0_2 +# 272| v1_3(void) = ExitFunction : # 274| Block 2 # 274| v2_0(void) = NoOp : @@ -1260,7 +1287,8 @@ ir.cpp: # 283| v3_0(void) = NoOp : # 278| v3_1(void) = ReturnVoid : # 278| v3_2(void) = UnmodeledUse : mu* -# 278| v3_3(void) = ExitFunction : +# 278| v3_3(void) = AliasedUse : ~mu0_2 +# 278| v3_4(void) = ExitFunction : # 285| void For_Update() # 285| Block 0 @@ -1275,7 +1303,8 @@ ir.cpp: # 285| Block 1 # 285| v1_0(void) = ReturnVoid : # 285| v1_1(void) = UnmodeledUse : mu* -# 285| v1_2(void) = ExitFunction : +# 285| v1_2(void) = AliasedUse : ~mu0_2 +# 285| v1_3(void) = ExitFunction : # 288| Block 2 # 288| v2_0(void) = NoOp : @@ -1313,7 +1342,8 @@ ir.cpp: # 296| v3_0(void) = NoOp : # 292| v3_1(void) = ReturnVoid : # 292| v3_2(void) = UnmodeledUse : mu* -# 292| v3_3(void) = ExitFunction : +# 292| v3_3(void) = AliasedUse : ~mu0_2 +# 292| v3_4(void) = ExitFunction : # 298| void For_InitUpdate() # 298| Block 0 @@ -1328,7 +1358,8 @@ ir.cpp: # 298| Block 1 # 298| v1_0(void) = ReturnVoid : # 298| v1_1(void) = UnmodeledUse : mu* -# 298| v1_2(void) = ExitFunction : +# 298| v1_2(void) = AliasedUse : ~mu0_2 +# 298| v1_3(void) = ExitFunction : # 300| Block 2 # 300| v2_0(void) = NoOp : @@ -1371,7 +1402,8 @@ ir.cpp: # 309| v3_0(void) = NoOp : # 304| v3_1(void) = ReturnVoid : # 304| v3_2(void) = UnmodeledUse : mu* -# 304| v3_3(void) = ExitFunction : +# 304| v3_3(void) = AliasedUse : ~mu0_2 +# 304| v3_4(void) = ExitFunction : # 311| void For_InitConditionUpdate() # 311| Block 0 @@ -1405,7 +1437,8 @@ ir.cpp: # 315| v3_0(void) = NoOp : # 311| v3_1(void) = ReturnVoid : # 311| v3_2(void) = UnmodeledUse : mu* -# 311| v3_3(void) = ExitFunction : +# 311| v3_3(void) = AliasedUse : ~mu0_2 +# 311| v3_4(void) = ExitFunction : # 317| void For_Break() # 317| Block 0 @@ -1452,7 +1485,8 @@ ir.cpp: # 323| v5_1(void) = NoOp : # 317| v5_2(void) = ReturnVoid : # 317| v5_3(void) = UnmodeledUse : mu* -# 317| v5_4(void) = ExitFunction : +# 317| v5_4(void) = AliasedUse : ~mu0_2 +# 317| v5_5(void) = ExitFunction : # 325| void For_Continue_Update() # 325| Block 0 @@ -1499,7 +1533,8 @@ ir.cpp: # 331| v5_0(void) = NoOp : # 325| v5_1(void) = ReturnVoid : # 325| v5_2(void) = UnmodeledUse : mu* -# 325| v5_3(void) = ExitFunction : +# 325| v5_3(void) = AliasedUse : ~mu0_2 +# 325| v5_4(void) = ExitFunction : # 333| void For_Continue_NoUpdate() # 333| Block 0 @@ -1541,7 +1576,8 @@ ir.cpp: # 339| v5_0(void) = NoOp : # 333| v5_1(void) = ReturnVoid : # 333| v5_2(void) = UnmodeledUse : mu* -# 333| v5_3(void) = ExitFunction : +# 333| v5_3(void) = AliasedUse : ~mu0_2 +# 333| v5_4(void) = ExitFunction : # 341| int Dereference(int*) # 341| Block 0 @@ -1563,7 +1599,8 @@ ir.cpp: # 341| r0_15(glval) = VariableAddress[#return] : # 341| v0_16(void) = ReturnValue : &:r0_15, ~mu0_2 # 341| v0_17(void) = UnmodeledUse : mu* -# 341| v0_18(void) = ExitFunction : +# 341| v0_18(void) = AliasedUse : ~mu0_2 +# 341| v0_19(void) = ExitFunction : # 348| int* AddressOf() # 348| Block 0 @@ -1577,7 +1614,8 @@ ir.cpp: # 348| r0_7(glval) = VariableAddress[#return] : # 348| v0_8(void) = ReturnValue : &:r0_7, ~mu0_2 # 348| v0_9(void) = UnmodeledUse : mu* -# 348| v0_10(void) = ExitFunction : +# 348| v0_10(void) = AliasedUse : ~mu0_2 +# 348| v0_11(void) = ExitFunction : # 352| void Break(int) # 352| Block 0 @@ -1614,7 +1652,8 @@ ir.cpp: # 358| v4_1(void) = NoOp : # 352| v4_2(void) = ReturnVoid : # 352| v4_3(void) = UnmodeledUse : mu* -# 352| v4_4(void) = ExitFunction : +# 352| v4_4(void) = AliasedUse : ~mu0_2 +# 352| v4_5(void) = ExitFunction : # 353| Block 5 # 353| r5_0(glval) = VariableAddress[n] : @@ -1669,7 +1708,8 @@ ir.cpp: # 367| v5_0(void) = NoOp : # 360| v5_1(void) = ReturnVoid : # 360| v5_2(void) = UnmodeledUse : mu* -# 360| v5_3(void) = ExitFunction : +# 360| v5_3(void) = AliasedUse : ~mu0_2 +# 360| v5_4(void) = ExitFunction : # 372| void Call() # 372| Block 0 @@ -1682,7 +1722,8 @@ ir.cpp: # 374| v0_6(void) = NoOp : # 372| v0_7(void) = ReturnVoid : # 372| v0_8(void) = UnmodeledUse : mu* -# 372| v0_9(void) = ExitFunction : +# 372| v0_9(void) = AliasedUse : ~mu0_2 +# 372| v0_10(void) = ExitFunction : # 376| int CallAdd(int, int) # 376| Block 0 @@ -1705,7 +1746,8 @@ ir.cpp: # 376| r0_16(glval) = VariableAddress[#return] : # 376| v0_17(void) = ReturnValue : &:r0_16, ~mu0_2 # 376| v0_18(void) = UnmodeledUse : mu* -# 376| v0_19(void) = ExitFunction : +# 376| v0_19(void) = AliasedUse : ~mu0_2 +# 376| v0_20(void) = ExitFunction : # 380| int Comma(int, int) # 380| Block 0 @@ -1732,7 +1774,8 @@ ir.cpp: # 380| r0_20(glval) = VariableAddress[#return] : # 380| v0_21(void) = ReturnValue : &:r0_20, ~mu0_2 # 380| v0_22(void) = UnmodeledUse : mu* -# 380| v0_23(void) = ExitFunction : +# 380| v0_23(void) = AliasedUse : ~mu0_2 +# 380| v0_24(void) = ExitFunction : # 384| void Switch(int) # 384| Block 0 @@ -1813,7 +1856,8 @@ ir.cpp: # 410| v9_1(void) = NoOp : # 384| v9_2(void) = ReturnVoid : # 384| v9_3(void) = UnmodeledUse : mu* -# 384| v9_4(void) = ExitFunction : +# 384| v9_4(void) = AliasedUse : ~mu0_2 +# 384| v9_5(void) = ExitFunction : # 422| Point ReturnStruct(Point) # 422| Block 0 @@ -1829,7 +1873,8 @@ ir.cpp: # 422| r0_9(glval) = VariableAddress[#return] : # 422| v0_10(void) = ReturnValue : &:r0_9, ~mu0_2 # 422| v0_11(void) = UnmodeledUse : mu* -# 422| v0_12(void) = ExitFunction : +# 422| v0_12(void) = AliasedUse : ~mu0_2 +# 422| v0_13(void) = ExitFunction : # 426| void FieldAccess() # 426| Block 0 @@ -1856,7 +1901,8 @@ ir.cpp: # 431| v0_20(void) = NoOp : # 426| v0_21(void) = ReturnVoid : # 426| v0_22(void) = UnmodeledUse : mu* -# 426| v0_23(void) = ExitFunction : +# 426| v0_23(void) = AliasedUse : ~mu0_2 +# 426| v0_24(void) = ExitFunction : # 433| void LogicalOr(bool, bool) # 433| Block 0 @@ -1918,7 +1964,8 @@ ir.cpp: # 445| v7_0(void) = NoOp : # 433| v7_1(void) = ReturnVoid : # 433| v7_2(void) = UnmodeledUse : mu* -# 433| v7_3(void) = ExitFunction : +# 433| v7_3(void) = AliasedUse : ~mu0_2 +# 433| v7_4(void) = ExitFunction : # 447| void LogicalAnd(bool, bool) # 447| Block 0 @@ -1980,7 +2027,8 @@ ir.cpp: # 459| v7_0(void) = NoOp : # 447| v7_1(void) = ReturnVoid : # 447| v7_2(void) = UnmodeledUse : mu* -# 447| v7_3(void) = ExitFunction : +# 447| v7_3(void) = AliasedUse : ~mu0_2 +# 447| v7_4(void) = ExitFunction : # 461| void LogicalNot(bool, bool) # 461| Block 0 @@ -2035,7 +2083,8 @@ ir.cpp: # 473| v6_0(void) = NoOp : # 461| v6_1(void) = ReturnVoid : # 461| v6_2(void) = UnmodeledUse : mu* -# 461| v6_3(void) = ExitFunction : +# 461| v6_3(void) = AliasedUse : ~mu0_2 +# 461| v6_4(void) = ExitFunction : # 475| void ConditionValues(bool, bool) # 475| Block 0 @@ -2106,7 +2155,8 @@ ir.cpp: # 480| v7_5(void) = NoOp : # 475| v7_6(void) = ReturnVoid : # 475| v7_7(void) = UnmodeledUse : mu* -# 475| v7_8(void) = ExitFunction : +# 475| v7_8(void) = AliasedUse : ~mu0_2 +# 475| v7_9(void) = ExitFunction : # 479| Block 8 # 479| r8_0(glval) = VariableAddress[#temp479:11] : @@ -2183,7 +2233,8 @@ ir.cpp: # 484| v3_3(void) = NoOp : # 482| v3_4(void) = ReturnVoid : # 482| v3_5(void) = UnmodeledUse : mu* -# 482| v3_6(void) = ExitFunction : +# 482| v3_6(void) = AliasedUse : ~mu0_2 +# 482| v3_7(void) = ExitFunction : # 486| void Conditional_LValue(bool) # 486| Block 0 @@ -2204,24 +2255,25 @@ ir.cpp: #-----| True -> Block 2 # 489| Block 1 -# 489| r1_0(glval) = VariableAddress[#temp489:6] : -# 489| r1_1(glval) = Load : &:r1_0, ~mu0_2 -# 489| mu1_2(int) = Store : &:r1_1, r0_9 -# 490| v1_3(void) = NoOp : -# 486| v1_4(void) = ReturnVoid : -# 486| v1_5(void) = UnmodeledUse : mu* -# 486| v1_6(void) = ExitFunction : +# 489| r1_0(glval) = VariableAddress[#temp489:6] : +# 489| r1_1(glval) = Load : &:r1_0, ~mu0_2 +# 489| mu1_2(int) = Store : &:r1_1, r0_9 +# 490| v1_3(void) = NoOp : +# 486| v1_4(void) = ReturnVoid : +# 486| v1_5(void) = UnmodeledUse : mu* +# 486| v1_6(void) = AliasedUse : ~mu0_2 +# 486| v1_7(void) = ExitFunction : # 489| Block 2 -# 489| r2_0(glval) = VariableAddress[x] : -# 489| r2_1(glval) = VariableAddress[#temp489:6] : -# 489| mu2_2(int) = Store : &:r2_1, r2_0 +# 489| r2_0(glval) = VariableAddress[x] : +# 489| r2_1(glval) = VariableAddress[#temp489:6] : +# 489| mu2_2(glval) = Store : &:r2_1, r2_0 #-----| Goto -> Block 1 # 489| Block 3 -# 489| r3_0(glval) = VariableAddress[y] : -# 489| r3_1(glval) = VariableAddress[#temp489:6] : -# 489| mu3_2(int) = Store : &:r3_1, r3_0 +# 489| r3_0(glval) = VariableAddress[y] : +# 489| r3_1(glval) = VariableAddress[#temp489:6] : +# 489| mu3_2(glval) = Store : &:r3_1, r3_0 #-----| Goto -> Block 1 # 492| void Conditional_Void(bool) @@ -2241,7 +2293,8 @@ ir.cpp: # 494| v1_0(void) = NoOp : # 492| v1_1(void) = ReturnVoid : # 492| v1_2(void) = UnmodeledUse : mu* -# 492| v1_3(void) = ExitFunction : +# 492| v1_3(void) = AliasedUse : ~mu0_2 +# 492| v1_4(void) = ExitFunction : # 493| Block 2 # 493| r2_0(glval) = FunctionAddress[VoidFunc] : @@ -2275,7 +2328,8 @@ ir.cpp: # 501| v0_15(void) = NoOp : # 496| v0_16(void) = ReturnVoid : # 496| v0_17(void) = UnmodeledUse : mu* -# 496| v0_18(void) = ExitFunction : +# 496| v0_18(void) = AliasedUse : ~mu0_2 +# 496| v0_19(void) = ExitFunction : # 503| void InitList(int, float) # 503| Block 0 @@ -2323,7 +2377,8 @@ ir.cpp: # 510| v0_41(void) = NoOp : # 503| v0_42(void) = ReturnVoid : # 503| v0_43(void) = UnmodeledUse : mu* -# 503| v0_44(void) = ExitFunction : +# 503| v0_44(void) = AliasedUse : ~mu0_2 +# 503| v0_45(void) = ExitFunction : # 512| void NestedInitList(int, float) # 512| Block 0 @@ -2400,7 +2455,8 @@ ir.cpp: # 517| v0_70(void) = NoOp : # 512| v0_71(void) = ReturnVoid : # 512| v0_72(void) = UnmodeledUse : mu* -# 512| v0_73(void) = ExitFunction : +# 512| v0_73(void) = AliasedUse : ~mu0_2 +# 512| v0_74(void) = ExitFunction : # 519| void ArrayInit(int, float) # 519| Block 0 @@ -2448,7 +2504,8 @@ ir.cpp: # 523| v0_41(void) = NoOp : # 519| v0_42(void) = ReturnVoid : # 519| v0_43(void) = UnmodeledUse : mu* -# 519| v0_44(void) = ExitFunction : +# 519| v0_44(void) = AliasedUse : ~mu0_2 +# 519| v0_45(void) = ExitFunction : # 530| void UnionInit(int, float) # 530| Block 0 @@ -2469,7 +2526,8 @@ ir.cpp: # 533| v0_14(void) = NoOp : # 530| v0_15(void) = ReturnVoid : # 530| v0_16(void) = UnmodeledUse : mu* -# 530| v0_17(void) = ExitFunction : +# 530| v0_17(void) = AliasedUse : ~mu0_2 +# 530| v0_18(void) = ExitFunction : # 535| void EarlyReturn(int, int) # 535| Block 0 @@ -2492,7 +2550,8 @@ ir.cpp: # 535| Block 1 # 535| v1_0(void) = ReturnVoid : # 535| v1_1(void) = UnmodeledUse : mu* -# 535| v1_2(void) = ExitFunction : +# 535| v1_2(void) = AliasedUse : ~mu0_2 +# 535| v1_3(void) = ExitFunction : # 537| Block 2 # 537| v2_0(void) = NoOp : @@ -2528,7 +2587,8 @@ ir.cpp: # 543| r1_0(glval) = VariableAddress[#return] : # 543| v1_1(void) = ReturnValue : &:r1_0, ~mu0_2 # 543| v1_2(void) = UnmodeledUse : mu* -# 543| v1_3(void) = ExitFunction : +# 543| v1_3(void) = AliasedUse : ~mu0_2 +# 543| v1_4(void) = ExitFunction : # 545| Block 2 # 545| r2_0(glval) = VariableAddress[#return] : @@ -2564,7 +2624,8 @@ ir.cpp: # 551| r0_12(glval) = VariableAddress[#return] : # 551| v0_13(void) = ReturnValue : &:r0_12, ~mu0_2 # 551| v0_14(void) = UnmodeledUse : mu* -# 551| v0_15(void) = ExitFunction : +# 551| v0_15(void) = AliasedUse : ~mu0_2 +# 551| v0_16(void) = ExitFunction : # 560| int EnumSwitch(E) # 560| Block 0 @@ -2585,7 +2646,8 @@ ir.cpp: # 560| r1_0(glval) = VariableAddress[#return] : # 560| v1_1(void) = ReturnValue : &:r1_0, ~mu0_2 # 560| v1_2(void) = UnmodeledUse : mu* -# 560| v1_3(void) = ExitFunction : +# 560| v1_3(void) = AliasedUse : ~mu0_2 +# 560| v1_4(void) = ExitFunction : # 564| Block 2 # 564| v2_0(void) = NoOp : @@ -2670,7 +2732,8 @@ ir.cpp: # 580| v0_57(void) = NoOp : # 571| v0_58(void) = ReturnVoid : # 571| v0_59(void) = UnmodeledUse : mu* -# 571| v0_60(void) = ExitFunction : +# 571| v0_60(void) = AliasedUse : ~mu0_2 +# 571| v0_61(void) = ExitFunction : # 584| void VarArgs() # 584| Block 0 @@ -2685,14 +2748,15 @@ ir.cpp: # 585| r0_8(char *) = Convert : r0_7 # 585| v0_9(void) = Call : func:r0_3, 0:r0_5, 1:r0_6, 2:r0_8 # 585| mu0_10(unknown) = ^CallSideEffect : ~mu0_2 -# 585| v0_11(void) = ^IndirectReadSideEffect[0] : &:r0_5, ~mu0_2 -# 585| v0_12(void) = ^IndirectReadSideEffect[2] : &:r0_8, ~mu0_2 +# 585| v0_11(void) = ^BufferReadSideEffect[0] : &:r0_5, ~mu0_2 +# 585| v0_12(void) = ^BufferReadSideEffect[2] : &:r0_8, ~mu0_2 # 585| mu0_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_5 # 585| mu0_14(unknown) = ^BufferMayWriteSideEffect[2] : &:r0_8 # 586| v0_15(void) = NoOp : # 584| v0_16(void) = ReturnVoid : # 584| v0_17(void) = UnmodeledUse : mu* -# 584| v0_18(void) = ExitFunction : +# 584| v0_18(void) = AliasedUse : ~mu0_2 +# 584| v0_19(void) = ExitFunction : # 590| void SetFuncPtr() # 590| Block 0 @@ -2700,13 +2764,13 @@ ir.cpp: # 590| mu0_1(unknown) = AliasedDefinition : # 590| mu0_2(unknown) = UnmodeledDefinition : # 591| r0_3(glval<..(*)(..)>) = VariableAddress[pfn] : -# 591| r0_4(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : +# 591| r0_4(..(*)(..)) = FunctionAddress[FuncPtrTarget] : # 591| mu0_5(..(*)(..)) = Store : &:r0_3, r0_4 # 592| r0_6(glval<..()(..)>) = FunctionAddress[FuncPtrTarget] : # 592| r0_7(..(*)(..)) = CopyValue : r0_6 # 592| r0_8(glval<..(*)(..)>) = VariableAddress[pfn] : # 592| mu0_9(..(*)(..)) = Store : &:r0_8, r0_7 -# 593| r0_10(glval<..(*)(..)>) = FunctionAddress[FuncPtrTarget] : +# 593| r0_10(..(*)(..)) = FunctionAddress[FuncPtrTarget] : # 593| r0_11(..(*)(..)) = CopyValue : r0_10 # 593| r0_12(glval<..(*)(..)>) = VariableAddress[pfn] : # 593| mu0_13(..(*)(..)) = Store : &:r0_12, r0_11 @@ -2720,7 +2784,8 @@ ir.cpp: # 595| v0_21(void) = NoOp : # 590| v0_22(void) = ReturnVoid : # 590| v0_23(void) = UnmodeledUse : mu* -# 590| v0_24(void) = ExitFunction : +# 590| v0_24(void) = AliasedUse : ~mu0_2 +# 590| v0_25(void) = ExitFunction : # 615| void DeclareObject() # 615| Block 0 @@ -2737,7 +2802,7 @@ ir.cpp: # 617| r0_10(char *) = Convert : r0_9 # 617| v0_11(void) = Call : func:r0_8, this:r0_7, 0:r0_10 # 617| mu0_12(unknown) = ^CallSideEffect : ~mu0_2 -# 617| v0_13(void) = ^IndirectReadSideEffect[0] : &:r0_10, ~mu0_2 +# 617| v0_13(void) = ^BufferReadSideEffect[0] : &:r0_10, ~mu0_2 # 617| mu0_14(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_10 # 618| r0_15(glval) = VariableAddress[s3] : # 618| r0_16(glval) = FunctionAddress[ReturnObject] : @@ -2750,12 +2815,13 @@ ir.cpp: # 619| r0_23(char *) = Convert : r0_22 # 619| v0_24(void) = Call : func:r0_21, this:r0_20, 0:r0_23 # 619| mu0_25(unknown) = ^CallSideEffect : ~mu0_2 -# 619| v0_26(void) = ^IndirectReadSideEffect[0] : &:r0_23, ~mu0_2 +# 619| v0_26(void) = ^BufferReadSideEffect[0] : &:r0_23, ~mu0_2 # 619| mu0_27(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_23 # 620| v0_28(void) = NoOp : # 615| v0_29(void) = ReturnVoid : # 615| v0_30(void) = UnmodeledUse : mu* -# 615| v0_31(void) = ExitFunction : +# 615| v0_31(void) = AliasedUse : ~mu0_2 +# 615| v0_32(void) = ExitFunction : # 622| void CallMethods(String&, String*, String) # 622| Block 0 @@ -2775,7 +2841,7 @@ ir.cpp: # 623| r0_13(glval) = FunctionAddress[c_str] : # 623| r0_14(char *) = Call : func:r0_13, this:r0_12 # 623| mu0_15(unknown) = ^CallSideEffect : ~mu0_2 -# 623| v0_16(void) = ^IndirectReadSideEffect[-1] : &:r0_12, ~mu0_2 +# 623| v0_16(void) = ^BufferReadSideEffect[-1] : &:r0_12, ~mu0_2 # 623| mu0_17(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_12 # 624| r0_18(glval) = VariableAddress[p] : # 624| r0_19(String *) = Load : &:r0_18, ~mu0_2 @@ -2783,19 +2849,20 @@ ir.cpp: # 624| r0_21(glval) = FunctionAddress[c_str] : # 624| r0_22(char *) = Call : func:r0_21, this:r0_20 # 624| mu0_23(unknown) = ^CallSideEffect : ~mu0_2 -# 624| v0_24(void) = ^IndirectReadSideEffect[-1] : &:r0_20, ~mu0_2 +# 624| v0_24(void) = ^BufferReadSideEffect[-1] : &:r0_20, ~mu0_2 # 624| mu0_25(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_20 # 625| r0_26(glval) = VariableAddress[s] : # 625| r0_27(glval) = Convert : r0_26 # 625| r0_28(glval) = FunctionAddress[c_str] : # 625| r0_29(char *) = Call : func:r0_28, this:r0_27 # 625| mu0_30(unknown) = ^CallSideEffect : ~mu0_2 -# 625| v0_31(void) = ^IndirectReadSideEffect[-1] : &:r0_27, ~mu0_2 +# 625| v0_31(void) = ^BufferReadSideEffect[-1] : &:r0_27, ~mu0_2 # 625| mu0_32(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_27 # 626| v0_33(void) = NoOp : # 622| v0_34(void) = ReturnVoid : # 622| v0_35(void) = UnmodeledUse : mu* -# 622| v0_36(void) = ExitFunction : +# 622| v0_36(void) = AliasedUse : ~mu0_2 +# 622| v0_37(void) = ExitFunction : # 630| int C::StaticMemberFunction(int) # 630| Block 0 @@ -2811,7 +2878,8 @@ ir.cpp: # 630| r0_9(glval) = VariableAddress[#return] : # 630| v0_10(void) = ReturnValue : &:r0_9, ~mu0_2 # 630| v0_11(void) = UnmodeledUse : mu* -# 630| v0_12(void) = ExitFunction : +# 630| v0_12(void) = AliasedUse : ~mu0_2 +# 630| v0_13(void) = ExitFunction : # 634| int C::InstanceMemberFunction(int) # 634| Block 0 @@ -2828,7 +2896,8 @@ ir.cpp: # 634| r0_10(glval) = VariableAddress[#return] : # 634| v0_11(void) = ReturnValue : &:r0_10, ~mu0_2 # 634| v0_12(void) = UnmodeledUse : mu* -# 634| v0_13(void) = ExitFunction : +# 634| v0_13(void) = AliasedUse : ~mu0_2 +# 634| v0_14(void) = ExitFunction : # 638| int C::VirtualMemberFunction(int) # 638| Block 0 @@ -2845,7 +2914,8 @@ ir.cpp: # 638| r0_10(glval) = VariableAddress[#return] : # 638| v0_11(void) = ReturnValue : &:r0_10, ~mu0_2 # 638| v0_12(void) = UnmodeledUse : mu* -# 638| v0_13(void) = ExitFunction : +# 638| v0_13(void) = AliasedUse : ~mu0_2 +# 638| v0_14(void) = ExitFunction : # 642| void C::FieldAccess() # 642| Block 0 @@ -2887,7 +2957,8 @@ ir.cpp: # 650| v0_35(void) = NoOp : # 642| v0_36(void) = ReturnVoid : # 642| v0_37(void) = UnmodeledUse : mu* -# 642| v0_38(void) = ExitFunction : +# 642| v0_38(void) = AliasedUse : ~mu0_2 +# 642| v0_39(void) = ExitFunction : # 652| void C::MethodCalls() # 652| Block 0 @@ -2900,7 +2971,7 @@ ir.cpp: # 653| r0_6(int) = Constant[0] : # 653| r0_7(int) = Call : func:r0_5, this:r0_4, 0:r0_6 # 653| mu0_8(unknown) = ^CallSideEffect : ~mu0_2 -# 653| v0_9(void) = ^IndirectReadSideEffect[-1] : &:r0_4, ~mu0_2 +# 653| v0_9(void) = ^BufferReadSideEffect[-1] : &:r0_4, ~mu0_2 # 653| mu0_10(C) = ^IndirectMayWriteSideEffect[-1] : &:r0_4 # 654| r0_11(C *) = CopyValue : r0_3 # 654| r0_12(glval) = CopyValue : r0_11 @@ -2908,19 +2979,20 @@ ir.cpp: # 654| r0_14(int) = Constant[1] : # 654| r0_15(int) = Call : func:r0_13, this:r0_12, 0:r0_14 # 654| mu0_16(unknown) = ^CallSideEffect : ~mu0_2 -# 654| v0_17(void) = ^IndirectReadSideEffect[-1] : &:r0_12, ~mu0_2 +# 654| v0_17(void) = ^BufferReadSideEffect[-1] : &:r0_12, ~mu0_2 # 654| mu0_18(C) = ^IndirectMayWriteSideEffect[-1] : &:r0_12 #-----| r0_19(C *) = CopyValue : r0_3 # 655| r0_20(glval) = FunctionAddress[InstanceMemberFunction] : # 655| r0_21(int) = Constant[2] : # 655| r0_22(int) = Call : func:r0_20, this:r0_19, 0:r0_21 # 655| mu0_23(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v0_24(void) = ^IndirectReadSideEffect[-1] : &:r0_19, ~mu0_2 +#-----| v0_24(void) = ^BufferReadSideEffect[-1] : &:r0_19, ~mu0_2 #-----| mu0_25(C) = ^IndirectMayWriteSideEffect[-1] : &:r0_19 # 656| v0_26(void) = NoOp : # 652| v0_27(void) = ReturnVoid : # 652| v0_28(void) = UnmodeledUse : mu* -# 652| v0_29(void) = ExitFunction : +# 652| v0_29(void) = AliasedUse : ~mu0_2 +# 652| v0_30(void) = ExitFunction : # 658| void C::C() # 658| Block 0 @@ -2947,12 +3019,13 @@ ir.cpp: # 662| r0_20(char *) = Convert : r0_19 # 662| v0_21(void) = Call : func:r0_18, this:r0_17, 0:r0_20 # 662| mu0_22(unknown) = ^CallSideEffect : ~mu0_2 -# 662| v0_23(void) = ^IndirectReadSideEffect[0] : &:r0_20, ~mu0_2 +# 662| v0_23(void) = ^BufferReadSideEffect[0] : &:r0_20, ~mu0_2 # 662| mu0_24(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_20 # 664| v0_25(void) = NoOp : # 658| v0_26(void) = ReturnVoid : # 658| v0_27(void) = UnmodeledUse : mu* -# 658| v0_28(void) = ExitFunction : +# 658| v0_28(void) = AliasedUse : ~mu0_2 +# 658| v0_29(void) = ExitFunction : # 675| int DerefReference(int&) # 675| Block 0 @@ -2969,7 +3042,8 @@ ir.cpp: # 675| r0_10(glval) = VariableAddress[#return] : # 675| v0_11(void) = ReturnValue : &:r0_10, ~mu0_2 # 675| v0_12(void) = UnmodeledUse : mu* -# 675| v0_13(void) = ExitFunction : +# 675| v0_13(void) = AliasedUse : ~mu0_2 +# 675| v0_14(void) = ExitFunction : # 679| int& TakeReference() # 679| Block 0 @@ -2983,7 +3057,8 @@ ir.cpp: # 679| r0_7(glval) = VariableAddress[#return] : # 679| v0_8(void) = ReturnValue : &:r0_7, ~mu0_2 # 679| v0_9(void) = UnmodeledUse : mu* -# 679| v0_10(void) = ExitFunction : +# 679| v0_10(void) = AliasedUse : ~mu0_2 +# 679| v0_11(void) = ExitFunction : # 685| void InitReference(int) # 685| Block 0 @@ -3013,7 +3088,8 @@ ir.cpp: # 689| v0_23(void) = NoOp : # 685| v0_24(void) = ReturnVoid : # 685| v0_25(void) = UnmodeledUse : mu* -# 685| v0_26(void) = ExitFunction : +# 685| v0_26(void) = AliasedUse : ~mu0_2 +# 685| v0_27(void) = ExitFunction : # 691| void ArrayReferences() # 691| Block 0 @@ -3038,7 +3114,8 @@ ir.cpp: # 695| v0_18(void) = NoOp : # 691| v0_19(void) = ReturnVoid : # 691| v0_20(void) = UnmodeledUse : mu* -# 691| v0_21(void) = ExitFunction : +# 691| v0_21(void) = AliasedUse : ~mu0_2 +# 691| v0_22(void) = ExitFunction : # 697| void FunctionReferences() # 697| Block 0 @@ -3063,7 +3140,8 @@ ir.cpp: # 701| v0_18(void) = NoOp : # 697| v0_19(void) = ReturnVoid : # 697| v0_20(void) = UnmodeledUse : mu* -# 697| v0_21(void) = ExitFunction : +# 697| v0_21(void) = AliasedUse : ~mu0_2 +# 697| v0_22(void) = ExitFunction : # 704| int min(int, int) # 704| Block 0 @@ -3105,7 +3183,8 @@ ir.cpp: # 704| r3_3(glval) = VariableAddress[#return] : # 704| v3_4(void) = ReturnValue : &:r3_3, ~mu0_2 # 704| v3_5(void) = UnmodeledUse : mu* -# 704| v3_6(void) = ExitFunction : +# 704| v3_6(void) = AliasedUse : ~mu0_2 +# 704| v3_7(void) = ExitFunction : # 708| int CallMin(int, int) # 708| Block 0 @@ -3128,7 +3207,8 @@ ir.cpp: # 708| r0_16(glval) = VariableAddress[#return] : # 708| v0_17(void) = ReturnValue : &:r0_16, ~mu0_2 # 708| v0_18(void) = UnmodeledUse : mu* -# 708| v0_19(void) = ExitFunction : +# 708| v0_19(void) = AliasedUse : ~mu0_2 +# 708| v0_20(void) = ExitFunction : # 715| long Outer::Func(void*, char) # 715| Block 0 @@ -3145,7 +3225,8 @@ ir.cpp: # 715| r0_10(glval) = VariableAddress[#return] : # 715| v0_11(void) = ReturnValue : &:r0_10, ~mu0_2 # 715| v0_12(void) = UnmodeledUse : mu* -# 715| v0_13(void) = ExitFunction : +# 715| v0_13(void) = AliasedUse : ~mu0_2 +# 715| v0_14(void) = ExitFunction : # 720| double CallNestedTemplateFunc() # 720| Block 0 @@ -3158,14 +3239,15 @@ ir.cpp: # 721| r0_6(char) = Constant[111] : # 721| r0_7(long) = Call : func:r0_4, 0:r0_5, 1:r0_6 # 721| mu0_8(unknown) = ^CallSideEffect : ~mu0_2 -# 721| v0_9(void) = ^IndirectReadSideEffect[0] : &:r0_5, ~mu0_2 +# 721| v0_9(void) = ^BufferReadSideEffect[0] : &:r0_5, ~mu0_2 # 721| mu0_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_5 # 721| r0_11(double) = Convert : r0_7 # 721| mu0_12(double) = Store : &:r0_3, r0_11 # 720| r0_13(glval) = VariableAddress[#return] : # 720| v0_14(void) = ReturnValue : &:r0_13, ~mu0_2 # 720| v0_15(void) = UnmodeledUse : mu* -# 720| v0_16(void) = ExitFunction : +# 720| v0_16(void) = AliasedUse : ~mu0_2 +# 720| v0_17(void) = ExitFunction : # 724| void TryCatch(bool) # 724| Block 0 @@ -3185,7 +3267,8 @@ ir.cpp: # 724| Block 1 # 724| v1_0(void) = UnmodeledUse : mu* -# 724| v1_1(void) = ExitFunction : +# 724| v1_1(void) = AliasedUse : ~mu0_2 +# 724| v1_2(void) = ExitFunction : # 724| Block 2 # 724| v2_0(void) = Unwind : @@ -3232,7 +3315,7 @@ ir.cpp: # 731| r7_3(char *) = Convert : r7_2 # 731| v7_4(void) = Call : func:r7_1, this:r7_0, 0:r7_3 # 731| mu7_5(unknown) = ^CallSideEffect : ~mu0_2 -# 731| v7_6(void) = ^IndirectReadSideEffect[0] : &:r7_3, ~mu0_2 +# 731| v7_6(void) = ^BufferReadSideEffect[0] : &:r7_3, ~mu0_2 # 731| mu7_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r7_3 # 731| v7_8(void) = ThrowValue : &:r7_0, ~mu0_2 #-----| Exception -> Block 9 @@ -3257,7 +3340,7 @@ ir.cpp: # 736| r10_5(char *) = Load : &:r10_4, ~mu0_2 # 736| v10_6(void) = Call : func:r10_3, this:r10_2, 0:r10_5 # 736| mu10_7(unknown) = ^CallSideEffect : ~mu0_2 -# 736| v10_8(void) = ^IndirectReadSideEffect[0] : &:r10_5, ~mu0_2 +# 736| v10_8(void) = ^BufferReadSideEffect[0] : &:r10_5, ~mu0_2 # 736| mu10_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r10_5 # 736| v10_10(void) = ThrowValue : &:r10_2, ~mu0_2 #-----| Exception -> Block 2 @@ -3302,8 +3385,8 @@ ir.cpp: #-----| r0_14(String &) = CopyValue : r0_13 # 745| r0_15(String &) = Call : func:r0_9, this:r0_8, 0:r0_14 # 745| mu0_16(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v0_17(void) = ^IndirectReadSideEffect[-1] : &:r0_8, ~mu0_2 -#-----| v0_18(void) = ^IndirectReadSideEffect[0] : &:r0_14, ~mu0_2 +#-----| v0_17(void) = ^BufferReadSideEffect[-1] : &:r0_8, ~mu0_2 +#-----| v0_18(void) = ^BufferReadSideEffect[0] : &:r0_14, ~mu0_2 #-----| mu0_19(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_8 #-----| mu0_20(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_14 #-----| r0_21(glval) = CopyValue : r0_15 @@ -3315,7 +3398,8 @@ ir.cpp: # 745| r0_27(glval) = VariableAddress[#return] : # 745| v0_28(void) = ReturnValue : &:r0_27, ~mu0_2 # 745| v0_29(void) = UnmodeledUse : mu* -# 745| v0_30(void) = ExitFunction : +# 745| v0_30(void) = AliasedUse : ~mu0_2 +# 745| v0_31(void) = ExitFunction : # 745| void Base::Base(Base const&) # 745| Block 0 @@ -3332,7 +3416,8 @@ ir.cpp: # 745| v0_10(void) = NoOp : # 745| v0_11(void) = ReturnVoid : # 745| v0_12(void) = UnmodeledUse : mu* -# 745| v0_13(void) = ExitFunction : +# 745| v0_13(void) = AliasedUse : ~mu0_2 +# 745| v0_14(void) = ExitFunction : # 748| void Base::Base() # 748| Block 0 @@ -3347,7 +3432,8 @@ ir.cpp: # 749| v0_8(void) = NoOp : # 748| v0_9(void) = ReturnVoid : # 748| v0_10(void) = UnmodeledUse : mu* -# 748| v0_11(void) = ExitFunction : +# 748| v0_11(void) = AliasedUse : ~mu0_2 +# 748| v0_12(void) = ExitFunction : # 750| void Base::~Base() # 750| Block 0 @@ -3362,7 +3448,8 @@ ir.cpp: # 751| mu0_8(unknown) = ^CallSideEffect : ~mu0_2 # 750| v0_9(void) = ReturnVoid : # 750| v0_10(void) = UnmodeledUse : mu* -# 750| v0_11(void) = ExitFunction : +# 750| v0_11(void) = AliasedUse : ~mu0_2 +# 750| v0_12(void) = ExitFunction : # 754| Middle& Middle::operator=(Middle const&) # 754| Block 0 @@ -3384,8 +3471,8 @@ ir.cpp: #-----| r0_15(Base &) = CopyValue : r0_14 # 754| r0_16(Base &) = Call : func:r0_8, this:r0_7, 0:r0_15 # 754| mu0_17(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v0_18(void) = ^IndirectReadSideEffect[-1] : &:r0_7, ~mu0_2 -#-----| v0_19(void) = ^IndirectReadSideEffect[0] : &:r0_15, ~mu0_2 +#-----| v0_18(void) = ^BufferReadSideEffect[-1] : &:r0_7, ~mu0_2 +#-----| v0_19(void) = ^BufferReadSideEffect[0] : &:r0_15, ~mu0_2 #-----| mu0_20(Base) = ^IndirectMayWriteSideEffect[-1] : &:r0_7 #-----| mu0_21(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_15 #-----| r0_22(glval) = CopyValue : r0_16 @@ -3400,8 +3487,8 @@ ir.cpp: #-----| r0_31(String &) = CopyValue : r0_30 # 754| r0_32(String &) = Call : func:r0_26, this:r0_25, 0:r0_31 # 754| mu0_33(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v0_34(void) = ^IndirectReadSideEffect[-1] : &:r0_25, ~mu0_2 -#-----| v0_35(void) = ^IndirectReadSideEffect[0] : &:r0_31, ~mu0_2 +#-----| v0_34(void) = ^BufferReadSideEffect[-1] : &:r0_25, ~mu0_2 +#-----| v0_35(void) = ^BufferReadSideEffect[0] : &:r0_31, ~mu0_2 #-----| mu0_36(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_25 #-----| mu0_37(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_31 #-----| r0_38(glval) = CopyValue : r0_32 @@ -3413,7 +3500,8 @@ ir.cpp: # 754| r0_44(glval) = VariableAddress[#return] : # 754| v0_45(void) = ReturnValue : &:r0_44, ~mu0_2 # 754| v0_46(void) = UnmodeledUse : mu* -# 754| v0_47(void) = ExitFunction : +# 754| v0_47(void) = AliasedUse : ~mu0_2 +# 754| v0_48(void) = ExitFunction : # 757| void Middle::Middle() # 757| Block 0 @@ -3432,7 +3520,8 @@ ir.cpp: # 758| v0_12(void) = NoOp : # 757| v0_13(void) = ReturnVoid : # 757| v0_14(void) = UnmodeledUse : mu* -# 757| v0_15(void) = ExitFunction : +# 757| v0_15(void) = AliasedUse : ~mu0_2 +# 757| v0_16(void) = ExitFunction : # 759| void Middle::~Middle() # 759| Block 0 @@ -3451,7 +3540,8 @@ ir.cpp: # 760| mu0_12(unknown) = ^CallSideEffect : ~mu0_2 # 759| v0_13(void) = ReturnVoid : # 759| v0_14(void) = UnmodeledUse : mu* -# 759| v0_15(void) = ExitFunction : +# 759| v0_15(void) = AliasedUse : ~mu0_2 +# 759| v0_16(void) = ExitFunction : # 763| Derived& Derived::operator=(Derived const&) # 763| Block 0 @@ -3473,8 +3563,8 @@ ir.cpp: #-----| r0_15(Middle &) = CopyValue : r0_14 # 763| r0_16(Middle &) = Call : func:r0_8, this:r0_7, 0:r0_15 # 763| mu0_17(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v0_18(void) = ^IndirectReadSideEffect[-1] : &:r0_7, ~mu0_2 -#-----| v0_19(void) = ^IndirectReadSideEffect[0] : &:r0_15, ~mu0_2 +#-----| v0_18(void) = ^BufferReadSideEffect[-1] : &:r0_7, ~mu0_2 +#-----| v0_19(void) = ^BufferReadSideEffect[0] : &:r0_15, ~mu0_2 #-----| mu0_20(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r0_7 #-----| mu0_21(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_15 #-----| r0_22(glval) = CopyValue : r0_16 @@ -3489,8 +3579,8 @@ ir.cpp: #-----| r0_31(String &) = CopyValue : r0_30 # 763| r0_32(String &) = Call : func:r0_26, this:r0_25, 0:r0_31 # 763| mu0_33(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v0_34(void) = ^IndirectReadSideEffect[-1] : &:r0_25, ~mu0_2 -#-----| v0_35(void) = ^IndirectReadSideEffect[0] : &:r0_31, ~mu0_2 +#-----| v0_34(void) = ^BufferReadSideEffect[-1] : &:r0_25, ~mu0_2 +#-----| v0_35(void) = ^BufferReadSideEffect[0] : &:r0_31, ~mu0_2 #-----| mu0_36(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_25 #-----| mu0_37(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_31 #-----| r0_38(glval) = CopyValue : r0_32 @@ -3502,7 +3592,8 @@ ir.cpp: # 763| r0_44(glval) = VariableAddress[#return] : # 763| v0_45(void) = ReturnValue : &:r0_44, ~mu0_2 # 763| v0_46(void) = UnmodeledUse : mu* -# 763| v0_47(void) = ExitFunction : +# 763| v0_47(void) = AliasedUse : ~mu0_2 +# 763| v0_48(void) = ExitFunction : # 766| void Derived::Derived() # 766| Block 0 @@ -3521,7 +3612,8 @@ ir.cpp: # 767| v0_12(void) = NoOp : # 766| v0_13(void) = ReturnVoid : # 766| v0_14(void) = UnmodeledUse : mu* -# 766| v0_15(void) = ExitFunction : +# 766| v0_15(void) = AliasedUse : ~mu0_2 +# 766| v0_16(void) = ExitFunction : # 768| void Derived::~Derived() # 768| Block 0 @@ -3540,7 +3632,8 @@ ir.cpp: # 769| mu0_12(unknown) = ^CallSideEffect : ~mu0_2 # 768| v0_13(void) = ReturnVoid : # 768| v0_14(void) = UnmodeledUse : mu* -# 768| v0_15(void) = ExitFunction : +# 768| v0_15(void) = AliasedUse : ~mu0_2 +# 768| v0_16(void) = ExitFunction : # 775| void MiddleVB1::MiddleVB1() # 775| Block 0 @@ -3559,7 +3652,8 @@ ir.cpp: # 776| v0_12(void) = NoOp : # 775| v0_13(void) = ReturnVoid : # 775| v0_14(void) = UnmodeledUse : mu* -# 775| v0_15(void) = ExitFunction : +# 775| v0_15(void) = AliasedUse : ~mu0_2 +# 775| v0_16(void) = ExitFunction : # 777| void MiddleVB1::~MiddleVB1() # 777| Block 0 @@ -3578,7 +3672,8 @@ ir.cpp: # 778| mu0_12(unknown) = ^CallSideEffect : ~mu0_2 # 777| v0_13(void) = ReturnVoid : # 777| v0_14(void) = UnmodeledUse : mu* -# 777| v0_15(void) = ExitFunction : +# 777| v0_15(void) = AliasedUse : ~mu0_2 +# 777| v0_16(void) = ExitFunction : # 784| void MiddleVB2::MiddleVB2() # 784| Block 0 @@ -3597,7 +3692,8 @@ ir.cpp: # 785| v0_12(void) = NoOp : # 784| v0_13(void) = ReturnVoid : # 784| v0_14(void) = UnmodeledUse : mu* -# 784| v0_15(void) = ExitFunction : +# 784| v0_15(void) = AliasedUse : ~mu0_2 +# 784| v0_16(void) = ExitFunction : # 786| void MiddleVB2::~MiddleVB2() # 786| Block 0 @@ -3616,7 +3712,8 @@ ir.cpp: # 787| mu0_12(unknown) = ^CallSideEffect : ~mu0_2 # 786| v0_13(void) = ReturnVoid : # 786| v0_14(void) = UnmodeledUse : mu* -# 786| v0_15(void) = ExitFunction : +# 786| v0_15(void) = AliasedUse : ~mu0_2 +# 786| v0_16(void) = ExitFunction : # 793| void DerivedVB::DerivedVB() # 793| Block 0 @@ -3643,7 +3740,8 @@ ir.cpp: # 794| v0_20(void) = NoOp : # 793| v0_21(void) = ReturnVoid : # 793| v0_22(void) = UnmodeledUse : mu* -# 793| v0_23(void) = ExitFunction : +# 793| v0_23(void) = AliasedUse : ~mu0_2 +# 793| v0_24(void) = ExitFunction : # 795| void DerivedVB::~DerivedVB() # 795| Block 0 @@ -3670,7 +3768,8 @@ ir.cpp: # 796| mu0_20(unknown) = ^CallSideEffect : ~mu0_2 # 795| v0_21(void) = ReturnVoid : # 795| v0_22(void) = UnmodeledUse : mu* -# 795| v0_23(void) = ExitFunction : +# 795| v0_23(void) = AliasedUse : ~mu0_2 +# 795| v0_24(void) = ExitFunction : # 799| void HierarchyConversions() # 799| Block 0 @@ -3708,8 +3807,8 @@ ir.cpp: # 808| r0_31(Base &) = CopyValue : r0_30 # 808| r0_32(Base &) = Call : func:r0_28, this:r0_27, 0:r0_31 # 808| mu0_33(unknown) = ^CallSideEffect : ~mu0_2 -# 808| v0_34(void) = ^IndirectReadSideEffect[-1] : &:r0_27, ~mu0_2 -# 808| v0_35(void) = ^IndirectReadSideEffect[0] : &:r0_31, ~mu0_2 +# 808| v0_34(void) = ^BufferReadSideEffect[-1] : &:r0_27, ~mu0_2 +# 808| v0_35(void) = ^BufferReadSideEffect[0] : &:r0_31, ~mu0_2 # 808| mu0_36(Base) = ^IndirectMayWriteSideEffect[-1] : &:r0_27 # 808| mu0_37(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_31 # 808| r0_38(glval) = CopyValue : r0_32 @@ -3721,14 +3820,14 @@ ir.cpp: # 809| r0_44(Base &) = CopyValue : r0_43 # 809| v0_45(void) = Call : func:r0_41, 0:r0_44 # 809| mu0_46(unknown) = ^CallSideEffect : ~mu0_2 -# 809| v0_47(void) = ^IndirectReadSideEffect[0] : &:r0_44, ~mu0_2 +# 809| v0_47(void) = ^BufferReadSideEffect[0] : &:r0_44, ~mu0_2 # 809| mu0_48(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_44 # 809| r0_49(glval) = Convert : v0_45 # 809| r0_50(Base &) = CopyValue : r0_49 # 809| r0_51(Base &) = Call : func:r0_40, this:r0_39, 0:r0_50 # 809| mu0_52(unknown) = ^CallSideEffect : ~mu0_2 -# 809| v0_53(void) = ^IndirectReadSideEffect[-1] : &:r0_39, ~mu0_2 -# 809| v0_54(void) = ^IndirectReadSideEffect[0] : &:r0_50, ~mu0_2 +# 809| v0_53(void) = ^BufferReadSideEffect[-1] : &:r0_39, ~mu0_2 +# 809| v0_54(void) = ^BufferReadSideEffect[0] : &:r0_50, ~mu0_2 # 809| mu0_55(Base) = ^IndirectMayWriteSideEffect[-1] : &:r0_39 # 809| mu0_56(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_50 # 809| r0_57(glval) = CopyValue : r0_51 @@ -3740,14 +3839,14 @@ ir.cpp: # 810| r0_63(Base &) = CopyValue : r0_62 # 810| v0_64(void) = Call : func:r0_60, 0:r0_63 # 810| mu0_65(unknown) = ^CallSideEffect : ~mu0_2 -# 810| v0_66(void) = ^IndirectReadSideEffect[0] : &:r0_63, ~mu0_2 +# 810| v0_66(void) = ^BufferReadSideEffect[0] : &:r0_63, ~mu0_2 # 810| mu0_67(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_63 # 810| r0_68(glval) = Convert : v0_64 # 810| r0_69(Base &) = CopyValue : r0_68 # 810| r0_70(Base &) = Call : func:r0_59, this:r0_58, 0:r0_69 # 810| mu0_71(unknown) = ^CallSideEffect : ~mu0_2 -# 810| v0_72(void) = ^IndirectReadSideEffect[-1] : &:r0_58, ~mu0_2 -# 810| v0_73(void) = ^IndirectReadSideEffect[0] : &:r0_69, ~mu0_2 +# 810| v0_72(void) = ^BufferReadSideEffect[-1] : &:r0_58, ~mu0_2 +# 810| v0_73(void) = ^BufferReadSideEffect[0] : &:r0_69, ~mu0_2 # 810| mu0_74(Base) = ^IndirectMayWriteSideEffect[-1] : &:r0_58 # 810| mu0_75(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_69 # 810| r0_76(glval) = CopyValue : r0_70 @@ -3779,8 +3878,8 @@ ir.cpp: # 816| r0_102(Middle &) = CopyValue : r0_101 # 816| r0_103(Middle &) = Call : func:r0_98, this:r0_97, 0:r0_102 # 816| mu0_104(unknown) = ^CallSideEffect : ~mu0_2 -# 816| v0_105(void) = ^IndirectReadSideEffect[-1] : &:r0_97, ~mu0_2 -# 816| v0_106(void) = ^IndirectReadSideEffect[0] : &:r0_102, ~mu0_2 +# 816| v0_105(void) = ^BufferReadSideEffect[-1] : &:r0_97, ~mu0_2 +# 816| v0_106(void) = ^BufferReadSideEffect[0] : &:r0_102, ~mu0_2 # 816| mu0_107(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r0_97 # 816| mu0_108(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_102 # 816| r0_109(glval) = CopyValue : r0_103 @@ -3792,8 +3891,8 @@ ir.cpp: # 817| r0_115(Middle &) = CopyValue : r0_114 # 817| r0_116(Middle &) = Call : func:r0_111, this:r0_110, 0:r0_115 # 817| mu0_117(unknown) = ^CallSideEffect : ~mu0_2 -# 817| v0_118(void) = ^IndirectReadSideEffect[-1] : &:r0_110, ~mu0_2 -# 817| v0_119(void) = ^IndirectReadSideEffect[0] : &:r0_115, ~mu0_2 +# 817| v0_118(void) = ^BufferReadSideEffect[-1] : &:r0_110, ~mu0_2 +# 817| v0_119(void) = ^BufferReadSideEffect[0] : &:r0_115, ~mu0_2 # 817| mu0_120(Middle) = ^IndirectMayWriteSideEffect[-1] : &:r0_110 # 817| mu0_121(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_115 # 817| r0_122(glval) = CopyValue : r0_116 @@ -3820,8 +3919,8 @@ ir.cpp: # 822| r0_143(Base &) = CopyValue : r0_142 # 822| r0_144(Base &) = Call : func:r0_139, this:r0_138, 0:r0_143 # 822| mu0_145(unknown) = ^CallSideEffect : ~mu0_2 -# 822| v0_146(void) = ^IndirectReadSideEffect[-1] : &:r0_138, ~mu0_2 -# 822| v0_147(void) = ^IndirectReadSideEffect[0] : &:r0_143, ~mu0_2 +# 822| v0_146(void) = ^BufferReadSideEffect[-1] : &:r0_138, ~mu0_2 +# 822| v0_147(void) = ^BufferReadSideEffect[0] : &:r0_143, ~mu0_2 # 822| mu0_148(Base) = ^IndirectMayWriteSideEffect[-1] : &:r0_138 # 822| mu0_149(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_143 # 822| r0_150(glval) = CopyValue : r0_144 @@ -3834,14 +3933,14 @@ ir.cpp: # 823| r0_157(Base &) = CopyValue : r0_156 # 823| v0_158(void) = Call : func:r0_153, 0:r0_157 # 823| mu0_159(unknown) = ^CallSideEffect : ~mu0_2 -# 823| v0_160(void) = ^IndirectReadSideEffect[0] : &:r0_157, ~mu0_2 +# 823| v0_160(void) = ^BufferReadSideEffect[0] : &:r0_157, ~mu0_2 # 823| mu0_161(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_157 # 823| r0_162(glval) = Convert : v0_158 # 823| r0_163(Base &) = CopyValue : r0_162 # 823| r0_164(Base &) = Call : func:r0_152, this:r0_151, 0:r0_163 # 823| mu0_165(unknown) = ^CallSideEffect : ~mu0_2 -# 823| v0_166(void) = ^IndirectReadSideEffect[-1] : &:r0_151, ~mu0_2 -# 823| v0_167(void) = ^IndirectReadSideEffect[0] : &:r0_163, ~mu0_2 +# 823| v0_166(void) = ^BufferReadSideEffect[-1] : &:r0_151, ~mu0_2 +# 823| v0_167(void) = ^BufferReadSideEffect[0] : &:r0_163, ~mu0_2 # 823| mu0_168(Base) = ^IndirectMayWriteSideEffect[-1] : &:r0_151 # 823| mu0_169(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_163 # 823| r0_170(glval) = CopyValue : r0_164 @@ -3854,14 +3953,14 @@ ir.cpp: # 824| r0_177(Base &) = CopyValue : r0_176 # 824| v0_178(void) = Call : func:r0_173, 0:r0_177 # 824| mu0_179(unknown) = ^CallSideEffect : ~mu0_2 -# 824| v0_180(void) = ^IndirectReadSideEffect[0] : &:r0_177, ~mu0_2 +# 824| v0_180(void) = ^BufferReadSideEffect[0] : &:r0_177, ~mu0_2 # 824| mu0_181(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_177 # 824| r0_182(glval) = Convert : v0_178 # 824| r0_183(Base &) = CopyValue : r0_182 # 824| r0_184(Base &) = Call : func:r0_172, this:r0_171, 0:r0_183 # 824| mu0_185(unknown) = ^CallSideEffect : ~mu0_2 -# 824| v0_186(void) = ^IndirectReadSideEffect[-1] : &:r0_171, ~mu0_2 -# 824| v0_187(void) = ^IndirectReadSideEffect[0] : &:r0_183, ~mu0_2 +# 824| v0_186(void) = ^BufferReadSideEffect[-1] : &:r0_171, ~mu0_2 +# 824| v0_187(void) = ^BufferReadSideEffect[0] : &:r0_183, ~mu0_2 # 824| mu0_188(Base) = ^IndirectMayWriteSideEffect[-1] : &:r0_171 # 824| mu0_189(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_183 # 824| r0_190(glval) = CopyValue : r0_184 @@ -3897,8 +3996,8 @@ ir.cpp: # 830| r0_220(Derived &) = CopyValue : r0_219 # 830| r0_221(Derived &) = Call : func:r0_215, this:r0_214, 0:r0_220 # 830| mu0_222(unknown) = ^CallSideEffect : ~mu0_2 -# 830| v0_223(void) = ^IndirectReadSideEffect[-1] : &:r0_214, ~mu0_2 -# 830| v0_224(void) = ^IndirectReadSideEffect[0] : &:r0_220, ~mu0_2 +# 830| v0_223(void) = ^BufferReadSideEffect[-1] : &:r0_214, ~mu0_2 +# 830| v0_224(void) = ^BufferReadSideEffect[0] : &:r0_220, ~mu0_2 # 830| mu0_225(Derived) = ^IndirectMayWriteSideEffect[-1] : &:r0_214 # 830| mu0_226(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_220 # 830| r0_227(glval) = CopyValue : r0_221 @@ -3911,8 +4010,8 @@ ir.cpp: # 831| r0_234(Derived &) = CopyValue : r0_233 # 831| r0_235(Derived &) = Call : func:r0_229, this:r0_228, 0:r0_234 # 831| mu0_236(unknown) = ^CallSideEffect : ~mu0_2 -# 831| v0_237(void) = ^IndirectReadSideEffect[-1] : &:r0_228, ~mu0_2 -# 831| v0_238(void) = ^IndirectReadSideEffect[0] : &:r0_234, ~mu0_2 +# 831| v0_237(void) = ^BufferReadSideEffect[-1] : &:r0_228, ~mu0_2 +# 831| v0_238(void) = ^BufferReadSideEffect[0] : &:r0_234, ~mu0_2 # 831| mu0_239(Derived) = ^IndirectMayWriteSideEffect[-1] : &:r0_228 # 831| mu0_240(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_234 # 831| r0_241(glval) = CopyValue : r0_235 @@ -3952,7 +4051,8 @@ ir.cpp: # 840| v0_275(void) = NoOp : # 799| v0_276(void) = ReturnVoid : # 799| v0_277(void) = UnmodeledUse : mu* -# 799| v0_278(void) = ExitFunction : +# 799| v0_278(void) = AliasedUse : ~mu0_2 +# 799| v0_279(void) = ExitFunction : # 842| void PolymorphicBase::PolymorphicBase() # 842| Block 0 @@ -3963,7 +4063,8 @@ ir.cpp: # 842| v0_4(void) = NoOp : # 842| v0_5(void) = ReturnVoid : # 842| v0_6(void) = UnmodeledUse : mu* -# 842| v0_7(void) = ExitFunction : +# 842| v0_7(void) = AliasedUse : ~mu0_2 +# 842| v0_8(void) = ExitFunction : # 846| void PolymorphicDerived::PolymorphicDerived() # 846| Block 0 @@ -3978,7 +4079,8 @@ ir.cpp: # 846| v0_8(void) = NoOp : # 846| v0_9(void) = ReturnVoid : # 846| v0_10(void) = UnmodeledUse : mu* -# 846| v0_11(void) = ExitFunction : +# 846| v0_11(void) = AliasedUse : ~mu0_2 +# 846| v0_12(void) = ExitFunction : # 846| void PolymorphicDerived::~PolymorphicDerived() # 846| Block 0 @@ -3993,7 +4095,8 @@ ir.cpp: # 846| mu0_8(unknown) = ^CallSideEffect : ~mu0_2 # 846| v0_9(void) = ReturnVoid : # 846| v0_10(void) = UnmodeledUse : mu* -# 846| v0_11(void) = ExitFunction : +# 846| v0_11(void) = AliasedUse : ~mu0_2 +# 846| v0_12(void) = ExitFunction : # 849| void DynamicCast() # 849| Block 0 @@ -4049,7 +4152,8 @@ ir.cpp: # 865| v0_49(void) = NoOp : # 849| v0_50(void) = ReturnVoid : # 849| v0_51(void) = UnmodeledUse : mu* -# 849| v0_52(void) = ExitFunction : +# 849| v0_52(void) = AliasedUse : ~mu0_2 +# 849| v0_53(void) = ExitFunction : # 867| void String::String() # 867| Block 0 @@ -4062,12 +4166,13 @@ ir.cpp: # 868| r0_6(char *) = Convert : r0_5 # 868| v0_7(void) = Call : func:r0_4, this:r0_3, 0:r0_6 # 868| mu0_8(unknown) = ^CallSideEffect : ~mu0_2 -# 868| v0_9(void) = ^IndirectReadSideEffect[0] : &:r0_6, ~mu0_2 +# 868| v0_9(void) = ^BufferReadSideEffect[0] : &:r0_6, ~mu0_2 # 868| mu0_10(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_6 # 869| v0_11(void) = NoOp : # 867| v0_12(void) = ReturnVoid : # 867| v0_13(void) = UnmodeledUse : mu* -# 867| v0_14(void) = ExitFunction : +# 867| v0_14(void) = AliasedUse : ~mu0_2 +# 867| v0_15(void) = ExitFunction : # 871| void ArrayConversions() # 871| Block 0 @@ -4120,7 +4225,8 @@ ir.cpp: # 881| v0_46(void) = NoOp : # 871| v0_47(void) = ReturnVoid : # 871| v0_48(void) = UnmodeledUse : mu* -# 871| v0_49(void) = ExitFunction : +# 871| v0_49(void) = AliasedUse : ~mu0_2 +# 871| v0_50(void) = ExitFunction : # 883| void FuncPtrConversions(int(*)(int), void*) # 883| Block 0 @@ -4144,7 +4250,8 @@ ir.cpp: # 886| v0_17(void) = NoOp : # 883| v0_18(void) = ReturnVoid : # 883| v0_19(void) = UnmodeledUse : mu* -# 883| v0_20(void) = ExitFunction : +# 883| v0_20(void) = AliasedUse : ~mu0_2 +# 883| v0_21(void) = ExitFunction : # 888| void VarArgUsage(int) # 888| Block 0 @@ -4188,7 +4295,8 @@ ir.cpp: # 898| v0_37(void) = NoOp : # 888| v0_38(void) = ReturnVoid : # 888| v0_39(void) = UnmodeledUse : mu* -# 888| v0_40(void) = ExitFunction : +# 888| v0_40(void) = AliasedUse : ~mu0_2 +# 888| v0_41(void) = ExitFunction : # 900| void CastToVoid(int) # 900| Block 0 @@ -4202,7 +4310,8 @@ ir.cpp: # 902| v0_7(void) = NoOp : # 900| v0_8(void) = ReturnVoid : # 900| v0_9(void) = UnmodeledUse : mu* -# 900| v0_10(void) = ExitFunction : +# 900| v0_10(void) = AliasedUse : ~mu0_2 +# 900| v0_11(void) = ExitFunction : # 904| void ConstantConditions(int) # 904| Block 0 @@ -4227,7 +4336,8 @@ ir.cpp: # 907| v1_3(void) = NoOp : # 904| v1_4(void) = ReturnVoid : # 904| v1_5(void) = UnmodeledUse : mu* -# 904| v1_6(void) = ExitFunction : +# 904| v1_6(void) = AliasedUse : ~mu0_2 +# 904| v1_7(void) = ExitFunction : # 906| Block 2 # 906| r2_0(glval) = VariableAddress[x] : @@ -4285,7 +4395,7 @@ ir.cpp: # 945| r0_37(char *) = Convert : r0_36 # 945| v0_38(void) = Call : func:r0_35, this:r0_34, 0:r0_37 # 945| mu0_39(unknown) = ^CallSideEffect : ~mu0_2 -# 945| v0_40(void) = ^IndirectReadSideEffect[0] : &:r0_37, ~mu0_2 +# 945| v0_40(void) = ^BufferReadSideEffect[0] : &:r0_37, ~mu0_2 # 945| mu0_41(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_37 # 946| r0_42(glval) = FunctionAddress[operator new] : # 946| r0_43(unsigned long) = Constant[256] : @@ -4305,7 +4415,8 @@ ir.cpp: # 948| v0_57(void) = NoOp : # 940| v0_58(void) = ReturnVoid : # 940| v0_59(void) = UnmodeledUse : mu* -# 940| v0_60(void) = ExitFunction : +# 940| v0_60(void) = AliasedUse : ~mu0_2 +# 940| v0_61(void) = ExitFunction : # 950| void OperatorNewArray(int) # 950| Block 0 @@ -4385,7 +4496,8 @@ ir.cpp: # 959| v0_73(void) = NoOp : # 950| v0_74(void) = ReturnVoid : # 950| v0_75(void) = UnmodeledUse : mu* -# 950| v0_76(void) = ExitFunction : +# 950| v0_76(void) = AliasedUse : ~mu0_2 +# 950| v0_77(void) = ExitFunction : # 961| int designatedInit() # 961| Block 0 @@ -4424,7 +4536,8 @@ ir.cpp: # 961| r0_32(glval) = VariableAddress[#return] : # 961| v0_33(void) = ReturnValue : &:r0_32, ~mu0_2 # 961| v0_34(void) = UnmodeledUse : mu* -# 961| v0_35(void) = ExitFunction : +# 961| v0_35(void) = AliasedUse : ~mu0_2 +# 961| v0_36(void) = ExitFunction : # 966| void IfStmtWithDeclaration(int, int) # 966| Block 0 @@ -4504,7 +4617,8 @@ ir.cpp: # 976| v6_0(void) = NoOp : # 966| v6_1(void) = ReturnVoid : # 966| v6_2(void) = UnmodeledUse : mu* -# 966| v6_3(void) = ExitFunction : +# 966| v6_3(void) = AliasedUse : ~mu0_2 +# 966| v6_4(void) = ExitFunction : # 978| void WhileStmtWithDeclaration(int, int) # 978| Block 0 @@ -4564,7 +4678,8 @@ ir.cpp: # 985| v6_0(void) = NoOp : # 978| v6_1(void) = ReturnVoid : # 978| v6_2(void) = UnmodeledUse : mu* -# 978| v6_3(void) = ExitFunction : +# 978| v6_3(void) = AliasedUse : ~mu0_2 +# 978| v6_4(void) = ExitFunction : # 979| Block 7 # 979| r7_0(glval) = VariableAddress[b] : @@ -4606,7 +4721,8 @@ ir.cpp: # 987| r0_20(glval) = VariableAddress[#return] : # 987| v0_21(void) = ReturnValue : &:r0_20, ~mu0_2 # 987| v0_22(void) = UnmodeledUse : mu* -# 987| v0_23(void) = ExitFunction : +# 987| v0_23(void) = AliasedUse : ~mu0_2 +# 987| v0_24(void) = ExitFunction : # 991| int ExprStmt(int, int, int) # 991| Block 0 @@ -4657,7 +4773,8 @@ ir.cpp: # 991| r3_9(glval) = VariableAddress[#return] : # 991| v3_10(void) = ReturnValue : &:r3_9, ~mu0_2 # 991| v3_11(void) = UnmodeledUse : mu* -# 991| v3_12(void) = ExitFunction : +# 991| v3_12(void) = AliasedUse : ~mu0_2 +# 991| v3_13(void) = ExitFunction : # 1006| void OperatorDelete() # 1006| Block 0 @@ -4677,7 +4794,8 @@ ir.cpp: # 1012| v0_13(void) = NoOp : # 1006| v0_14(void) = ReturnVoid : # 1006| v0_15(void) = UnmodeledUse : mu* -# 1006| v0_16(void) = ExitFunction : +# 1006| v0_16(void) = AliasedUse : ~mu0_2 +# 1006| v0_17(void) = ExitFunction : # 1015| void OperatorDeleteArray() # 1015| Block 0 @@ -4697,7 +4815,8 @@ ir.cpp: # 1021| v0_13(void) = NoOp : # 1015| v0_14(void) = ReturnVoid : # 1015| v0_15(void) = UnmodeledUse : mu* -# 1015| v0_16(void) = ExitFunction : +# 1015| v0_16(void) = AliasedUse : ~mu0_2 +# 1015| v0_17(void) = ExitFunction : # 1025| void EmptyStructInit() # 1025| Block 0 @@ -4709,7 +4828,8 @@ ir.cpp: # 1027| v0_5(void) = NoOp : # 1025| v0_6(void) = ReturnVoid : # 1025| v0_7(void) = UnmodeledUse : mu* -# 1025| v0_8(void) = ExitFunction : +# 1025| v0_8(void) = AliasedUse : ~mu0_2 +# 1025| v0_9(void) = ExitFunction : # 1029| void (lambda [] type at line 1029, col. 12)::(constructor)((lambda [] type at line 1029, col. 12)&&) # 1029| Block 0 @@ -4722,7 +4842,8 @@ ir.cpp: # 1029| v0_6(void) = NoOp : # 1029| v0_7(void) = ReturnVoid : # 1029| v0_8(void) = UnmodeledUse : mu* -# 1029| v0_9(void) = ExitFunction : +# 1029| v0_9(void) = AliasedUse : ~mu0_2 +# 1029| v0_10(void) = ExitFunction : # 1029| void (lambda [] type at line 1029, col. 12)::operator()() const # 1029| Block 0 @@ -4733,7 +4854,8 @@ ir.cpp: # 1029| v0_4(void) = NoOp : # 1029| v0_5(void) = ReturnVoid : # 1029| v0_6(void) = UnmodeledUse : mu* -# 1029| v0_7(void) = ExitFunction : +# 1029| v0_7(void) = AliasedUse : ~mu0_2 +# 1029| v0_8(void) = ExitFunction : # 1029| void(* (lambda [] type at line 1029, col. 12)::operator void (*)()() const)() # 1029| Block 0 @@ -4742,12 +4864,13 @@ ir.cpp: # 1029| mu0_2(unknown) = UnmodeledDefinition : # 1029| r0_3(glval) = InitializeThis : # 1029| r0_4(glval<..(*)(..)>) = VariableAddress[#return] : -# 1029| r0_5(glval<..(*)(..)>) = FunctionAddress[_FUN] : +# 1029| r0_5(..(*)(..)) = FunctionAddress[_FUN] : # 1029| mu0_6(..(*)(..)) = Store : &:r0_4, r0_5 # 1029| r0_7(glval<..(*)(..)>) = VariableAddress[#return] : # 1029| v0_8(void) = ReturnValue : &:r0_7, ~mu0_2 # 1029| v0_9(void) = UnmodeledUse : mu* -# 1029| v0_10(void) = ExitFunction : +# 1029| v0_10(void) = AliasedUse : ~mu0_2 +# 1029| v0_11(void) = ExitFunction : # 1031| void Lambda(int, String const&) # 1031| Block 0 @@ -4785,7 +4908,7 @@ ir.cpp: # 1035| r0_31(float) = Constant[1.0] : # 1035| r0_32(char) = Call : func:r0_30, this:r0_29, 0:r0_31 # 1035| mu0_33(unknown) = ^CallSideEffect : ~mu0_2 -# 1035| v0_34(void) = ^IndirectReadSideEffect[-1] : &:r0_29, ~mu0_2 +# 1035| v0_34(void) = ^BufferReadSideEffect[-1] : &:r0_29, ~mu0_2 # 1035| mu0_35(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r0_29 # 1036| r0_36(glval) = VariableAddress[lambda_val] : # 1036| r0_37(glval) = FunctionAddress[(constructor)] : @@ -4803,7 +4926,7 @@ ir.cpp: # 1036| r0_49(lambda [] type at line 1036, col. 21 &) = CopyValue : r0_48 # 1036| v0_50(void) = Call : func:r0_37, this:r0_36, 0:r0_49 # 1036| mu0_51(unknown) = ^CallSideEffect : ~mu0_2 -# 1036| v0_52(void) = ^IndirectReadSideEffect[0] : &:r0_49, ~mu0_2 +# 1036| v0_52(void) = ^BufferReadSideEffect[0] : &:r0_49, ~mu0_2 # 1036| mu0_53(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_49 # 1037| r0_54(glval) = VariableAddress[lambda_val] : # 1037| r0_55(glval) = Convert : r0_54 @@ -4811,7 +4934,7 @@ ir.cpp: # 1037| r0_57(float) = Constant[2.0] : # 1037| r0_58(char) = Call : func:r0_56, this:r0_55, 0:r0_57 # 1037| mu0_59(unknown) = ^CallSideEffect : ~mu0_2 -# 1037| v0_60(void) = ^IndirectReadSideEffect[-1] : &:r0_55, ~mu0_2 +# 1037| v0_60(void) = ^BufferReadSideEffect[-1] : &:r0_55, ~mu0_2 # 1037| mu0_61(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r0_55 # 1038| r0_62(glval) = VariableAddress[lambda_ref_explicit] : # 1038| r0_63(glval) = VariableAddress[#temp1038:30] : @@ -4830,7 +4953,7 @@ ir.cpp: # 1039| r0_76(float) = Constant[3.0] : # 1039| r0_77(char) = Call : func:r0_75, this:r0_74, 0:r0_76 # 1039| mu0_78(unknown) = ^CallSideEffect : ~mu0_2 -# 1039| v0_79(void) = ^IndirectReadSideEffect[-1] : &:r0_74, ~mu0_2 +# 1039| v0_79(void) = ^BufferReadSideEffect[-1] : &:r0_74, ~mu0_2 # 1039| mu0_80(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r0_74 # 1040| r0_81(glval) = VariableAddress[lambda_val_explicit] : # 1040| r0_82(glval) = FunctionAddress[(constructor)] : @@ -4844,7 +4967,7 @@ ir.cpp: # 1040| r0_90(lambda [] type at line 1040, col. 30 &) = CopyValue : r0_89 # 1040| v0_91(void) = Call : func:r0_82, this:r0_81, 0:r0_90 # 1040| mu0_92(unknown) = ^CallSideEffect : ~mu0_2 -# 1040| v0_93(void) = ^IndirectReadSideEffect[0] : &:r0_90, ~mu0_2 +# 1040| v0_93(void) = ^BufferReadSideEffect[0] : &:r0_90, ~mu0_2 # 1040| mu0_94(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_90 # 1041| r0_95(glval) = VariableAddress[lambda_val_explicit] : # 1041| r0_96(glval) = Convert : r0_95 @@ -4852,7 +4975,7 @@ ir.cpp: # 1041| r0_98(float) = Constant[4.0] : # 1041| r0_99(char) = Call : func:r0_97, this:r0_96, 0:r0_98 # 1041| mu0_100(unknown) = ^CallSideEffect : ~mu0_2 -# 1041| v0_101(void) = ^IndirectReadSideEffect[-1] : &:r0_96, ~mu0_2 +# 1041| v0_101(void) = ^BufferReadSideEffect[-1] : &:r0_96, ~mu0_2 # 1041| mu0_102(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r0_96 # 1042| r0_103(glval) = VariableAddress[lambda_mixed_explicit] : # 1042| r0_104(glval) = VariableAddress[#temp1042:32] : @@ -4875,7 +4998,7 @@ ir.cpp: # 1043| r0_121(float) = Constant[5.0] : # 1043| r0_122(char) = Call : func:r0_120, this:r0_119, 0:r0_121 # 1043| mu0_123(unknown) = ^CallSideEffect : ~mu0_2 -# 1043| v0_124(void) = ^IndirectReadSideEffect[-1] : &:r0_119, ~mu0_2 +# 1043| v0_124(void) = ^BufferReadSideEffect[-1] : &:r0_119, ~mu0_2 # 1043| mu0_125(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r0_119 # 1044| r0_126(glval) = VariableAddress[r] : # 1044| r0_127(glval) = VariableAddress[x] : @@ -4914,12 +5037,13 @@ ir.cpp: # 1046| r0_160(float) = Constant[6.0] : # 1046| r0_161(char) = Call : func:r0_159, this:r0_158, 0:r0_160 # 1046| mu0_162(unknown) = ^CallSideEffect : ~mu0_2 -# 1046| v0_163(void) = ^IndirectReadSideEffect[-1] : &:r0_158, ~mu0_2 +# 1046| v0_163(void) = ^BufferReadSideEffect[-1] : &:r0_158, ~mu0_2 # 1046| mu0_164(decltype([...](...){...})) = ^IndirectMayWriteSideEffect[-1] : &:r0_158 # 1047| v0_165(void) = NoOp : # 1031| v0_166(void) = ReturnVoid : # 1031| v0_167(void) = UnmodeledUse : mu* -# 1031| v0_168(void) = ExitFunction : +# 1031| v0_168(void) = AliasedUse : ~mu0_2 +# 1031| v0_169(void) = ExitFunction : # 1032| void (void Lambda(int, String const&))::(lambda [] type at line 1032, col. 23)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1032, col. 23)&&) # 1032| Block 0 @@ -4932,7 +5056,8 @@ ir.cpp: # 1032| v0_6(void) = NoOp : # 1032| v0_7(void) = ReturnVoid : # 1032| v0_8(void) = UnmodeledUse : mu* -# 1032| v0_9(void) = ExitFunction : +# 1032| v0_9(void) = AliasedUse : ~mu0_2 +# 1032| v0_10(void) = ExitFunction : # 1032| char (void Lambda(int, String const&))::(lambda [] type at line 1032, col. 23)::operator()(float) const # 1032| Block 0 @@ -4948,7 +5073,8 @@ ir.cpp: # 1032| r0_9(glval) = VariableAddress[#return] : # 1032| v0_10(void) = ReturnValue : &:r0_9, ~mu0_2 # 1032| v0_11(void) = UnmodeledUse : mu* -# 1032| v0_12(void) = ExitFunction : +# 1032| v0_12(void) = AliasedUse : ~mu0_2 +# 1032| v0_13(void) = ExitFunction : # 1032| char(* (void Lambda(int, String const&))::(lambda [] type at line 1032, col. 23)::operator char (*)(float)() const)(float) # 1032| Block 0 @@ -4957,12 +5083,13 @@ ir.cpp: # 1032| mu0_2(unknown) = UnmodeledDefinition : # 1032| r0_3(glval) = InitializeThis : # 1032| r0_4(glval<..(*)(..)>) = VariableAddress[#return] : -# 1032| r0_5(glval<..(*)(..)>) = FunctionAddress[_FUN] : +# 1032| r0_5(..(*)(..)) = FunctionAddress[_FUN] : # 1032| mu0_6(..(*)(..)) = Store : &:r0_4, r0_5 # 1032| r0_7(glval<..(*)(..)>) = VariableAddress[#return] : # 1032| v0_8(void) = ReturnValue : &:r0_7, ~mu0_2 # 1032| v0_9(void) = UnmodeledUse : mu* -# 1032| v0_10(void) = ExitFunction : +# 1032| v0_10(void) = AliasedUse : ~mu0_2 +# 1032| v0_11(void) = ExitFunction : # 1034| char (void Lambda(int, String const&))::(lambda [] type at line 1034, col. 21)::operator()(float) const # 1034| Block 0 @@ -4980,7 +5107,7 @@ ir.cpp: # 1034| r0_11(glval) = FunctionAddress[c_str] : # 1034| r0_12(char *) = Call : func:r0_11, this:r0_10 # 1034| mu0_13(unknown) = ^CallSideEffect : ~mu0_2 -# 1034| v0_14(void) = ^IndirectReadSideEffect[-1] : &:r0_10, ~mu0_2 +# 1034| v0_14(void) = ^BufferReadSideEffect[-1] : &:r0_10, ~mu0_2 # 1034| mu0_15(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_10 #-----| r0_16(lambda [] type at line 1034, col. 21 *) = CopyValue : r0_3 #-----| r0_17(glval) = FieldAddress[x] : r0_16 @@ -4992,7 +5119,8 @@ ir.cpp: # 1034| r0_23(glval) = VariableAddress[#return] : # 1034| v0_24(void) = ReturnValue : &:r0_23, ~mu0_2 # 1034| v0_25(void) = UnmodeledUse : mu* -# 1034| v0_26(void) = ExitFunction : +# 1034| v0_26(void) = AliasedUse : ~mu0_2 +# 1034| v0_27(void) = ExitFunction : # 1036| void (void Lambda(int, String const&))::(lambda [] type at line 1036, col. 21)::~() # 1036| Block 0 @@ -5007,7 +5135,8 @@ ir.cpp: # 1036| mu0_8(unknown) = ^CallSideEffect : ~mu0_2 # 1036| v0_9(void) = ReturnVoid : # 1036| v0_10(void) = UnmodeledUse : mu* -# 1036| v0_11(void) = ExitFunction : +# 1036| v0_11(void) = AliasedUse : ~mu0_2 +# 1036| v0_12(void) = ExitFunction : # 1036| char (void Lambda(int, String const&))::(lambda [] type at line 1036, col. 21)::operator()(float) const # 1036| Block 0 @@ -5023,7 +5152,7 @@ ir.cpp: # 1036| r0_9(glval) = FunctionAddress[c_str] : # 1036| r0_10(char *) = Call : func:r0_9, this:r0_8 # 1036| mu0_11(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v0_12(void) = ^IndirectReadSideEffect[-1] : &:r0_8, ~mu0_2 +#-----| v0_12(void) = ^BufferReadSideEffect[-1] : &:r0_8, ~mu0_2 #-----| mu0_13(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_8 #-----| r0_14(lambda [] type at line 1036, col. 21 *) = CopyValue : r0_3 #-----| r0_15(glval) = FieldAddress[x] : r0_14 @@ -5034,7 +5163,8 @@ ir.cpp: # 1036| r0_20(glval) = VariableAddress[#return] : # 1036| v0_21(void) = ReturnValue : &:r0_20, ~mu0_2 # 1036| v0_22(void) = UnmodeledUse : mu* -# 1036| v0_23(void) = ExitFunction : +# 1036| v0_23(void) = AliasedUse : ~mu0_2 +# 1036| v0_24(void) = ExitFunction : # 1038| char (void Lambda(int, String const&))::(lambda [] type at line 1038, col. 30)::operator()(float) const # 1038| Block 0 @@ -5052,7 +5182,7 @@ ir.cpp: # 1038| r0_11(glval) = FunctionAddress[c_str] : # 1038| r0_12(char *) = Call : func:r0_11, this:r0_10 # 1038| mu0_13(unknown) = ^CallSideEffect : ~mu0_2 -# 1038| v0_14(void) = ^IndirectReadSideEffect[-1] : &:r0_10, ~mu0_2 +# 1038| v0_14(void) = ^BufferReadSideEffect[-1] : &:r0_10, ~mu0_2 # 1038| mu0_15(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_10 # 1038| r0_16(int) = Constant[0] : # 1038| r0_17(glval) = PointerAdd[1] : r0_12, r0_16 @@ -5061,7 +5191,8 @@ ir.cpp: # 1038| r0_20(glval) = VariableAddress[#return] : # 1038| v0_21(void) = ReturnValue : &:r0_20, ~mu0_2 # 1038| v0_22(void) = UnmodeledUse : mu* -# 1038| v0_23(void) = ExitFunction : +# 1038| v0_23(void) = AliasedUse : ~mu0_2 +# 1038| v0_24(void) = ExitFunction : # 1040| void (void Lambda(int, String const&))::(lambda [] type at line 1040, col. 30)::(constructor)((void Lambda(int, String const&))::(lambda [] type at line 1040, col. 30)&&) # 1040| Block 0 @@ -5078,7 +5209,8 @@ ir.cpp: # 1040| v0_10(void) = NoOp : # 1040| v0_11(void) = ReturnVoid : # 1040| v0_12(void) = UnmodeledUse : mu* -# 1040| v0_13(void) = ExitFunction : +# 1040| v0_13(void) = AliasedUse : ~mu0_2 +# 1040| v0_14(void) = ExitFunction : # 1040| void (void Lambda(int, String const&))::(lambda [] type at line 1040, col. 30)::~() # 1040| Block 0 @@ -5093,7 +5225,8 @@ ir.cpp: # 1040| mu0_8(unknown) = ^CallSideEffect : ~mu0_2 # 1040| v0_9(void) = ReturnVoid : # 1040| v0_10(void) = UnmodeledUse : mu* -# 1040| v0_11(void) = ExitFunction : +# 1040| v0_11(void) = AliasedUse : ~mu0_2 +# 1040| v0_12(void) = ExitFunction : # 1040| char (void Lambda(int, String const&))::(lambda [] type at line 1040, col. 30)::operator()(float) const # 1040| Block 0 @@ -5109,7 +5242,7 @@ ir.cpp: # 1040| r0_9(glval) = FunctionAddress[c_str] : # 1040| r0_10(char *) = Call : func:r0_9, this:r0_8 # 1040| mu0_11(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v0_12(void) = ^IndirectReadSideEffect[-1] : &:r0_8, ~mu0_2 +#-----| v0_12(void) = ^BufferReadSideEffect[-1] : &:r0_8, ~mu0_2 #-----| mu0_13(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_8 # 1040| r0_14(int) = Constant[0] : # 1040| r0_15(glval) = PointerAdd[1] : r0_10, r0_14 @@ -5118,7 +5251,8 @@ ir.cpp: # 1040| r0_18(glval) = VariableAddress[#return] : # 1040| v0_19(void) = ReturnValue : &:r0_18, ~mu0_2 # 1040| v0_20(void) = UnmodeledUse : mu* -# 1040| v0_21(void) = ExitFunction : +# 1040| v0_21(void) = AliasedUse : ~mu0_2 +# 1040| v0_22(void) = ExitFunction : # 1042| char (void Lambda(int, String const&))::(lambda [] type at line 1042, col. 32)::operator()(float) const # 1042| Block 0 @@ -5136,7 +5270,7 @@ ir.cpp: # 1042| r0_11(glval) = FunctionAddress[c_str] : # 1042| r0_12(char *) = Call : func:r0_11, this:r0_10 # 1042| mu0_13(unknown) = ^CallSideEffect : ~mu0_2 -# 1042| v0_14(void) = ^IndirectReadSideEffect[-1] : &:r0_10, ~mu0_2 +# 1042| v0_14(void) = ^BufferReadSideEffect[-1] : &:r0_10, ~mu0_2 # 1042| mu0_15(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_10 #-----| r0_16(lambda [] type at line 1042, col. 32 *) = CopyValue : r0_3 #-----| r0_17(glval) = FieldAddress[x] : r0_16 @@ -5147,7 +5281,8 @@ ir.cpp: # 1042| r0_22(glval) = VariableAddress[#return] : # 1042| v0_23(void) = ReturnValue : &:r0_22, ~mu0_2 # 1042| v0_24(void) = UnmodeledUse : mu* -# 1042| v0_25(void) = ExitFunction : +# 1042| v0_25(void) = AliasedUse : ~mu0_2 +# 1042| v0_26(void) = ExitFunction : # 1045| char (void Lambda(int, String const&))::(lambda [] type at line 1045, col. 23)::operator()(float) const # 1045| Block 0 @@ -5165,7 +5300,7 @@ ir.cpp: # 1045| r0_11(glval) = FunctionAddress[c_str] : # 1045| r0_12(char *) = Call : func:r0_11, this:r0_10 # 1045| mu0_13(unknown) = ^CallSideEffect : ~mu0_2 -# 1045| v0_14(void) = ^IndirectReadSideEffect[-1] : &:r0_10, ~mu0_2 +# 1045| v0_14(void) = ^BufferReadSideEffect[-1] : &:r0_10, ~mu0_2 # 1045| mu0_15(String) = ^IndirectMayWriteSideEffect[-1] : &:r0_10 #-----| r0_16(lambda [] type at line 1045, col. 23 *) = CopyValue : r0_3 #-----| r0_17(glval) = FieldAddress[x] : r0_16 @@ -5185,7 +5320,8 @@ ir.cpp: # 1045| r0_31(glval) = VariableAddress[#return] : # 1045| v0_32(void) = ReturnValue : &:r0_31, ~mu0_2 # 1045| v0_33(void) = UnmodeledUse : mu* -# 1045| v0_34(void) = ExitFunction : +# 1045| v0_34(void) = AliasedUse : ~mu0_2 +# 1045| v0_35(void) = ExitFunction : # 1068| void RangeBasedFor(vector const&) # 1068| Block 0 @@ -5207,7 +5343,7 @@ ir.cpp: # 1069| r0_15(glval) = FunctionAddress[begin] : # 1069| r0_16(iterator) = Call : func:r0_15, this:r0_14 # 1069| mu0_17(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v0_18(void) = ^IndirectReadSideEffect[-1] : &:r0_14, ~mu0_2 +#-----| v0_18(void) = ^BufferReadSideEffect[-1] : &:r0_14, ~mu0_2 #-----| mu0_19(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_14 # 1069| mu0_20(iterator) = Store : &:r0_11, r0_16 # 1069| r0_21(glval) = VariableAddress[(__end)] : @@ -5217,7 +5353,7 @@ ir.cpp: # 1069| r0_25(glval) = FunctionAddress[end] : # 1069| r0_26(iterator) = Call : func:r0_25, this:r0_24 # 1069| mu0_27(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v0_28(void) = ^IndirectReadSideEffect[-1] : &:r0_24, ~mu0_2 +#-----| v0_28(void) = ^BufferReadSideEffect[-1] : &:r0_24, ~mu0_2 #-----| mu0_29(vector) = ^IndirectMayWriteSideEffect[-1] : &:r0_24 # 1069| mu0_30(iterator) = Store : &:r0_21, r0_26 #-----| Goto -> Block 6 @@ -5230,7 +5366,7 @@ ir.cpp: #-----| r1_4(iterator) = Load : &:r1_3, ~mu0_2 # 1075| r1_5(bool) = Call : func:r1_2, this:r1_1, 0:r1_4 # 1075| mu1_6(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v1_7(void) = ^IndirectReadSideEffect[-1] : &:r1_1, ~mu0_2 +#-----| v1_7(void) = ^BufferReadSideEffect[-1] : &:r1_1, ~mu0_2 #-----| mu1_8(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r1_1 # 1075| v1_9(void) = ConditionalBranch : r1_5 #-----| False -> Block 5 @@ -5241,7 +5377,7 @@ ir.cpp: # 1075| r2_1(glval) = FunctionAddress[operator++] : # 1075| r2_2(iterator &) = Call : func:r2_1, this:r2_0 # 1075| mu2_3(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v2_4(void) = ^IndirectReadSideEffect[-1] : &:r2_0, ~mu0_2 +#-----| v2_4(void) = ^BufferReadSideEffect[-1] : &:r2_0, ~mu0_2 #-----| mu2_5(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r2_0 # 1075| r2_6(glval) = CopyValue : r2_2 #-----| Goto (back edge) -> Block 1 @@ -5253,7 +5389,7 @@ ir.cpp: # 1075| r3_3(glval) = FunctionAddress[operator*] : # 1075| r3_4(int &) = Call : func:r3_3, this:r3_2 # 1075| mu3_5(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v3_6(void) = ^IndirectReadSideEffect[-1] : &:r3_2, ~mu0_2 +#-----| v3_6(void) = ^BufferReadSideEffect[-1] : &:r3_2, ~mu0_2 #-----| mu3_7(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r3_2 # 1075| r3_8(glval) = CopyValue : r3_4 # 1075| r3_9(glval) = Convert : r3_8 @@ -5277,7 +5413,8 @@ ir.cpp: # 1080| v5_1(void) = NoOp : # 1068| v5_2(void) = ReturnVoid : # 1068| v5_3(void) = UnmodeledUse : mu* -# 1068| v5_4(void) = ExitFunction : +# 1068| v5_4(void) = AliasedUse : ~mu0_2 +# 1068| v5_5(void) = ExitFunction : #-----| Block 6 #-----| r6_0(glval) = VariableAddress[(__begin)] : @@ -5287,7 +5424,7 @@ ir.cpp: #-----| r6_4(iterator) = Load : &:r6_3, ~mu0_2 # 1069| r6_5(bool) = Call : func:r6_2, this:r6_1, 0:r6_4 # 1069| mu6_6(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v6_7(void) = ^IndirectReadSideEffect[-1] : &:r6_1, ~mu0_2 +#-----| v6_7(void) = ^BufferReadSideEffect[-1] : &:r6_1, ~mu0_2 #-----| mu6_8(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r6_1 # 1069| v6_9(void) = ConditionalBranch : r6_5 #-----| False -> Block 10 @@ -5300,7 +5437,7 @@ ir.cpp: # 1069| r7_3(glval) = FunctionAddress[operator*] : # 1069| r7_4(int &) = Call : func:r7_3, this:r7_2 # 1069| mu7_5(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v7_6(void) = ^IndirectReadSideEffect[-1] : &:r7_2, ~mu0_2 +#-----| v7_6(void) = ^BufferReadSideEffect[-1] : &:r7_2, ~mu0_2 #-----| mu7_7(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r7_2 # 1069| r7_8(int) = Load : &:r7_4, ~mu0_2 # 1069| mu7_9(int) = Store : &:r7_0, r7_8 @@ -5322,7 +5459,7 @@ ir.cpp: # 1069| r9_2(glval) = FunctionAddress[operator++] : # 1069| r9_3(iterator &) = Call : func:r9_2, this:r9_1 # 1069| mu9_4(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v9_5(void) = ^IndirectReadSideEffect[-1] : &:r9_1, ~mu0_2 +#-----| v9_5(void) = ^BufferReadSideEffect[-1] : &:r9_1, ~mu0_2 #-----| mu9_6(iterator) = ^IndirectMayWriteSideEffect[-1] : &:r9_1 # 1069| r9_7(glval) = CopyValue : r9_3 #-----| Goto (back edge) -> Block 6 @@ -5341,7 +5478,7 @@ ir.cpp: # 1075| r10_10(glval) = FunctionAddress[begin] : # 1075| r10_11(iterator) = Call : func:r10_10, this:r10_9 # 1075| mu10_12(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v10_13(void) = ^IndirectReadSideEffect[-1] : &:r10_9, ~mu0_2 +#-----| v10_13(void) = ^BufferReadSideEffect[-1] : &:r10_9, ~mu0_2 #-----| mu10_14(vector) = ^IndirectMayWriteSideEffect[-1] : &:r10_9 # 1075| mu10_15(iterator) = Store : &:r10_6, r10_11 # 1075| r10_16(glval) = VariableAddress[(__end)] : @@ -5351,7 +5488,7 @@ ir.cpp: # 1075| r10_20(glval) = FunctionAddress[end] : # 1075| r10_21(iterator) = Call : func:r10_20, this:r10_19 # 1075| mu10_22(unknown) = ^CallSideEffect : ~mu0_2 -#-----| v10_23(void) = ^IndirectReadSideEffect[-1] : &:r10_19, ~mu0_2 +#-----| v10_23(void) = ^BufferReadSideEffect[-1] : &:r10_19, ~mu0_2 #-----| mu10_24(vector) = ^IndirectMayWriteSideEffect[-1] : &:r10_19 # 1075| mu10_25(iterator) = Store : &:r10_16, r10_21 #-----| Goto -> Block 1 @@ -5371,30 +5508,37 @@ ir.cpp: # 1099| r0_10(glval) = VariableAddress[#return] : # 1099| v0_11(void) = ReturnValue : &:r0_10, ~mu0_2 # 1099| v0_12(void) = UnmodeledUse : mu* -# 1099| v0_13(void) = ExitFunction : +# 1099| v0_13(void) = AliasedUse : ~mu0_2 +# 1099| v0_14(void) = ExitFunction : -# 1104| void AsmStmtWithOutputs(unsigned int&, unsigned int&, unsigned int&, unsigned int&) +# 1104| void AsmStmtWithOutputs(unsigned int&, unsigned int, unsigned int&, unsigned int) # 1104| Block 0 # 1104| v0_0(void) = EnterFunction : # 1104| mu0_1(unknown) = AliasedDefinition : # 1104| mu0_2(unknown) = UnmodeledDefinition : # 1104| r0_3(glval) = VariableAddress[a] : # 1104| mu0_4(unsigned int &) = InitializeParameter[a] : &:r0_3 -# 1104| r0_5(glval) = VariableAddress[b] : -# 1104| mu0_6(unsigned int &) = InitializeParameter[b] : &:r0_5 +# 1104| r0_5(glval) = VariableAddress[b] : +# 1104| mu0_6(unsigned int) = InitializeParameter[b] : &:r0_5 # 1104| r0_7(glval) = VariableAddress[c] : # 1104| mu0_8(unsigned int &) = InitializeParameter[c] : &:r0_7 -# 1104| r0_9(glval) = VariableAddress[d] : -# 1104| mu0_10(unsigned int &) = InitializeParameter[d] : &:r0_9 -# 1106| r0_11(glval) = VariableAddress[a] : -# 1106| r0_12(glval) = VariableAddress[b] : -# 1106| r0_13(glval) = VariableAddress[c] : -# 1106| r0_14(glval) = VariableAddress[d] : -# 1106| mu0_15(unknown) = InlineAsm : ~mu0_2, 0:r0_11, 1:r0_12, 2:r0_13, 3:r0_14 -# 1111| v0_16(void) = NoOp : -# 1104| v0_17(void) = ReturnVoid : -# 1104| v0_18(void) = UnmodeledUse : mu* -# 1104| v0_19(void) = ExitFunction : +# 1104| r0_9(glval) = VariableAddress[d] : +# 1104| mu0_10(unsigned int) = InitializeParameter[d] : &:r0_9 +# 1109| r0_11(glval) = VariableAddress[a] : +# 1109| r0_12(unsigned int &) = Load : &:r0_11, ~mu0_2 +# 1109| r0_13(glval) = CopyValue : r0_12 +# 1109| r0_14(glval) = VariableAddress[b] : +# 1109| r0_15(glval) = VariableAddress[c] : +# 1109| r0_16(unsigned int &) = Load : &:r0_15, ~mu0_2 +# 1109| r0_17(unsigned int) = Load : &:r0_16, ~mu0_2 +# 1109| r0_18(glval) = VariableAddress[d] : +# 1109| r0_19(unsigned int) = Load : &:r0_18, ~mu0_2 +# 1106| mu0_20(unknown) = InlineAsm : ~mu0_2, 0:r0_13, 1:r0_14, 2:r0_17, 3:r0_19 +# 1111| v0_21(void) = NoOp : +# 1104| v0_22(void) = ReturnVoid : +# 1104| v0_23(void) = UnmodeledUse : mu* +# 1104| v0_24(void) = AliasedUse : ~mu0_2 +# 1104| v0_25(void) = ExitFunction : # 1113| void ExternDeclarations() # 1113| Block 0 @@ -5410,7 +5554,8 @@ ir.cpp: # 1120| v0_9(void) = NoOp : # 1113| v0_10(void) = ReturnVoid : # 1113| v0_11(void) = UnmodeledUse : mu* -# 1113| v0_12(void) = ExitFunction : +# 1113| v0_12(void) = AliasedUse : ~mu0_2 +# 1113| v0_13(void) = ExitFunction : # 1128| void ExternDeclarationsInMacro() # 1128| Block 0 @@ -5444,7 +5589,8 @@ ir.cpp: # 1131| v3_1(void) = NoOp : # 1128| v3_2(void) = ReturnVoid : # 1128| v3_3(void) = UnmodeledUse : mu* -# 1128| v3_4(void) = ExitFunction : +# 1128| v3_4(void) = AliasedUse : ~mu0_2 +# 1128| v3_5(void) = ExitFunction : # 1133| void TryCatchNoCatchAny(bool) # 1133| Block 0 @@ -5464,7 +5610,8 @@ ir.cpp: # 1133| Block 1 # 1133| v1_0(void) = UnmodeledUse : mu* -# 1133| v1_1(void) = ExitFunction : +# 1133| v1_1(void) = AliasedUse : ~mu0_2 +# 1133| v1_2(void) = ExitFunction : # 1133| Block 2 # 1133| v2_0(void) = Unwind : @@ -5511,7 +5658,7 @@ ir.cpp: # 1140| r7_3(char *) = Convert : r7_2 # 1140| v7_4(void) = Call : func:r7_1, this:r7_0, 0:r7_3 # 1140| mu7_5(unknown) = ^CallSideEffect : ~mu0_2 -# 1140| v7_6(void) = ^IndirectReadSideEffect[0] : &:r7_3, ~mu0_2 +# 1140| v7_6(void) = ^BufferReadSideEffect[0] : &:r7_3, ~mu0_2 # 1140| mu7_7(unknown) = ^BufferMayWriteSideEffect[0] : &:r7_3 # 1140| v7_8(void) = ThrowValue : &:r7_0, ~mu0_2 #-----| Exception -> Block 9 @@ -5536,7 +5683,7 @@ ir.cpp: # 1145| r10_5(char *) = Load : &:r10_4, ~mu0_2 # 1145| v10_6(void) = Call : func:r10_3, this:r10_2, 0:r10_5 # 1145| mu10_7(unknown) = ^CallSideEffect : ~mu0_2 -# 1145| v10_8(void) = ^IndirectReadSideEffect[0] : &:r10_5, ~mu0_2 +# 1145| v10_8(void) = ^BufferReadSideEffect[0] : &:r10_5, ~mu0_2 # 1145| mu10_9(unknown) = ^BufferMayWriteSideEffect[0] : &:r10_5 # 1145| v10_10(void) = ThrowValue : &:r10_2, ~mu0_2 #-----| Exception -> Block 2 @@ -5617,7 +5764,8 @@ ir.cpp: # 1159| v0_55(void) = NoOp : # 1153| v0_56(void) = ReturnVoid : # 1153| v0_57(void) = UnmodeledUse : mu* -# 1153| v0_58(void) = ExitFunction : +# 1153| v0_58(void) = AliasedUse : ~mu0_2 +# 1153| v0_59(void) = ExitFunction : # 1163| int ModeledCallTarget(int) # 1163| Block 0 @@ -5646,7 +5794,8 @@ ir.cpp: # 1163| r0_22(glval) = VariableAddress[#return] : # 1163| v0_23(void) = ReturnValue : &:r0_22, ~mu0_2 # 1163| v0_24(void) = UnmodeledUse : mu* -# 1163| v0_25(void) = ExitFunction : +# 1163| v0_25(void) = AliasedUse : ~mu0_2 +# 1163| v0_26(void) = ExitFunction : perf-regression.cpp: # 6| void Big::Big() @@ -5663,7 +5812,8 @@ perf-regression.cpp: # 6| v0_9(void) = NoOp : # 6| v0_10(void) = ReturnVoid : # 6| v0_11(void) = UnmodeledUse : mu* -# 6| v0_12(void) = ExitFunction : +# 6| v0_12(void) = AliasedUse : ~mu0_2 +# 6| v0_13(void) = ExitFunction : # 9| int main() # 9| Block 0 @@ -5686,4 +5836,5 @@ perf-regression.cpp: # 9| r0_16(glval) = VariableAddress[#return] : # 9| v0_17(void) = ReturnValue : &:r0_16, ~mu0_2 # 9| v0_18(void) = UnmodeledUse : mu* -# 9| v0_19(void) = ExitFunction : +# 9| v0_19(void) = AliasedUse : ~mu0_2 +# 9| v0_20(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected b/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected index 012ff53039b..928f535f9d8 100644 --- a/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/raw_sanity.expected @@ -14,3 +14,8 @@ containsLoopOfForwardEdges lostReachability backEdgeCountMismatch useNotDominatedByDefinition +missingCanonicalLanguageType +multipleCanonicalLanguageTypes +missingIRType +multipleIRTypes +missingCppType diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected index 012ff53039b..928f535f9d8 100644 --- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected @@ -14,3 +14,8 @@ containsLoopOfForwardEdges lostReachability backEdgeCountMismatch useNotDominatedByDefinition +missingCanonicalLanguageType +multipleCanonicalLanguageTypes +missingIRType +multipleIRTypes +missingCppType diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected index ff8c2b30a05..46f8cf19aea 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_ir.expected @@ -84,7 +84,8 @@ ssa.cpp: # 13| r6_12(glval) = VariableAddress[#return] : # 13| v6_13(void) = ReturnValue : &:r6_12, m6_11 # 13| v6_14(void) = UnmodeledUse : mu* -# 13| v6_15(void) = ExitFunction : +# 13| v6_15(void) = AliasedUse : ~m6_0 +# 13| v6_16(void) = ExitFunction : # 31| int UnreachableViaGoto() # 31| Block 0 @@ -99,7 +100,8 @@ ssa.cpp: # 31| r0_8(glval) = VariableAddress[#return] : # 31| v0_9(void) = ReturnValue : &:r0_8, m0_7 # 31| v0_10(void) = UnmodeledUse : mu* -# 31| v0_11(void) = ExitFunction : +# 31| v0_11(void) = AliasedUse : ~m0_1 +# 31| v0_12(void) = ExitFunction : # 38| int UnreachableIf(bool) # 38| Block 0 @@ -125,7 +127,8 @@ ssa.cpp: # 38| r1_1(glval) = VariableAddress[#return] : # 38| v1_2(void) = ReturnValue : &:r1_1, m1_0 # 38| v1_3(void) = UnmodeledUse : mu* -# 38| v1_4(void) = ExitFunction : +# 38| v1_4(void) = AliasedUse : ~m0_1 +# 38| v1_5(void) = ExitFunction : # 42| Block 2 # 42| r2_0(glval) = VariableAddress[x] : @@ -188,7 +191,8 @@ ssa.cpp: # 59| r1_4(glval) = VariableAddress[#return] : # 59| v1_5(void) = ReturnValue : &:r1_4, m1_3 # 59| v1_6(void) = UnmodeledUse : mu* -# 59| v1_7(void) = ExitFunction : +# 59| v1_7(void) = AliasedUse : ~m0_1 +# 59| v1_8(void) = ExitFunction : # 59| Block 2 # 59| v2_0(void) = Unreached : @@ -220,7 +224,8 @@ ssa.cpp: # 71| v2_0(void) = NoOp : # 68| v2_1(void) = ReturnVoid : # 68| v2_2(void) = UnmodeledUse : mu* -# 68| v2_3(void) = ExitFunction : +# 68| v2_3(void) = AliasedUse : ~m3_0 +# 68| v2_4(void) = ExitFunction : # 69| Block 3 # 69| m3_0(unknown) = Phi : from 0:~m0_1, from 1:~m1_8 @@ -292,7 +297,8 @@ ssa.cpp: # 89| v3_14(void) = NoOp : # 75| v3_15(void) = ReturnVoid : # 75| v3_16(void) = UnmodeledUse : mu* -# 75| v3_17(void) = ExitFunction : +# 75| v3_17(void) = AliasedUse : ~m0_1 +# 75| v3_18(void) = ExitFunction : # 91| void MustExactlyOverlap(Point) # 91| Block 0 @@ -308,7 +314,8 @@ ssa.cpp: # 93| v0_9(void) = NoOp : # 91| v0_10(void) = ReturnVoid : # 91| v0_11(void) = UnmodeledUse : mu* -# 91| v0_12(void) = ExitFunction : +# 91| v0_12(void) = AliasedUse : ~m0_1 +# 91| v0_13(void) = ExitFunction : # 95| void MustExactlyOverlapEscaped(Point) # 95| Block 0 @@ -329,13 +336,14 @@ ssa.cpp: # 97| v0_14(void) = Call : func:r0_10, 0:r0_13 # 97| m0_15(unknown) = ^CallSideEffect : ~m0_5 # 97| m0_16(unknown) = Chi : total:m0_5, partial:m0_15 -# 97| v0_17(void) = ^IndirectReadSideEffect[0] : &:r0_13, ~m0_16 +# 97| v0_17(void) = ^BufferReadSideEffect[0] : &:r0_13, ~m0_16 # 97| m0_18(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_13 # 97| m0_19(unknown) = Chi : total:m0_16, partial:m0_18 # 98| v0_20(void) = NoOp : # 95| v0_21(void) = ReturnVoid : # 95| v0_22(void) = UnmodeledUse : mu* -# 95| v0_23(void) = ExitFunction : +# 95| v0_23(void) = AliasedUse : ~m0_16 +# 95| v0_24(void) = ExitFunction : # 100| void MustTotallyOverlap(Point) # 100| Block 0 @@ -357,7 +365,8 @@ ssa.cpp: # 103| v0_15(void) = NoOp : # 100| v0_16(void) = ReturnVoid : # 100| v0_17(void) = UnmodeledUse : mu* -# 100| v0_18(void) = ExitFunction : +# 100| v0_18(void) = AliasedUse : ~m0_1 +# 100| v0_19(void) = ExitFunction : # 105| void MustTotallyOverlapEscaped(Point) # 105| Block 0 @@ -384,13 +393,14 @@ ssa.cpp: # 108| v0_20(void) = Call : func:r0_16, 0:r0_19 # 108| m0_21(unknown) = ^CallSideEffect : ~m0_5 # 108| m0_22(unknown) = Chi : total:m0_5, partial:m0_21 -# 108| v0_23(void) = ^IndirectReadSideEffect[0] : &:r0_19, ~m0_22 +# 108| v0_23(void) = ^BufferReadSideEffect[0] : &:r0_19, ~m0_22 # 108| m0_24(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_19 # 108| m0_25(unknown) = Chi : total:m0_22, partial:m0_24 # 109| v0_26(void) = NoOp : # 105| v0_27(void) = ReturnVoid : # 105| v0_28(void) = UnmodeledUse : mu* -# 105| v0_29(void) = ExitFunction : +# 105| v0_29(void) = AliasedUse : ~m0_22 +# 105| v0_30(void) = ExitFunction : # 111| void MayPartiallyOverlap(int, int) # 111| Block 0 @@ -420,7 +430,8 @@ ssa.cpp: # 114| v0_23(void) = NoOp : # 111| v0_24(void) = ReturnVoid : # 111| v0_25(void) = UnmodeledUse : mu* -# 111| v0_26(void) = ExitFunction : +# 111| v0_26(void) = AliasedUse : ~m0_1 +# 111| v0_27(void) = ExitFunction : # 116| void MayPartiallyOverlapEscaped(int, int) # 116| Block 0 @@ -455,13 +466,14 @@ ssa.cpp: # 119| v0_28(void) = Call : func:r0_24, 0:r0_27 # 119| m0_29(unknown) = ^CallSideEffect : ~m0_19 # 119| m0_30(unknown) = Chi : total:m0_19, partial:m0_29 -# 119| v0_31(void) = ^IndirectReadSideEffect[0] : &:r0_27, ~m0_30 +# 119| v0_31(void) = ^BufferReadSideEffect[0] : &:r0_27, ~m0_30 # 119| m0_32(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_27 # 119| m0_33(unknown) = Chi : total:m0_30, partial:m0_32 # 120| v0_34(void) = NoOp : # 116| v0_35(void) = ReturnVoid : # 116| v0_36(void) = UnmodeledUse : mu* -# 116| v0_37(void) = ExitFunction : +# 116| v0_37(void) = AliasedUse : ~m0_30 +# 116| v0_38(void) = ExitFunction : # 122| void MergeMustExactlyOverlap(bool, int, int) # 122| Block 0 @@ -523,7 +535,8 @@ ssa.cpp: # 132| v3_11(void) = NoOp : # 122| v3_12(void) = ReturnVoid : # 122| v3_13(void) = UnmodeledUse : mu* -# 122| v3_14(void) = ExitFunction : +# 122| v3_14(void) = AliasedUse : ~m0_1 +# 122| v3_15(void) = ExitFunction : # 134| void MergeMustExactlyWithMustTotallyOverlap(bool, Point, int) # 134| Block 0 @@ -579,7 +592,8 @@ ssa.cpp: # 143| v3_7(void) = NoOp : # 134| v3_8(void) = ReturnVoid : # 134| v3_9(void) = UnmodeledUse : mu* -# 134| v3_10(void) = ExitFunction : +# 134| v3_10(void) = AliasedUse : ~m0_1 +# 134| v3_11(void) = ExitFunction : # 145| void MergeMustExactlyWithMayPartiallyOverlap(bool, Point, int) # 145| Block 0 @@ -633,7 +647,8 @@ ssa.cpp: # 154| v3_5(void) = NoOp : # 145| v3_6(void) = ReturnVoid : # 145| v3_7(void) = UnmodeledUse : mu* -# 145| v3_8(void) = ExitFunction : +# 145| v3_8(void) = AliasedUse : ~m0_1 +# 145| v3_9(void) = ExitFunction : # 156| void MergeMustTotallyOverlapWithMayPartiallyOverlap(bool, Rect, int) # 156| Block 0 @@ -689,7 +704,8 @@ ssa.cpp: # 165| v3_6(void) = NoOp : # 156| v3_7(void) = ReturnVoid : # 156| v3_8(void) = UnmodeledUse : mu* -# 156| v3_9(void) = ExitFunction : +# 156| v3_9(void) = AliasedUse : ~m0_1 +# 156| v3_10(void) = ExitFunction : # 171| void WrapperStruct(Wrapper) # 171| Block 0 @@ -723,7 +739,8 @@ ssa.cpp: # 177| v0_27(void) = NoOp : # 171| v0_28(void) = ReturnVoid : # 171| v0_29(void) = UnmodeledUse : mu* -# 171| v0_30(void) = ExitFunction : +# 171| v0_30(void) = AliasedUse : ~m0_1 +# 171| v0_31(void) = ExitFunction : # 179| int AsmStmt(int*) # 179| Block 0 @@ -742,7 +759,8 @@ ssa.cpp: # 179| r0_12(glval) = VariableAddress[#return] : # 179| v0_13(void) = ReturnValue : &:r0_12, m0_11 # 179| v0_14(void) = UnmodeledUse : mu* -# 179| v0_15(void) = ExitFunction : +# 179| v0_15(void) = AliasedUse : ~m0_6 +# 179| v0_16(void) = ExitFunction : # 184| void AsmStmtWithOutputs(unsigned int&, unsigned int&, unsigned int&, unsigned int&) # 184| Block 0 @@ -751,26 +769,31 @@ ssa.cpp: # 184| mu0_2(unknown) = UnmodeledDefinition : # 184| r0_3(glval) = VariableAddress[a] : # 184| m0_4(unsigned int &) = InitializeParameter[a] : &:r0_3 -# 184| m0_5(unknown) = Chi : total:m0_1, partial:m0_4 -# 184| r0_6(glval) = VariableAddress[b] : -# 184| m0_7(unsigned int &) = InitializeParameter[b] : &:r0_6 -# 184| m0_8(unknown) = Chi : total:m0_5, partial:m0_7 -# 184| r0_9(glval) = VariableAddress[c] : -# 184| m0_10(unsigned int &) = InitializeParameter[c] : &:r0_9 -# 184| m0_11(unknown) = Chi : total:m0_8, partial:m0_10 -# 184| r0_12(glval) = VariableAddress[d] : -# 184| m0_13(unsigned int &) = InitializeParameter[d] : &:r0_12 -# 184| m0_14(unknown) = Chi : total:m0_11, partial:m0_13 -# 186| r0_15(glval) = VariableAddress[a] : -# 186| r0_16(glval) = VariableAddress[b] : -# 186| r0_17(glval) = VariableAddress[c] : -# 186| r0_18(glval) = VariableAddress[d] : -# 186| m0_19(unknown) = InlineAsm : ~mu0_2, 0:r0_15, 1:r0_16, 2:r0_17, 3:r0_18 -# 186| m0_20(unknown) = Chi : total:m0_14, partial:m0_19 -# 192| v0_21(void) = NoOp : -# 184| v0_22(void) = ReturnVoid : -# 184| v0_23(void) = UnmodeledUse : mu* -# 184| v0_24(void) = ExitFunction : +# 184| r0_5(glval) = VariableAddress[b] : +# 184| m0_6(unsigned int &) = InitializeParameter[b] : &:r0_5 +# 184| r0_7(glval) = VariableAddress[c] : +# 184| m0_8(unsigned int &) = InitializeParameter[c] : &:r0_7 +# 184| r0_9(glval) = VariableAddress[d] : +# 184| m0_10(unsigned int &) = InitializeParameter[d] : &:r0_9 +# 189| r0_11(glval) = VariableAddress[a] : +# 189| r0_12(unsigned int &) = Load : &:r0_11, m0_4 +# 189| r0_13(glval) = CopyValue : r0_12 +# 189| r0_14(glval) = VariableAddress[b] : +# 189| r0_15(unsigned int &) = Load : &:r0_14, m0_6 +# 189| r0_16(glval) = CopyValue : r0_15 +# 190| r0_17(glval) = VariableAddress[c] : +# 190| r0_18(unsigned int &) = Load : &:r0_17, m0_8 +# 190| r0_19(unsigned int) = Load : &:r0_18, ~m0_1 +# 190| r0_20(glval) = VariableAddress[d] : +# 190| r0_21(unsigned int &) = Load : &:r0_20, m0_10 +# 190| r0_22(unsigned int) = Load : &:r0_21, ~m0_1 +# 186| m0_23(unknown) = InlineAsm : ~mu0_2, 0:r0_13, 1:r0_16, 2:r0_19, 3:r0_22 +# 186| m0_24(unknown) = Chi : total:m0_1, partial:m0_23 +# 192| v0_25(void) = NoOp : +# 184| v0_26(void) = ReturnVoid : +# 184| v0_27(void) = UnmodeledUse : mu* +# 184| v0_28(void) = AliasedUse : ~m0_24 +# 184| v0_29(void) = ExitFunction : # 198| int PureFunctions(char*, char*, int) # 198| Block 0 @@ -819,7 +842,8 @@ ssa.cpp: # 198| r0_42(glval) = VariableAddress[#return] : # 198| v0_43(void) = ReturnValue : &:r0_42, m0_41 # 198| v0_44(void) = UnmodeledUse : mu* -# 198| v0_45(void) = ExitFunction : +# 198| v0_45(void) = AliasedUse : ~m0_1 +# 198| v0_46(void) = ExitFunction : # 207| int ModeledCallTarget(int) # 207| Block 0 @@ -851,4 +875,5 @@ ssa.cpp: # 207| r0_25(glval) = VariableAddress[#return] : # 207| v0_26(void) = ReturnValue : &:r0_25, m0_24 # 207| v0_27(void) = UnmodeledUse : mu* -# 207| v0_28(void) = ExitFunction : +# 207| v0_28(void) = AliasedUse : ~m0_1 +# 207| v0_29(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity.expected index 012ff53039b..928f535f9d8 100644 --- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_sanity.expected @@ -14,3 +14,8 @@ containsLoopOfForwardEdges lostReachability backEdgeCountMismatch useNotDominatedByDefinition +missingCanonicalLanguageType +multipleCanonicalLanguageTypes +missingIRType +multipleIRTypes +missingCppType diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected index 8204cdad960..7aae6b70dfd 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_ir.expected @@ -78,7 +78,8 @@ ssa.cpp: # 13| r6_11(glval) = VariableAddress[#return] : # 13| v6_12(void) = ReturnValue : &:r6_11, m6_10 # 13| v6_13(void) = UnmodeledUse : mu* -# 13| v6_14(void) = ExitFunction : +# 13| v6_14(void) = AliasedUse : ~mu0_2 +# 13| v6_15(void) = ExitFunction : # 31| int UnreachableViaGoto() # 31| Block 0 @@ -93,7 +94,8 @@ ssa.cpp: # 31| r0_8(glval) = VariableAddress[#return] : # 31| v0_9(void) = ReturnValue : &:r0_8, m0_7 # 31| v0_10(void) = UnmodeledUse : mu* -# 31| v0_11(void) = ExitFunction : +# 31| v0_11(void) = AliasedUse : ~mu0_2 +# 31| v0_12(void) = ExitFunction : # 38| int UnreachableIf(bool) # 38| Block 0 @@ -119,7 +121,8 @@ ssa.cpp: # 38| r1_1(glval) = VariableAddress[#return] : # 38| v1_2(void) = ReturnValue : &:r1_1, m1_0 # 38| v1_3(void) = UnmodeledUse : mu* -# 38| v1_4(void) = ExitFunction : +# 38| v1_4(void) = AliasedUse : ~mu0_2 +# 38| v1_5(void) = ExitFunction : # 42| Block 2 # 42| r2_0(glval) = VariableAddress[x] : @@ -191,7 +194,8 @@ ssa.cpp: # 59| r1_4(glval) = VariableAddress[#return] : # 59| v1_5(void) = ReturnValue : &:r1_4, m1_3 # 59| v1_6(void) = UnmodeledUse : mu* -# 59| v1_7(void) = ExitFunction : +# 59| v1_7(void) = AliasedUse : ~mu0_2 +# 59| v1_8(void) = ExitFunction : # 59| Block 2 # 59| v2_0(void) = Unreached : @@ -222,7 +226,8 @@ ssa.cpp: # 71| v2_0(void) = NoOp : # 68| v2_1(void) = ReturnVoid : # 68| v2_2(void) = UnmodeledUse : mu* -# 68| v2_3(void) = ExitFunction : +# 68| v2_3(void) = AliasedUse : ~mu0_2 +# 68| v2_4(void) = ExitFunction : # 69| Block 3 # 69| m3_0(int) = Phi : from 0:m0_4, from 1:m3_6 @@ -293,7 +298,8 @@ ssa.cpp: # 89| v3_14(void) = NoOp : # 75| v3_15(void) = ReturnVoid : # 75| v3_16(void) = UnmodeledUse : mu* -# 75| v3_17(void) = ExitFunction : +# 75| v3_17(void) = AliasedUse : ~mu0_2 +# 75| v3_18(void) = ExitFunction : # 91| void MustExactlyOverlap(Point) # 91| Block 0 @@ -309,7 +315,8 @@ ssa.cpp: # 93| v0_9(void) = NoOp : # 91| v0_10(void) = ReturnVoid : # 91| v0_11(void) = UnmodeledUse : mu* -# 91| v0_12(void) = ExitFunction : +# 91| v0_12(void) = AliasedUse : ~mu0_2 +# 91| v0_13(void) = ExitFunction : # 95| void MustExactlyOverlapEscaped(Point) # 95| Block 0 @@ -328,12 +335,13 @@ ssa.cpp: # 97| r0_12(void *) = Convert : r0_11 # 97| v0_13(void) = Call : func:r0_9, 0:r0_12 # 97| mu0_14(unknown) = ^CallSideEffect : ~mu0_2 -# 97| v0_15(void) = ^IndirectReadSideEffect[0] : &:r0_12, ~mu0_2 +# 97| v0_15(void) = ^BufferReadSideEffect[0] : &:r0_12, ~mu0_2 # 97| mu0_16(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_12 # 98| v0_17(void) = NoOp : # 95| v0_18(void) = ReturnVoid : # 95| v0_19(void) = UnmodeledUse : mu* -# 95| v0_20(void) = ExitFunction : +# 95| v0_20(void) = AliasedUse : ~mu0_2 +# 95| v0_21(void) = ExitFunction : # 100| void MustTotallyOverlap(Point) # 100| Block 0 @@ -355,7 +363,8 @@ ssa.cpp: # 103| v0_15(void) = NoOp : # 100| v0_16(void) = ReturnVoid : # 100| v0_17(void) = UnmodeledUse : mu* -# 100| v0_18(void) = ExitFunction : +# 100| v0_18(void) = AliasedUse : ~mu0_2 +# 100| v0_19(void) = ExitFunction : # 105| void MustTotallyOverlapEscaped(Point) # 105| Block 0 @@ -380,12 +389,13 @@ ssa.cpp: # 108| r0_18(void *) = Convert : r0_17 # 108| v0_19(void) = Call : func:r0_15, 0:r0_18 # 108| mu0_20(unknown) = ^CallSideEffect : ~mu0_2 -# 108| v0_21(void) = ^IndirectReadSideEffect[0] : &:r0_18, ~mu0_2 +# 108| v0_21(void) = ^BufferReadSideEffect[0] : &:r0_18, ~mu0_2 # 108| mu0_22(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_18 # 109| v0_23(void) = NoOp : # 105| v0_24(void) = ReturnVoid : # 105| v0_25(void) = UnmodeledUse : mu* -# 105| v0_26(void) = ExitFunction : +# 105| v0_26(void) = AliasedUse : ~mu0_2 +# 105| v0_27(void) = ExitFunction : # 111| void MayPartiallyOverlap(int, int) # 111| Block 0 @@ -413,7 +423,8 @@ ssa.cpp: # 114| v0_21(void) = NoOp : # 111| v0_22(void) = ReturnVoid : # 111| v0_23(void) = UnmodeledUse : mu* -# 111| v0_24(void) = ExitFunction : +# 111| v0_24(void) = AliasedUse : ~mu0_2 +# 111| v0_25(void) = ExitFunction : # 116| void MayPartiallyOverlapEscaped(int, int) # 116| Block 0 @@ -444,12 +455,13 @@ ssa.cpp: # 119| r0_24(void *) = Convert : r0_23 # 119| v0_25(void) = Call : func:r0_21, 0:r0_24 # 119| mu0_26(unknown) = ^CallSideEffect : ~mu0_2 -# 119| v0_27(void) = ^IndirectReadSideEffect[0] : &:r0_24, ~mu0_2 +# 119| v0_27(void) = ^BufferReadSideEffect[0] : &:r0_24, ~mu0_2 # 119| mu0_28(unknown) = ^BufferMayWriteSideEffect[0] : &:r0_24 # 120| v0_29(void) = NoOp : # 116| v0_30(void) = ReturnVoid : # 116| v0_31(void) = UnmodeledUse : mu* -# 116| v0_32(void) = ExitFunction : +# 116| v0_32(void) = AliasedUse : ~mu0_2 +# 116| v0_33(void) = ExitFunction : # 122| void MergeMustExactlyOverlap(bool, int, int) # 122| Block 0 @@ -505,7 +517,8 @@ ssa.cpp: # 132| v3_9(void) = NoOp : # 122| v3_10(void) = ReturnVoid : # 122| v3_11(void) = UnmodeledUse : mu* -# 122| v3_12(void) = ExitFunction : +# 122| v3_12(void) = AliasedUse : ~mu0_2 +# 122| v3_13(void) = ExitFunction : # 134| void MergeMustExactlyWithMustTotallyOverlap(bool, Point, int) # 134| Block 0 @@ -556,7 +569,8 @@ ssa.cpp: # 143| v3_5(void) = NoOp : # 134| v3_6(void) = ReturnVoid : # 134| v3_7(void) = UnmodeledUse : mu* -# 134| v3_8(void) = ExitFunction : +# 134| v3_8(void) = AliasedUse : ~mu0_2 +# 134| v3_9(void) = ExitFunction : # 145| void MergeMustExactlyWithMayPartiallyOverlap(bool, Point, int) # 145| Block 0 @@ -606,7 +620,8 @@ ssa.cpp: # 154| v3_4(void) = NoOp : # 145| v3_5(void) = ReturnVoid : # 145| v3_6(void) = UnmodeledUse : mu* -# 145| v3_7(void) = ExitFunction : +# 145| v3_7(void) = AliasedUse : ~mu0_2 +# 145| v3_8(void) = ExitFunction : # 156| void MergeMustTotallyOverlapWithMayPartiallyOverlap(bool, Rect, int) # 156| Block 0 @@ -658,7 +673,8 @@ ssa.cpp: # 165| v3_5(void) = NoOp : # 156| v3_6(void) = ReturnVoid : # 156| v3_7(void) = UnmodeledUse : mu* -# 156| v3_8(void) = ExitFunction : +# 156| v3_8(void) = AliasedUse : ~mu0_2 +# 156| v3_9(void) = ExitFunction : # 171| void WrapperStruct(Wrapper) # 171| Block 0 @@ -692,7 +708,8 @@ ssa.cpp: # 177| v0_27(void) = NoOp : # 171| v0_28(void) = ReturnVoid : # 171| v0_29(void) = UnmodeledUse : mu* -# 171| v0_30(void) = ExitFunction : +# 171| v0_30(void) = AliasedUse : ~mu0_2 +# 171| v0_31(void) = ExitFunction : # 179| int AsmStmt(int*) # 179| Block 0 @@ -710,7 +727,8 @@ ssa.cpp: # 179| r0_11(glval) = VariableAddress[#return] : # 179| v0_12(void) = ReturnValue : &:r0_11, m0_10 # 179| v0_13(void) = UnmodeledUse : mu* -# 179| v0_14(void) = ExitFunction : +# 179| v0_14(void) = AliasedUse : ~mu0_2 +# 179| v0_15(void) = ExitFunction : # 184| void AsmStmtWithOutputs(unsigned int&, unsigned int&, unsigned int&, unsigned int&) # 184| Block 0 @@ -718,22 +736,31 @@ ssa.cpp: # 184| mu0_1(unknown) = AliasedDefinition : # 184| mu0_2(unknown) = UnmodeledDefinition : # 184| r0_3(glval) = VariableAddress[a] : -# 184| mu0_4(unsigned int &) = InitializeParameter[a] : &:r0_3 +# 184| m0_4(unsigned int &) = InitializeParameter[a] : &:r0_3 # 184| r0_5(glval) = VariableAddress[b] : -# 184| mu0_6(unsigned int &) = InitializeParameter[b] : &:r0_5 +# 184| m0_6(unsigned int &) = InitializeParameter[b] : &:r0_5 # 184| r0_7(glval) = VariableAddress[c] : -# 184| mu0_8(unsigned int &) = InitializeParameter[c] : &:r0_7 +# 184| m0_8(unsigned int &) = InitializeParameter[c] : &:r0_7 # 184| r0_9(glval) = VariableAddress[d] : -# 184| mu0_10(unsigned int &) = InitializeParameter[d] : &:r0_9 -# 186| r0_11(glval) = VariableAddress[a] : -# 186| r0_12(glval) = VariableAddress[b] : -# 186| r0_13(glval) = VariableAddress[c] : -# 186| r0_14(glval) = VariableAddress[d] : -# 186| mu0_15(unknown) = InlineAsm : ~mu0_2, 0:r0_11, 1:r0_12, 2:r0_13, 3:r0_14 -# 192| v0_16(void) = NoOp : -# 184| v0_17(void) = ReturnVoid : -# 184| v0_18(void) = UnmodeledUse : mu* -# 184| v0_19(void) = ExitFunction : +# 184| m0_10(unsigned int &) = InitializeParameter[d] : &:r0_9 +# 189| r0_11(glval) = VariableAddress[a] : +# 189| r0_12(unsigned int &) = Load : &:r0_11, m0_4 +# 189| r0_13(glval) = CopyValue : r0_12 +# 189| r0_14(glval) = VariableAddress[b] : +# 189| r0_15(unsigned int &) = Load : &:r0_14, m0_6 +# 189| r0_16(glval) = CopyValue : r0_15 +# 190| r0_17(glval) = VariableAddress[c] : +# 190| r0_18(unsigned int &) = Load : &:r0_17, m0_8 +# 190| r0_19(unsigned int) = Load : &:r0_18, ~mu0_2 +# 190| r0_20(glval) = VariableAddress[d] : +# 190| r0_21(unsigned int &) = Load : &:r0_20, m0_10 +# 190| r0_22(unsigned int) = Load : &:r0_21, ~mu0_2 +# 186| mu0_23(unknown) = InlineAsm : ~mu0_2, 0:r0_13, 1:r0_16, 2:r0_19, 3:r0_22 +# 192| v0_24(void) = NoOp : +# 184| v0_25(void) = ReturnVoid : +# 184| v0_26(void) = UnmodeledUse : mu* +# 184| v0_27(void) = AliasedUse : ~mu0_2 +# 184| v0_28(void) = ExitFunction : # 198| int PureFunctions(char*, char*, int) # 198| Block 0 @@ -782,7 +809,8 @@ ssa.cpp: # 198| r0_42(glval) = VariableAddress[#return] : # 198| v0_43(void) = ReturnValue : &:r0_42, m0_41 # 198| v0_44(void) = UnmodeledUse : mu* -# 198| v0_45(void) = ExitFunction : +# 198| v0_45(void) = AliasedUse : ~mu0_2 +# 198| v0_46(void) = ExitFunction : # 207| int ModeledCallTarget(int) # 207| Block 0 @@ -811,4 +839,5 @@ ssa.cpp: # 207| r0_22(glval) = VariableAddress[#return] : # 207| v0_23(void) = ReturnValue : &:r0_22, m0_21 # 207| v0_24(void) = UnmodeledUse : mu* -# 207| v0_25(void) = ExitFunction : +# 207| v0_25(void) = AliasedUse : ~mu0_2 +# 207| v0_26(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity.expected index 012ff53039b..928f535f9d8 100644 --- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_sanity.expected @@ -14,3 +14,8 @@ containsLoopOfForwardEdges lostReachability backEdgeCountMismatch useNotDominatedByDefinition +missingCanonicalLanguageType +multipleCanonicalLanguageTypes +missingIRType +multipleIRTypes +missingCppType diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected index cb3366e91b5..a7d7a6684e3 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.expected @@ -51,7 +51,6 @@ | test.cpp:163:12:163:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -1 | false | CompareNE: ... != ... | test.cpp:160:9:160:16 | test.cpp:160:9:160:16 | | test.cpp:163:12:163:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -1 | true | CompareLT: ... < ... | test.cpp:154:6:154:10 | test.cpp:154:6:154:10 | | test.cpp:163:12:163:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | -1 | true | CompareNE: ... != ... | test.cpp:160:9:160:16 | test.cpp:160:9:160:16 | -| test.cpp:167:12:167:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | 0 | false | CompareLT: ... < ... | test.cpp:154:6:154:10 | test.cpp:154:6:154:10 | | test.cpp:167:12:167:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | 1 | false | CompareEQ: ... == ... | test.cpp:166:9:166:16 | test.cpp:166:9:166:16 | | test.cpp:167:12:167:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | 1 | true | CompareEQ: ... == ... | test.cpp:166:9:166:16 | test.cpp:166:9:166:16 | | test.cpp:169:12:169:12 | Load: x | test.cpp:153:23:153:23 | InitializeParameter: y | 0 | false | CompareLT: ... < ... | test.cpp:154:6:154:10 | test.cpp:154:6:154:10 | diff --git a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql index b009ce5001f..ef158f0de28 100644 --- a/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql +++ b/cpp/ql/test/library-tests/rangeanalysis/rangeanalysis/RangeAnalysis.ql @@ -11,13 +11,7 @@ query predicate instructionBounds( or exists(ReturnValueInstruction retInstr | retInstr.getReturnValueOperand() = i.getAUse()) ) and - ( - upper = true and - delta = min(int d | boundedInstruction(i, b, d, upper, reason)) - or - upper = false and - delta = max(int d | boundedInstruction(i, b, d, upper, reason)) - ) and + boundedInstruction(i, b, delta, upper, reason) and not valueNumber(b.getInstruction()) = valueNumber(i) and if reason instanceof CondReason then reasonLoc = reason.(CondReason).getCond().getLocation() diff --git a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_sanity.expected b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_sanity.expected index 9e8588507ff..a7d9b22fc72 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_sanity.expected @@ -1,9 +1,9 @@ missingOperand | misc.c:125:5:125:11 | CopyValue: (statement expression) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:97:6:97:10 | IR: misc3 | void misc3() | -| parameterinitializer.cpp:27:3:27:6 | IndirectReadSideEffect: my_c | Instruction 'IndirectReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | allocators.cpp:14:5:14:8 | IR: main | int main() | -| parameterinitializer.cpp:27:3:27:6 | IndirectReadSideEffect: my_c | Instruction 'IndirectReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | no_dynamic_init.cpp:9:5:9:8 | IR: main | int main() | -| parameterinitializer.cpp:27:3:27:6 | IndirectReadSideEffect: my_c | Instruction 'IndirectReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | parameterinitializer.cpp:18:5:18:8 | IR: main | int main() | -| parameterinitializer.cpp:27:3:27:6 | IndirectReadSideEffect: my_c | Instruction 'IndirectReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | stream_it.cpp:16:5:16:8 | IR: main | int main() | +| parameterinitializer.cpp:27:3:27:6 | BufferReadSideEffect: my_c | Instruction 'BufferReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | allocators.cpp:14:5:14:8 | IR: main | int main() | +| parameterinitializer.cpp:27:3:27:6 | BufferReadSideEffect: my_c | Instruction 'BufferReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | no_dynamic_init.cpp:9:5:9:8 | IR: main | int main() | +| parameterinitializer.cpp:27:3:27:6 | BufferReadSideEffect: my_c | Instruction 'BufferReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | parameterinitializer.cpp:18:5:18:8 | IR: main | int main() | +| parameterinitializer.cpp:27:3:27:6 | BufferReadSideEffect: my_c | Instruction 'BufferReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | stream_it.cpp:16:5:16:8 | IR: main | int main() | | try_catch.cpp:13:5:13:16 | ThrowValue: throw ... | Instruction 'ThrowValue' is missing an expected operand with tag 'Load' in function '$@'. | try_catch.cpp:11:6:11:17 | IR: bypass_catch | void bypass_catch() | unexpectedOperand duplicateOperand @@ -13,7 +13,6 @@ missingOperandType sideEffectWithoutPrimary instructionWithoutSuccessor | VacuousDestructorCall.cpp:2:29:2:29 | InitializeParameter: y | -| assume0.cpp:7:2:7:2 | Chi: call to f | | condition_decls.cpp:16:19:16:20 | Chi: call to BoxedInt | | condition_decls.cpp:26:23:26:24 | Chi: call to BoxedInt | | condition_decls.cpp:41:22:41:23 | Chi: call to BoxedInt | @@ -546,3 +545,8 @@ lostReachability | range_analysis.c:371:37:371:39 | Constant: 500 | backEdgeCountMismatch useNotDominatedByDefinition +missingCanonicalLanguageType +multipleCanonicalLanguageTypes +missingIRType +multipleIRTypes +missingCppType diff --git a/cpp/ql/test/library-tests/syntax-zoo/raw_sanity.expected b/cpp/ql/test/library-tests/syntax-zoo/raw_sanity.expected index 1bcffed0736..68298b46256 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/raw_sanity.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/raw_sanity.expected @@ -8,6 +8,7 @@ missingOperand | misc.c:220:9:223:3 | FieldAddress: {...} | Instruction 'FieldAddress' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:219:5:219:26 | IR: assign_designated_init | int assign_designated_init(someStruct*) | | misc.c:220:9:223:3 | FieldAddress: {...} | Instruction 'FieldAddress' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:219:5:219:26 | IR: assign_designated_init | int assign_designated_init(someStruct*) | | pointer_to_member.cpp:36:13:36:19 | FieldAddress: x1 | Instruction 'FieldAddress' is missing an expected operand with tag 'Unary' in function '$@'. | pointer_to_member.cpp:32:6:32:14 | IR: pmIsConst | void pmIsConst() | +| pointer_to_member.cpp:36:22:36:28 | Store: f1 | Instruction 'Store' is missing an expected operand with tag 'StoreValue' in function '$@'. | pointer_to_member.cpp:32:6:32:14 | IR: pmIsConst | void pmIsConst() | | range_analysis.c:368:10:368:21 | Store: ... ? ... : ... | Instruction 'Store' is missing an expected operand with tag 'StoreValue' in function '$@'. | range_analysis.c:355:14:355:27 | IR: test_ternary01 | unsigned int test_ternary01(unsigned int) | | range_analysis.c:369:10:369:36 | Store: ... ? ... : ... | Instruction 'Store' is missing an expected operand with tag 'StoreValue' in function '$@'. | range_analysis.c:355:14:355:27 | IR: test_ternary01 | unsigned int test_ternary01(unsigned int) | | range_analysis.c:370:10:370:38 | Store: ... ? ... : ... | Instruction 'Store' is missing an expected operand with tag 'StoreValue' in function '$@'. | range_analysis.c:355:14:355:27 | IR: test_ternary01 | unsigned int test_ternary01(unsigned int) | @@ -25,8 +26,6 @@ instructionWithoutSuccessor | VacuousDestructorCall.cpp:2:29:2:29 | InitializeParameter: y | | VacuousDestructorCall.cpp:3:3:3:3 | VariableAddress: x | | VacuousDestructorCall.cpp:4:3:4:3 | Load: y | -| assume0.cpp:7:2:7:2 | CallSideEffect: call to f | -| assume0.cpp:9:11:9:11 | Constant: (bool)... | | condition_decls.cpp:16:19:16:20 | CallSideEffect: call to BoxedInt | | condition_decls.cpp:26:19:26:20 | IndirectMayWriteSideEffect: bi | | condition_decls.cpp:26:23:26:24 | CallSideEffect: call to BoxedInt | @@ -46,7 +45,6 @@ instructionWithoutSuccessor | misc.c:219:47:219:48 | InitializeParameter: sp | | misc.c:221:10:221:10 | Store: 1 | | misc.c:222:10:222:10 | Store: 2 | -| ms_assume.cpp:20:12:20:12 | Constant: (bool)... | | ms_try_except.cpp:3:9:3:9 | Uninitialized: definition of x | | ms_try_except.cpp:7:13:7:17 | Store: ... = ... | | ms_try_except.cpp:9:19:9:19 | Load: j | @@ -68,6 +66,7 @@ instructionWithoutSuccessor | ms_try_mix.cpp:51:5:51:11 | ThrowValue: throw ... | | ms_try_mix.cpp:53:13:54:3 | NoOp: { ... } | | pointer_to_member.cpp:36:11:36:30 | FieldAddress: {...} | +| pointer_to_member.cpp:36:11:36:30 | FieldAddress: {...} | | static_init_templates.cpp:80:27:80:36 | Convert: (void *)... | | static_init_templates.cpp:80:27:80:36 | Convert: (void *)... | | static_init_templates.cpp:89:27:89:36 | Convert: (void *)... | @@ -611,86 +610,12 @@ lostReachability | range_analysis.c:371:37:371:39 | Constant: 500 | backEdgeCountMismatch useNotDominatedByDefinition -| VacuousDestructorCall.cpp:4:3:4:3 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | IR: CallDestructor | void CallDestructor(int, int*) | -| assume0.cpp:11:2:11:2 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | assume0.cpp:5:6:5:6 | IR: h | void h() | -| condition_decls.cpp:16:15:16:15 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) | -| condition_decls.cpp:16:15:16:16 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) | -| condition_decls.cpp:16:15:16:16 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) | -| condition_decls.cpp:17:5:17:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) | -| condition_decls.cpp:17:11:17:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) | -| condition_decls.cpp:20:5:20:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) | -| condition_decls.cpp:20:11:20:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:15:6:15:17 | IR: if_decl_bind | void if_decl_bind(int) | -| condition_decls.cpp:26:19:26:19 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) | -| condition_decls.cpp:26:19:26:20 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) | -| condition_decls.cpp:26:19:26:20 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) | -| condition_decls.cpp:28:5:28:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) | -| condition_decls.cpp:28:11:28:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) | -| condition_decls.cpp:31:5:31:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) | -| condition_decls.cpp:31:11:31:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) | -| condition_decls.cpp:34:5:34:18 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) | -| condition_decls.cpp:34:9:34:13 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:25:6:25:21 | IR: switch_decl_bind | void switch_decl_bind(int) | -| condition_decls.cpp:41:18:41:18 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | condition_decls.cpp:40:6:40:20 | IR: while_decl_bind | void while_decl_bind(int) | -| condition_decls.cpp:41:18:41:19 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:40:6:40:20 | IR: while_decl_bind | void while_decl_bind(int) | -| condition_decls.cpp:41:18:41:19 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | condition_decls.cpp:40:6:40:20 | IR: while_decl_bind | void while_decl_bind(int) | -| condition_decls.cpp:42:5:42:7 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:40:6:40:20 | IR: while_decl_bind | void while_decl_bind(int) | -| condition_decls.cpp:44:3:44:5 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:40:6:40:20 | IR: while_decl_bind | void while_decl_bind(int) | -| condition_decls.cpp:48:48:48:48 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | condition_decls.cpp:47:6:47:18 | IR: for_decl_bind | void for_decl_bind(int) | -| condition_decls.cpp:48:48:48:49 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:47:6:47:18 | IR: for_decl_bind | void for_decl_bind(int) | -| condition_decls.cpp:48:48:48:49 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | condition_decls.cpp:47:6:47:18 | IR: for_decl_bind | void for_decl_bind(int) | -| condition_decls.cpp:48:56:48:61 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:47:6:47:18 | IR: for_decl_bind | void for_decl_bind(int) | -| condition_decls.cpp:49:5:49:7 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:47:6:47:18 | IR: for_decl_bind | void for_decl_bind(int) | -| condition_decls.cpp:51:3:51:5 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | condition_decls.cpp:47:6:47:18 | IR: for_decl_bind | void for_decl_bind(int) | -| cpp11.cpp:28:21:28:21 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | cpp11.cpp:27:7:27:14 | IR: getFirst | int range_based_for_11::getFirst() | -| file://:0:0:0:0 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | cpp11.cpp:27:7:27:14 | IR: getFirst | int range_based_for_11::getFirst() | -| misc.c:68:16:68:16 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:70:13:70:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:72:11:72:11 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:82:5:82:12 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:83:5:83:12 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:83:14:83:14 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:83:17:83:17 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:84:6:84:13 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:84:6:84:13 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:84:16:84:16 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:84:19:84:19 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:85:9:85:13 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:86:9:86:9 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:87:10:87:10 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:88:9:88:9 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:88:9:88:17 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:16:6:16:10 | IR: misc1 | void misc1(int, int) | -| misc.c:171:15:171:15 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:168:6:168:8 | IR: vla | void vla() | -| misc.c:173:19:173:24 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | misc.c:168:6:168:8 | IR: vla | void vla() | -| misc.c:174:17:174:22 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | misc.c:168:6:168:8 | IR: vla | void vla() | -| misc.c:174:30:174:35 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | misc.c:168:6:168:8 | IR: vla | void vla() | -| misc.c:219:5:219:26 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:219:5:219:26 | IR: assign_designated_init | int assign_designated_init(someStruct*) | -| misc.c:220:4:220:5 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | misc.c:219:5:219:26 | IR: assign_designated_init | int assign_designated_init(someStruct*) | -| ms_try_except.cpp:9:19:9:19 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | IR: ms_try_except | void ms_try_except(int) | -| ms_try_except.cpp:19:17:19:17 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | IR: ms_try_except | void ms_try_except(int) | -| ms_try_mix.cpp:14:16:14:19 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:10:6:10:18 | IR: ms_except_mix | void ms_except_mix(int) | -| ms_try_mix.cpp:15:13:15:14 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:10:6:10:18 | IR: ms_except_mix | void ms_except_mix(int) | -| ms_try_mix.cpp:16:13:16:19 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:10:6:10:18 | IR: ms_except_mix | void ms_except_mix(int) | -| ms_try_mix.cpp:18:16:18:19 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:10:6:10:18 | IR: ms_except_mix | void ms_except_mix(int) | -| ms_try_mix.cpp:21:16:21:19 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:10:6:10:18 | IR: ms_except_mix | void ms_except_mix(int) | -| ms_try_mix.cpp:24:12:24:15 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:10:6:10:18 | IR: ms_except_mix | void ms_except_mix(int) | -| ms_try_mix.cpp:31:16:31:19 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:27:6:27:19 | IR: ms_finally_mix | void ms_finally_mix(int) | -| ms_try_mix.cpp:32:13:32:14 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:27:6:27:19 | IR: ms_finally_mix | void ms_finally_mix(int) | -| ms_try_mix.cpp:33:13:33:19 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:27:6:27:19 | IR: ms_finally_mix | void ms_finally_mix(int) | -| ms_try_mix.cpp:35:16:35:19 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:27:6:27:19 | IR: ms_finally_mix | void ms_finally_mix(int) | -| ms_try_mix.cpp:38:16:38:19 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:27:6:27:19 | IR: ms_finally_mix | void ms_finally_mix(int) | -| ms_try_mix.cpp:41:12:41:15 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:27:6:27:19 | IR: ms_finally_mix | void ms_finally_mix(int) | -| ms_try_mix.cpp:51:5:51:11 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | ms_try_mix.cpp:47:6:47:28 | IR: ms_empty_finally_at_end | void ms_empty_finally_at_end() | | pointer_to_member.cpp:36:11:36:30 | Unary | Operand 'Unary' is not dominated by its definition in function '$@'. | pointer_to_member.cpp:32:6:32:14 | IR: pmIsConst | void pmIsConst() | | pointer_to_member.cpp:36:13:36:19 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | pointer_to_member.cpp:32:6:32:14 | IR: pmIsConst | void pmIsConst() | -| stmt_expr.cpp:30:20:30:21 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | stmt_expr.cpp:21:6:21:6 | IR: g | void stmtexpr::g(int) | -| stmt_expr.cpp:31:16:31:18 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | stmt_expr.cpp:21:6:21:6 | IR: g | void stmtexpr::g(int) | +| pointer_to_member.cpp:36:22:36:28 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | pointer_to_member.cpp:32:6:32:14 | IR: pmIsConst | void pmIsConst() | | try_catch.cpp:21:13:21:24 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | try_catch.cpp:19:6:19:23 | IR: throw_from_nonstmt | void throw_from_nonstmt(int) | -| vla.c:3:5:3:8 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | vla.c:3:5:3:8 | IR: main | int main(int, char**) | -| vla.c:5:16:5:19 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | vla.c:3:5:3:8 | IR: main | int main(int, char**) | -| vla.c:5:22:5:25 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | vla.c:3:5:3:8 | IR: main | int main(int, char**) | -| vla.c:5:27:5:30 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | vla.c:3:5:3:8 | IR: main | int main(int, char**) | -| vla.c:5:27:5:33 | Load | Operand 'Load' is not dominated by its definition in function '$@'. | vla.c:3:5:3:8 | IR: main | int main(int, char**) | -| vla.c:12:37:12:42 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | vla.c:11:6:11:16 | IR: vla_typedef | void vla_typedef() | -| vla.c:12:55:12:60 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | vla.c:11:6:11:16 | IR: vla_typedef | void vla_typedef() | -| vla.c:14:40:14:45 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | vla.c:11:6:11:16 | IR: vla_typedef | void vla_typedef() | -| vla.c:14:58:14:63 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | vla.c:11:6:11:16 | IR: vla_typedef | void vla_typedef() | -| vla.c:14:74:14:79 | Operand | Operand 'Operand' is not dominated by its definition in function '$@'. | vla.c:11:6:11:16 | IR: vla_typedef | void vla_typedef() | +missingCanonicalLanguageType +multipleCanonicalLanguageTypes +missingIRType +multipleIRTypes +missingCppType diff --git a/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_sanity.expected b/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_sanity.expected index d064a1264ec..4ba89c3d0c6 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_sanity.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_sanity.expected @@ -1,9 +1,5 @@ missingOperand | misc.c:125:5:125:11 | CopyValue: (statement expression) | Instruction 'CopyValue' is missing an expected operand with tag 'Unary' in function '$@'. | misc.c:97:6:97:10 | IR: misc3 | void misc3() | -| parameterinitializer.cpp:27:3:27:6 | IndirectReadSideEffect: my_c | Instruction 'IndirectReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | allocators.cpp:14:5:14:8 | IR: main | int main() | -| parameterinitializer.cpp:27:3:27:6 | IndirectReadSideEffect: my_c | Instruction 'IndirectReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | no_dynamic_init.cpp:9:5:9:8 | IR: main | int main() | -| parameterinitializer.cpp:27:3:27:6 | IndirectReadSideEffect: my_c | Instruction 'IndirectReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | parameterinitializer.cpp:18:5:18:8 | IR: main | int main() | -| parameterinitializer.cpp:27:3:27:6 | IndirectReadSideEffect: my_c | Instruction 'IndirectReadSideEffect' is missing an expected operand with tag 'SideEffect' in function '$@'. | stream_it.cpp:16:5:16:8 | IR: main | int main() | | try_catch.cpp:13:5:13:16 | ThrowValue: throw ... | Instruction 'ThrowValue' is missing an expected operand with tag 'Load' in function '$@'. | try_catch.cpp:11:6:11:17 | IR: bypass_catch | void bypass_catch() | unexpectedOperand duplicateOperand @@ -22,7 +18,6 @@ missingOperandType sideEffectWithoutPrimary instructionWithoutSuccessor | VacuousDestructorCall.cpp:2:29:2:29 | InitializeParameter: y | -| assume0.cpp:7:2:7:2 | CallSideEffect: call to f | | condition_decls.cpp:16:19:16:20 | CallSideEffect: call to BoxedInt | | condition_decls.cpp:26:23:26:24 | CallSideEffect: call to BoxedInt | | condition_decls.cpp:41:22:41:23 | CallSideEffect: call to BoxedInt | @@ -555,3 +550,8 @@ lostReachability | range_analysis.c:371:37:371:39 | Constant: 500 | backEdgeCountMismatch useNotDominatedByDefinition +missingCanonicalLanguageType +multipleCanonicalLanguageTypes +missingIRType +multipleIRTypes +missingCppType diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected index ec6f300a957..c37bf5c8bfc 100644 --- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected +++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected @@ -67,7 +67,8 @@ test.cpp: # 1| valnum = unique # 1| v0_33(void) = ReturnValue : &:r0_32 # 1| v0_34(void) = UnmodeledUse : mu* -# 1| v0_35(void) = ExitFunction : +# 1| v0_35(void) = AliasedUse : ~m0_1 +# 1| v0_36(void) = ExitFunction : # 12| int test01(int, int) # 12| Block 0 @@ -149,7 +150,8 @@ test.cpp: # 12| valnum = unique # 12| v0_39(void) = ReturnValue : &:r0_38 # 12| v0_40(void) = UnmodeledUse : mu* -# 12| v0_41(void) = ExitFunction : +# 12| v0_41(void) = AliasedUse : ~m0_1 +# 12| v0_42(void) = ExitFunction : # 25| int test02(int, int) # 25| Block 0 @@ -238,7 +240,8 @@ test.cpp: # 25| valnum = unique # 25| v0_43(void) = ReturnValue : &:r0_42 # 25| v0_44(void) = UnmodeledUse : mu* -# 25| v0_45(void) = ExitFunction : +# 25| v0_45(void) = AliasedUse : ~m0_26 +# 25| v0_46(void) = ExitFunction : # 39| int test03(int, int, int*) # 39| Block 0 @@ -336,7 +339,8 @@ test.cpp: # 39| valnum = unique # 39| v0_47(void) = ReturnValue : &:r0_46 # 39| v0_48(void) = UnmodeledUse : mu* -# 39| v0_49(void) = ExitFunction : +# 39| v0_49(void) = AliasedUse : ~m0_30 +# 39| v0_50(void) = ExitFunction : # 49| unsigned int my_strspn(char const*, char const*) # 49| Block 0 @@ -396,27 +400,27 @@ test.cpp: #-----| Goto -> Block 3 # 56| Block 3 -# 56| m3_0(char *) = Phi : from 2:m2_3, from 5:m5_4 +# 56| m3_0(decltype(nullptr)) = Phi : from 2:m2_3, from 5:m5_4 # 56| valnum = m3_0 -# 56| r3_1(glval) = VariableAddress[ptr] : +# 56| r3_1(glval) = VariableAddress[ptr] : # 56| valnum = r0_7 -# 56| r3_2(char *) = Load : &:r3_1, m3_0 +# 56| r3_2(char *) = Load : &:r3_1, m3_0 # 56| valnum = m3_0 -# 56| r3_3(char) = Load : &:r3_2, ~m0_1 +# 56| r3_3(char) = Load : &:r3_2, ~m0_1 # 56| valnum = unique -# 56| r3_4(int) = Convert : r3_3 +# 56| r3_4(int) = Convert : r3_3 # 56| valnum = unique -# 56| r3_5(glval) = VariableAddress[str] : +# 56| r3_5(glval) = VariableAddress[str] : # 56| valnum = r0_3 -# 56| r3_6(char *) = Load : &:r3_5, m0_4 +# 56| r3_6(char *) = Load : &:r3_5, m0_4 # 56| valnum = m0_4 -# 56| r3_7(char) = Load : &:r3_6, ~m0_1 +# 56| r3_7(char) = Load : &:r3_6, ~m0_1 # 56| valnum = unique -# 56| r3_8(int) = Convert : r3_7 +# 56| r3_8(int) = Convert : r3_7 # 56| valnum = unique -# 56| r3_9(bool) = CompareNE : r3_4, r3_8 +# 56| r3_9(bool) = CompareNE : r3_4, r3_8 # 56| valnum = unique -# 56| v3_10(void) = ConditionalBranch : r3_9 +# 56| v3_10(void) = ConditionalBranch : r3_9 #-----| False -> Block 6 #-----| True -> Block 4 @@ -498,7 +502,8 @@ test.cpp: # 49| valnum = r9_1 # 49| v9_6(void) = ReturnValue : &:r9_5, m9_4 # 49| v9_7(void) = UnmodeledUse : mu* -# 49| v9_8(void) = ExitFunction : +# 49| v9_8(void) = AliasedUse : ~m0_1 +# 49| v9_9(void) = ExitFunction : # 75| void test04(two_values*) # 75| Block 0 @@ -577,10 +582,13 @@ test.cpp: #-----| Goto -> Block 2 # 82| Block 2 -# 82| v2_0(void) = NoOp : -# 75| v2_1(void) = ReturnVoid : -# 75| v2_2(void) = UnmodeledUse : mu* -# 75| v2_3(void) = ExitFunction : +# 82| m2_0(unknown) = Phi : from 0:~m0_9, from 1:~m1_3 +# 82| valnum = unique +# 82| v2_1(void) = NoOp : +# 75| v2_2(void) = ReturnVoid : +# 75| v2_3(void) = UnmodeledUse : mu* +# 75| v2_4(void) = AliasedUse : ~m2_0 +# 75| v2_5(void) = ExitFunction : # 84| void test05(int, int, void*) # 84| Block 0 @@ -653,7 +661,8 @@ test.cpp: # 89| v3_5(void) = NoOp : # 84| v3_6(void) = ReturnVoid : # 84| v3_7(void) = UnmodeledUse : mu* -# 84| v3_8(void) = ExitFunction : +# 84| v3_8(void) = AliasedUse : ~m0_1 +# 84| v3_9(void) = ExitFunction : # 91| int regression_test00() # 91| Block 0 @@ -686,7 +695,8 @@ test.cpp: # 91| valnum = r0_9 # 91| v0_14(void) = ReturnValue : &:r0_13, m0_12 # 91| v0_15(void) = UnmodeledUse : mu* -# 91| v0_16(void) = ExitFunction : +# 91| v0_16(void) = AliasedUse : ~m0_1 +# 91| v0_17(void) = ExitFunction : # 104| int inheritanceConversions(Derived*) # 104| Block 0 @@ -747,7 +757,8 @@ test.cpp: # 104| valnum = r0_23 # 104| v0_28(void) = ReturnValue : &:r0_27, m0_26 # 104| v0_29(void) = UnmodeledUse : mu* -# 104| v0_30(void) = ExitFunction : +# 104| v0_30(void) = AliasedUse : ~m0_1 +# 104| v0_31(void) = ExitFunction : # 112| void test06() # 112| Block 0 @@ -767,4 +778,5 @@ test.cpp: # 117| v0_7(void) = NoOp : # 112| v0_8(void) = ReturnVoid : # 112| v0_9(void) = UnmodeledUse : mu* -# 112| v0_10(void) = ExitFunction : +# 112| v0_10(void) = AliasedUse : ~m0_1 +# 112| v0_11(void) = ExitFunction : diff --git a/cpp/ql/test/library-tests/variables/variables/types.expected b/cpp/ql/test/library-tests/variables/variables/types.expected index f239d276871..cdc8c42a762 100644 --- a/cpp/ql/test/library-tests/variables/variables/types.expected +++ b/cpp/ql/test/library-tests/variables/variables/types.expected @@ -1,18 +1,18 @@ | ..()(..) | RoutineType | | | | | | ..(*)(..) | FunctionPointerType | | ..()(..) | | | -| _Complex __float128 | ArithmeticType | | | | | +| _Complex __float128 | FloatingPointType | | | | | | _Complex double | FloatingPointType | | | | | | _Complex float | FloatingPointType | | | | | | _Complex long double | FloatingPointType | | | | | -| _Decimal32 | ArithmeticType | | | | | -| _Decimal64 | ArithmeticType | | | | | -| _Decimal128 | ArithmeticType | | | | | -| _Float32 | ArithmeticType | | | | | -| _Float32x | ArithmeticType | | | | | -| _Float64 | ArithmeticType | | | | | -| _Float64x | ArithmeticType | | | | | -| _Float128 | ArithmeticType | | | | | -| _Float128x | ArithmeticType | | | | | +| _Decimal32 | Decimal32Type | | | | | +| _Decimal64 | Decimal64Type | | | | | +| _Decimal128 | Decimal128Type | | | | | +| _Float32 | FloatingPointType | | | | | +| _Float32x | FloatingPointType | | | | | +| _Float64 | FloatingPointType | | | | | +| _Float64x | FloatingPointType | | | | | +| _Float128 | FloatingPointType | | | | | +| _Float128x | FloatingPointType | | | | | | _Imaginary double | FloatingPointType | | | | | | _Imaginary float | FloatingPointType | | | | | | _Imaginary long double | FloatingPointType | | | | | diff --git a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/ComparisonPrecedence/ComparisonPrecedence.expected b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/ComparisonPrecedence/ComparisonPrecedence.expected index e07d202a678..c3ae88df15c 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/ComparisonPrecedence/ComparisonPrecedence.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Arithmetic/ComparisonPrecedence/ComparisonPrecedence.expected @@ -1,9 +1,9 @@ -| template.cpp:4:7:4:15 | ... < ... | Check the comparison operator precedence. | -| test.cpp:42:6:42:14 | ... < ... | Check the comparison operator precedence. | -| test.cpp:43:6:43:14 | ... > ... | Check the comparison operator precedence. | -| test.cpp:44:6:44:16 | ... <= ... | Check the comparison operator precedence. | -| test.cpp:45:6:45:16 | ... <= ... | Check the comparison operator precedence. | -| test.cpp:46:6:46:14 | ... > ... | Check the comparison operator precedence. | -| test.cpp:50:6:50:32 | ... < ... | Check the comparison operator precedence. | -| test.cpp:51:6:51:18 | ... < ... | Check the comparison operator precedence. | -| test.cpp:54:8:54:16 | ... < ... | Check the comparison operator precedence. | +| template.cpp:4:7:4:15 | ... < ... | Comparison as an operand to another comparison. | +| test.cpp:42:6:42:14 | ... < ... | Comparison as an operand to another comparison. | +| test.cpp:43:6:43:14 | ... > ... | Comparison as an operand to another comparison. | +| test.cpp:44:6:44:16 | ... <= ... | Comparison as an operand to another comparison. | +| test.cpp:45:6:45:16 | ... <= ... | Comparison as an operand to another comparison. | +| test.cpp:46:6:46:14 | ... > ... | Comparison as an operand to another comparison. | +| test.cpp:50:6:50:32 | ... < ... | Comparison as an operand to another comparison. | +| test.cpp:51:6:51:18 | ... < ... | Comparison as an operand to another comparison. | +| test.cpp:54:8:54:16 | ... < ... | Comparison as an operand to another comparison. | diff --git a/cpp/upgrades/qlpack.yml b/cpp/upgrades/qlpack.yml new file mode 100644 index 00000000000..eaf90d6cf90 --- /dev/null +++ b/cpp/upgrades/qlpack.yml @@ -0,0 +1,2 @@ +name: codeql-cpp-upgrades +upgrades: . diff --git a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs b/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs index dd0397c9605..20d19a3525c 100644 --- a/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs +++ b/csharp/autobuilder/Semmle.Autobuild.Tests/BuildScripts.cs @@ -340,6 +340,9 @@ namespace Semmle.Extraction.Tests string nugetRestore = null, string allSolutions = null, string cwd = @"C:\Project") { + Actions.GetEnvironmentVariable["CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING"] = "false"; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_ROOT"] = @"C:\codeql\csharp"; + Actions.GetEnvironmentVariable["CODEQL_JAVA_HOME"] = @"C:\codeql\tools\java"; Actions.GetEnvironmentVariable["SEMMLE_DIST"] = @"C:\odasa"; Actions.GetEnvironmentVariable["SEMMLE_JAVA_HOME"] = @"C:\odasa\tools\java"; Actions.GetEnvironmentVariable["LGTM_PROJECT_LANGUAGE"] = lgtmLanguage; @@ -373,12 +376,12 @@ namespace Semmle.Extraction.Tests Actions.RunProcess["cmd.exe /C dotnet clean test.csproj"] = 0; Actions.RunProcess["cmd.exe /C dotnet restore test.csproj"] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; + Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbar.cs\ntest.csproj"; Actions.EnumerateDirectories[@"C:\Project"] = ""; var xml = new XmlDocument(); @@ -402,12 +405,12 @@ namespace Semmle.Extraction.Tests Actions.RunProcess["dotnet clean test.csproj"] = 0; Actions.RunProcess["dotnet restore test.csproj"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; Actions.EnumerateDirectories[@"C:\Project"] = ""; var xml = new XmlDocument(); @@ -428,8 +431,8 @@ namespace Semmle.Extraction.Tests public void TestLinuxCSharpAutoBuilderExtractorFailed() { Actions.FileExists["csharp.log"] = false; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -522,11 +525,11 @@ namespace Semmle.Extraction.Tests public void TestLinuxBuildlessExtractionSuccess() { Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone --references:."] = 0; - Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -539,8 +542,8 @@ namespace Semmle.Extraction.Tests { Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone --references:."] = 10; Actions.FileExists["csharp.log"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -552,11 +555,11 @@ namespace Semmle.Extraction.Tests public void TestLinuxBuildlessExtractionSolution() { Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone foo.sln --references:."] = 0; - Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -596,11 +599,11 @@ namespace Semmle.Extraction.Tests public void TestLinuxBuildCommand() { Actions.RunProcess[@"C:\odasa/tools/odasa index --auto ""./build.sh --skip-tests"""] = 0; - Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -615,12 +618,12 @@ namespace Semmle.Extraction.Tests { Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild/build.sh"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.RunProcess["/bin/chmod u+x build/build.sh"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto build/build.sh"] = 0; Actions.RunProcessWorkingDirectory[@"C:\odasa/tools/odasa index --auto build/build.sh"] = "build"; - Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; @@ -633,8 +636,8 @@ namespace Semmle.Extraction.Tests { Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.sh"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.RunProcess["/bin/chmod u+x build.sh"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto build.sh"] = 0; @@ -650,8 +653,8 @@ namespace Semmle.Extraction.Tests { Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.sh"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.RunProcess["/bin/chmod u+x build.sh"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto build.sh"] = 5; @@ -667,11 +670,11 @@ namespace Semmle.Extraction.Tests { Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.bat"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto build.bat"] = 0; Actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\odasa\tools\odasa index --auto build.bat"] = ""; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; + Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; @@ -684,11 +687,11 @@ namespace Semmle.Extraction.Tests { Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbuild.bat"; Actions.EnumerateDirectories[@"C:\Project"] = ""; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto build.bat"] = 1; Actions.RunProcessWorkingDirectory[@"cmd.exe /C C:\odasa\tools\odasa index --auto build.bat"] = ""; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; + Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config"] = 0; Actions.FileExists["csharp.log"] = true; @@ -700,13 +703,13 @@ namespace Semmle.Extraction.Tests public void TestWindowsCmdIgnoreErrors() { Actions.RunProcess["cmd.exe /C C:\\odasa\\tools\\odasa index --auto ^\"build.cmd --skip-tests^\""] = 3; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; + Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config"] = 0; Actions.FileExists["csharp.log"] = true; SkipVsWhere(); - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -721,7 +724,7 @@ namespace Semmle.Extraction.Tests Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore C:\Project\test2.sln"] = 0; Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; + Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; @@ -730,8 +733,8 @@ namespace Semmle.Extraction.Tests Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -752,7 +755,7 @@ namespace Semmle.Extraction.Tests Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && C:\\odasa\\tools\\odasa index --auto msbuild test1.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore test2.csproj"] = 0; Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && C:\\odasa\\tools\\odasa index --auto msbuild test2.csproj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; + Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"test1.csproj"] = true; @@ -763,8 +766,8 @@ namespace Semmle.Extraction.Tests Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "test1.csproj\ntest2.csproj\ntest1.cs\ntest2.cs"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -803,8 +806,8 @@ namespace Semmle.Extraction.Tests Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -824,7 +827,7 @@ namespace Semmle.Extraction.Tests { Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test1.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && C:\\odasa\\tools\\odasa index --auto msbuild C:\\Project\\test2.sln /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; + Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"] = false; @@ -832,8 +835,8 @@ namespace Semmle.Extraction.Tests Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat"] = true; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest1.cs\ntest2.cs"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -852,11 +855,11 @@ namespace Semmle.Extraction.Tests public void TestSkipNugetBuildless() { Actions.RunProcess[@"C:\odasa\tools/csharp/Semmle.Extraction.CSharp.Standalone foo.sln --references:. --skip-nuget"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.sln"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -872,12 +875,12 @@ namespace Semmle.Extraction.Tests Actions.RunProcess["dotnet clean test.csproj"] = 0; Actions.RunProcess["dotnet restore test.csproj"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto dotnet build --no-incremental /p:UseSharedCompilation=false --no-restore test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; Actions.EnumerateDirectories[@"C:\Project"] = ""; var xml = new XmlDocument(); @@ -899,7 +902,7 @@ namespace Semmle.Extraction.Tests { Actions.RunProcess["dotnet --list-sdks"] = 0; Actions.RunProcessOut["dotnet --list-sdks"] = "2.1.2 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]"; - Actions.RunProcess[@"curl -sO https://dot.net/v1/dotnet-install.sh"] = 0; + Actions.RunProcess[@"curl -L -sO https://dot.net/v1/dotnet-install.sh"] = 0; Actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; Actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; Actions.RunProcess[@"rm dotnet-install.sh"] = 0; @@ -907,12 +910,12 @@ namespace Semmle.Extraction.Tests Actions.RunProcess[@"C:\Project/.dotnet/dotnet clean test.csproj"] = 0; Actions.RunProcess[@"C:\Project/.dotnet/dotnet restore test.csproj"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.GetEnvironmentVariable["PATH"] = "/bin:/usr/bin"; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -935,7 +938,7 @@ namespace Semmle.Extraction.Tests { Actions.RunProcess["dotnet --list-sdks"] = 0; Actions.RunProcessOut["dotnet --list-sdks"] = "2.1.3 [C:\\Program Files\\dotnet\\sdks]\n2.1.4 [C:\\Program Files\\dotnet\\sdks]"; - Actions.RunProcess[@"curl -sO https://dot.net/v1/dotnet-install.sh"] = 0; + Actions.RunProcess[@"curl -L -sO https://dot.net/v1/dotnet-install.sh"] = 0; Actions.RunProcess[@"chmod u+x dotnet-install.sh"] = 0; Actions.RunProcess[@"./dotnet-install.sh --channel release --version 2.1.3 --install-dir C:\Project/.dotnet"] = 0; Actions.RunProcess[@"rm dotnet-install.sh"] = 0; @@ -943,12 +946,12 @@ namespace Semmle.Extraction.Tests Actions.RunProcess[@"C:\Project/.dotnet/dotnet clean test.csproj"] = 0; Actions.RunProcess[@"C:\Project/.dotnet/dotnet restore test.csproj"] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto C:\Project/.dotnet/dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.GetEnvironmentVariable["PATH"] = "/bin:/usr/bin"; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\nbar.cs\ntest.csproj"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -977,12 +980,12 @@ namespace Semmle.Extraction.Tests Actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet clean test.csproj"] = 0; Actions.RunProcess[@"cmd.exe /C C:\Project\.dotnet\dotnet restore test.csproj"] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --auto C:\Project\.dotnet\dotnet build --no-incremental /p:UseSharedCompilation=false test.csproj"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; + Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["test.csproj"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.GetEnvironmentVariable["PATH"] = "/bin:/usr/bin"; Actions.EnumerateFiles[@"C:\Project"] = "foo.cs\ntest.cs\ntest.csproj"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -1005,7 +1008,7 @@ namespace Semmle.Extraction.Tests { Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\csharp\nuget\nuget.exe restore dirs.proj"] = 1; Actions.RunProcess["cmd.exe /C CALL ^\"C:\\Program Files ^(x86^)\\Microsoft Visual Studio 12.0\\VC\\vcvarsall.bat^\" && C:\\odasa\\tools\\odasa index --auto msbuild dirs.proj /p:UseSharedCompilation=false /t:Windows /p:Platform=\"x86\" /p:Configuration=\"Debug\" /p:MvcBuildViews=true /P:Fu=Bar"] = 0; - Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\java\bin\java -jar C:\odasa\tools\extractor-asp.jar ."] = 0; + Actions.RunProcess[@"cmd.exe /C C:\codeql\tools\java\bin\java -jar C:\codeql\csharp\tools\extractor-asp.jar ."] = 0; Actions.RunProcess[@"cmd.exe /C C:\odasa\tools\odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists[@"a\test.csproj"] = true; @@ -1016,8 +1019,8 @@ namespace Semmle.Extraction.Tests Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\vcvarsall.bat"] = false; Actions.FileExists[@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "a\\test.cs\na\\test.csproj\ndirs.proj"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -1048,13 +1051,13 @@ namespace Semmle.Extraction.Tests { Actions.RunProcess[@"mono C:\odasa\tools/csharp/nuget/nuget.exe restore dirs.proj"] = 1; Actions.RunProcess[@"C:\odasa/tools/odasa index --auto msbuild dirs.proj /p:UseSharedCompilation=false /t:rebuild /p:MvcBuildViews=true"] = 0; - Actions.RunProcess[@"C:\odasa\tools\java/bin/java -jar C:\odasa/tools/extractor-asp.jar ."] = 0; + Actions.RunProcess[@"C:\codeql\tools\java/bin/java -jar C:\codeql\csharp/tools/extractor-asp.jar ."] = 0; Actions.RunProcess[@"C:\odasa/tools/odasa index --xml --extensions config csproj props xml"] = 0; Actions.FileExists["csharp.log"] = true; Actions.FileExists["a/test.csproj"] = true; Actions.FileExists["dirs.proj"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.EnumerateFiles[@"C:\Project"] = "a/test.cs\na/test.csproj\ndirs.proj"; Actions.EnumerateDirectories[@"C:\Project"] = ""; @@ -1083,8 +1086,8 @@ namespace Semmle.Extraction.Tests public void TestCyclicDirsProj() { Actions.FileExists["dirs.proj"] = true; - Actions.GetEnvironmentVariable["TRAP_FOLDER"] = null; - Actions.GetEnvironmentVariable["SOURCE_ARCHIVE"] = null; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = ""; + Actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = ""; Actions.FileExists["csharp.log"] = false; Actions.EnumerateFiles[@"C:\Project"] = "dirs.proj"; Actions.EnumerateDirectories[@"C:\Project"] = ""; diff --git a/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs index a39481545d8..b5b3dfcf61d 100644 --- a/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/AspBuildRule.cs @@ -1,6 +1,4 @@ -using System.IO; - -namespace Semmle.Autobuild +namespace Semmle.Autobuild { /// /// ASP extraction. @@ -9,10 +7,14 @@ namespace Semmle.Autobuild { public BuildScript Analyse(Autobuilder builder, bool auto) { + (var javaHome, var dist) = + builder.CodeQLJavaHome != null ? + (builder.CodeQLJavaHome, builder.CodeQLExtractorCSharpRoot) : + (builder.SemmleJavaHome, builder.SemmleDist); var command = new CommandBuilder(builder.Actions). - RunCommand(builder.Actions.PathCombine(builder.SemmleJavaHome, "bin", "java")). + RunCommand(builder.Actions.PathCombine(javaHome, "bin", "java")). Argument("-jar"). - QuoteArgument(builder.Actions.PathCombine(builder.SemmleDist, "tools", "extractor-asp.jar")). + QuoteArgument(builder.Actions.PathCombine(dist, "tools", "extractor-asp.jar")). Argument("."); return command.Script; } diff --git a/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs b/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs index 4a2e906fc81..740b740f9fb 100644 --- a/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs +++ b/csharp/autobuilder/Semmle.Autobuild/AutobuildOptions.cs @@ -29,6 +29,7 @@ namespace Semmle.Autobuild public bool NugetRestore; public Language Language; + public bool Indexing; /// /// Reads options from environment variables. @@ -53,6 +54,7 @@ namespace Semmle.Autobuild NugetRestore = actions.GetEnvironmentVariable(prefix + "NUGET_RESTORE").AsBool("nuget_restore", true); Language = actions.GetEnvironmentVariable("LGTM_PROJECT_LANGUAGE").AsLanguage(); + Indexing = !actions.GetEnvironmentVariable("CODEQL_AUTOBUILDER_CSHARP_NO_INDEXING").AsBool("no_indexing", false); } } diff --git a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs b/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs index a5a463d01a3..001934f489d 100644 --- a/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs +++ b/csharp/autobuilder/Semmle.Autobuild/Autobuilder.cs @@ -176,14 +176,18 @@ namespace Semmle.Autobuild return ret ?? new List(); }); + CodeQLExtractorCSharpRoot = Actions.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_ROOT"); + + CodeQLJavaHome = Actions.GetEnvironmentVariable("CODEQL_JAVA_HOME"); + SemmleDist = Actions.GetEnvironmentVariable("SEMMLE_DIST"); SemmleJavaHome = Actions.GetEnvironmentVariable("SEMMLE_JAVA_HOME"); SemmlePlatformTools = Actions.GetEnvironmentVariable("SEMMLE_PLATFORM_TOOLS"); - if (SemmleDist == null) - Log(Severity.Error, "The environment variable SEMMLE_DIST has not been set."); + if (CodeQLExtractorCSharpRoot == null && SemmleDist == null) + Log(Severity.Error, "The environment variables CODEQL_EXTRACTOR_CSHARP_ROOT and SEMMLE_DIST have not been set."); } readonly ILogger logger = new ConsoleLogger(Verbosity.Info); @@ -259,9 +263,9 @@ namespace Semmle.Autobuild break; case CSharpBuildStrategy.Auto: var cleanTrapFolder = - BuildScript.DeleteDirectory(Actions.GetEnvironmentVariable("TRAP_FOLDER")); + BuildScript.DeleteDirectory(Actions.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_TRAP_DIR") ?? Actions.GetEnvironmentVariable("TRAP_FOLDER")); var cleanSourceArchive = - BuildScript.DeleteDirectory(Actions.GetEnvironmentVariable("SOURCE_ARCHIVE")); + BuildScript.DeleteDirectory(Actions.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR") ?? Actions.GetEnvironmentVariable("SOURCE_ARCHIVE")); var tryCleanExtractorArgsLogs = BuildScript.Create(actions => { @@ -361,6 +365,16 @@ namespace Semmle.Autobuild return 1; }); + /// + /// Value of CODEQL_EXTRACTOR_CSHARP_ROOT environment variable. + /// + public string CodeQLExtractorCSharpRoot { get; private set; } + + /// + /// Value of CODEQL_JAVA_HOME environment variable. + /// + public string CodeQLJavaHome { get; private set; } + /// /// Value of SEMMLE_DIST environment variable. /// @@ -380,5 +394,12 @@ namespace Semmle.Autobuild /// The absolute path of the odasa executable. /// public string Odasa => SemmleDist == null ? null : Actions.PathCombine(SemmleDist, "tools", "odasa"); + + /// + /// Construct a command that executed the given wrapped in + /// an odasa --index, unless indexing has been disabled, in which case + /// is run directly. + /// + internal CommandBuilder MaybeIndex(CommandBuilder builder, string cmd) => Options.Indexing ? builder.IndexCommand(Odasa, cmd) : builder.RunCommand(cmd); } } diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs b/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs index 8ae3ee60966..09c41ccf74c 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/BuildCommandAutoRule.cs @@ -52,7 +52,7 @@ namespace Semmle.Autobuild if (vsTools != null) command.CallBatFile(vsTools.Path); - command.IndexCommand(builder.Odasa, scriptPath); + builder.MaybeIndex(command, scriptPath); return command.Script; }); } diff --git a/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs b/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs index 8019b5798bc..2e582b707f2 100644 --- a/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/BuildCommandRule.cs @@ -19,7 +19,7 @@ var vsTools = MsBuildRule.GetVcVarsBatFile(builder); if (vsTools != null) command.CallBatFile(vsTools.Path); - command.IndexCommand(builder.Odasa, builder.Options.BuildCommand); + builder.MaybeIndex(command, builder.Options.BuildCommand); return command.Script; }); diff --git a/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs b/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs index 105e466071b..789d68db30a 100644 --- a/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/DotNetRule.cs @@ -196,6 +196,7 @@ Invoke-Command -ScriptBlock $ScriptBlock"; { var curl = new CommandBuilder(builder.Actions). RunCommand("curl"). + Argument("-L"). Argument("-sO"). Argument("https://dot.net/v1/dotnet-install.sh"); @@ -259,13 +260,12 @@ Invoke-Command -ScriptBlock $ScriptBlock"; CommandBuilder GetBuildCommand(Autobuilder builder, (string DotNetPath, IDictionary Environment)? arg) { - var build = new CommandBuilder(builder.Actions, null, arg?.Environment). - IndexCommand(builder.Odasa, DotNetCommand(builder.Actions, arg?.DotNetPath)). + var build = new CommandBuilder(builder.Actions, null, arg?.Environment); + return builder.MaybeIndex(build, DotNetCommand(builder.Actions, arg?.DotNetPath)). Argument("build"). Argument("--no-incremental"). Argument("/p:UseSharedCompilation=false"). Argument(builder.Options.DotNetArguments); - return build; } } } diff --git a/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs index 593039919ed..b0ee01b8d3c 100644 --- a/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/MsBuildRule.cs @@ -36,7 +36,10 @@ namespace Semmle.Autobuild builder.Log(Severity.Warning, "Could not find a suitable version of vcvarsall.bat"); } - var nuget = builder.Actions.PathCombine(builder.SemmlePlatformTools, "csharp", "nuget", "nuget.exe"); + var nuget = + builder.SemmlePlatformTools != null ? + builder.Actions.PathCombine(builder.SemmlePlatformTools, "csharp", "nuget", "nuget.exe") : + "nuget"; var ret = BuildScript.Success; @@ -56,7 +59,7 @@ namespace Semmle.Autobuild if (vsTools != null) command.CallBatFile(vsTools.Path); - command.IndexCommand(builder.Odasa, MsBuild); + builder.MaybeIndex(command, MsBuild); command.QuoteArgument(projectOrSolution.FullPath); command.Argument("/p:UseSharedCompilation=false"); diff --git a/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs b/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs index 8d34fde6413..5814c2b63d2 100644 --- a/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs +++ b/csharp/autobuilder/Semmle.Autobuild/XmlBuildRule.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Semmle.Autobuild +namespace Semmle.Autobuild { /// /// XML extraction. @@ -9,6 +7,9 @@ namespace Semmle.Autobuild { public BuildScript Analyse(Autobuilder builder, bool auto) { + if (!builder.Options.Indexing) + return BuildScript.Success; + var command = new CommandBuilder(builder.Actions). RunCommand(builder.Odasa). Argument("index --xml --extensions config csproj props xml"); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs index acabad9e349..7ca12e00f26 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor.cs @@ -405,22 +405,23 @@ namespace Semmle.Extraction.CSharp static string GetCSharpLogDirectory() { - string snapshot = Environment.GetEnvironmentVariable("ODASA_SNAPSHOT"); - string buildErrorDir = Environment.GetEnvironmentVariable("ODASA_BUILD_ERROR_DIR"); - string traps = Environment.GetEnvironmentVariable("TRAP_FOLDER"); + var codeQlLogDir = Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_LOG_DIR"); + if (!string.IsNullOrEmpty(codeQlLogDir)) + return codeQlLogDir; + + var snapshot = Environment.GetEnvironmentVariable("ODASA_SNAPSHOT"); if (!string.IsNullOrEmpty(snapshot)) - { return Path.Combine(snapshot, "log"); - } + + var buildErrorDir = Environment.GetEnvironmentVariable("ODASA_BUILD_ERROR_DIR"); if (!string.IsNullOrEmpty(buildErrorDir)) - { // Used by `qltest` return buildErrorDir; - } + + var traps = Environment.GetEnvironmentVariable("TRAP_FOLDER"); if (!string.IsNullOrEmpty(traps)) - { return traps; - } + return Directory.GetCurrentDirectory(); } } diff --git a/csharp/extractor/Semmle.Extraction/Layout.cs b/csharp/extractor/Semmle.Extraction/Layout.cs index cf6cad327cd..d59e455083d 100644 --- a/csharp/extractor/Semmle.Extraction/Layout.cs +++ b/csharp/extractor/Semmle.Extraction/Layout.cs @@ -100,8 +100,8 @@ namespace Semmle.Extraction /// Default constructor reads parameters from the environment. /// public Layout() : this( - Environment.GetEnvironmentVariable("TRAP_FOLDER"), - Environment.GetEnvironmentVariable("SOURCE_ARCHIVE"), + Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_TRAP_DIR") ?? Environment.GetEnvironmentVariable("TRAP_FOLDER"), + Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR") ?? Environment.GetEnvironmentVariable("SOURCE_ARCHIVE"), Environment.GetEnvironmentVariable("ODASA_CSHARP_LAYOUT")) { } diff --git a/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.qhelp b/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.qhelp new file mode 100644 index 00000000000..1ac3a59befc --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.qhelp @@ -0,0 +1,20 @@ + + + + +

    + A write that looks like it may be bypassing runtime checks. +

    + +
    + + +
  • +OWASP: +Data Validation. +
  • + +
    +
    diff --git a/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql b/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql new file mode 100644 index 00000000000..438c566361b --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-020/RuntimeChecksBypass.ql @@ -0,0 +1,35 @@ +/** + * @name Serialization check bypass + * @description A write that looks like it may be bypassing runtime checks. + * @kind problem + * @id cs/serialization-check-bypass + * @problem.severity warning + * @tags security + * external/cwe/cwe-20 + */ + +/* + * consider: @precision medium + */ + +import semmle.code.csharp.serialization.Serialization + +/** + * The result is a write to the field `f`, assigning it the value + * of variable `v` which was checked by the condition `check`. + */ +Expr checkedWrite(Field f, Variable v, IfStmt check) { + result = v.getAnAccess() and + result = f.getAnAssignedValue() and + check.getCondition() = v.getAnAccess().getParent*() and + result.getAControlFlowNode() = check.getAControlFlowNode().getASuccessor*() +} + +from BinarySerializableType t, Field f, IfStmt check, Expr write, Expr unsafeWrite +where + f = t.getASerializedField() and + write = checkedWrite(f, t.getAConstructor().getAParameter(), check) and + unsafeWrite = f.getAnAssignedValue() and + t.getADeserializationCallback() = unsafeWrite.getEnclosingCallable() and + not t.getADeserializationCallback().calls*(checkedWrite(f, _, _).getEnclosingCallable()) +select unsafeWrite, "This write to $@ may be circumventing a $@.", f, f.toString(), check, "check" diff --git a/csharp/ql/src/Security Features/CWE-091/XMLInjection.qhelp b/csharp/ql/src/Security Features/CWE-091/XMLInjection.qhelp new file mode 100644 index 00000000000..3aff9901bfc --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-091/XMLInjection.qhelp @@ -0,0 +1,45 @@ + + + +

    +The APIs provided by the .NET libraries for XML manipulation allow the insertion of "raw" text at +a specified point in an XML document. If user input is passed to this API, it could allow a +malicious user to add extra content that could corrupt or supersede existing content, or enable +unintended additional functionality. +

    +
    + +

    +Avoid using the WriteRaw method on System.Xml.XmlWriter with user input. +If possible, use the high-level APIs to write new XML elements to a document, as these automatically +escape user content. If that is not possible, then user input should be escaped before being +included in a string that will be used with the WriteRaw API. +

    +
    + +

    In this example, user input is provided describing the name of an employee to add to an XML +document representing a set of names. The WriteRaw API is used to write the new +employee record to the XML file.

    + +

    However, if a malicious user were to provide the content + Bobby Pages</name></employee><employee><name>Hacker1, they +would be able to add an extra entry into the XML file. +

    +

    The corrected version demonstrates two ways to avoid this issue. The first is to escape user +input before passing it to the WriteRaw API, which prevents a malicious user from +closing or opening XML tags. The second approach uses the high level XML API to add XML elements, +which ensures the content is appropriately escaped.

    + +
    + + +
  • + Web Application Security Consortium: XML Injection. +
  • +
  • + Microsoft Docs: WriteRaw. +
  • +
    +
    diff --git a/csharp/ql/src/Security Features/CWE-091/XMLInjection.ql b/csharp/ql/src/Security Features/CWE-091/XMLInjection.ql new file mode 100644 index 00000000000..16e897ab2e9 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-091/XMLInjection.ql @@ -0,0 +1,53 @@ +/** + * @name XML injection + * @description Building an XML document from user-controlled sources is vulnerable to insertion of + * malicious code by the user. + * @kind problem + * @id cs/xml-injection + * @problem.severity error + * @tags security + * external/cwe/cwe-091 + */ + +/* + * consider: @precision high + */ + +import csharp +import semmle.code.csharp.dataflow.flowsources.Remote +import semmle.code.csharp.frameworks.system.Xml + +/** + * A taint-tracking configuration for untrusted user input used in XML. + */ +class TaintTrackingConfiguration extends TaintTracking::Configuration { + TaintTrackingConfiguration() { this = "XMLInjection" } + + override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node sink) { + exists(MethodCall mc | + mc.getTarget().hasName("WriteRaw") and + mc.getTarget().getDeclaringType().getABaseType*().hasQualifiedName("System.Xml.XmlWriter") + | + mc.getArgument(0) = sink.asExpr() + ) + } + + override predicate isSanitizer(DataFlow::Node node) { + exists(MethodCall mc | + mc.getTarget().hasName("Escape") and + mc + .getTarget() + .getDeclaringType() + .getABaseType*() + .hasQualifiedName("System.Security.SecurityElement") + | + mc = node.asExpr() + ) + } +} + +from TaintTrackingConfiguration c, DataFlow::Node source, DataFlow::Node sink +where c.hasFlow(source, sink) +select sink, "$@ flows to here and is inserted as XML.", source, "User-provided value" diff --git a/csharp/ql/src/Security Features/CWE-091/XMLInjectionBad.cs b/csharp/ql/src/Security Features/CWE-091/XMLInjectionBad.cs new file mode 100644 index 00000000000..522e6606580 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-091/XMLInjectionBad.cs @@ -0,0 +1,21 @@ +using System; +using System.Security; +using System.Web; +using System.Xml; + +public class XMLInjectionHandler : IHttpHandler { + public void ProcessRequest(HttpContext ctx) { + string employeeName = ctx.Request.QueryString["employeeName"]; + + using (XmlWriter writer = XmlWriter.Create("employees.xml")) + { + writer.WriteStartDocument(); + + // BAD: Insert user input directly into XML + writer.WriteRaw("" + employeeName + ""); + + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + } +} \ No newline at end of file diff --git a/csharp/ql/src/Security Features/CWE-091/XMLInjectionGood.cs b/csharp/ql/src/Security Features/CWE-091/XMLInjectionGood.cs new file mode 100644 index 00000000000..be04ea7acb3 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-091/XMLInjectionGood.cs @@ -0,0 +1,25 @@ +using System; +using System.Security; +using System.Web; +using System.Xml; + +public class XMLInjectionHandler : IHttpHandler { + public void ProcessRequest(HttpContext ctx) { + string employeeName = ctx.Request.QueryString["employeeName"]; + + using (XmlWriter writer = XmlWriter.Create("employees.xml")) + { + writer.WriteStartDocument(); + + // GOOD: Escape user input before inserting into string + writer.WriteRaw("" + SecurityElement.Escape(employeeName) + ""); + + // GOOD: Use standard API, which automatically encodes values + writer.WriteStartElement("Employee"); + writer.WriteElementString("Name", employeeName); + writer.WriteEndElement(); + + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + } \ No newline at end of file diff --git a/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.qhelp b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.qhelp new file mode 100644 index 00000000000..e1dbe9c1bd0 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.qhelp @@ -0,0 +1,37 @@ + + + +

    +C# supports runtime loading of assemblies by path through the use of the +System.Reflection.Assembly API. If an external user can influence the path used to +load an assembly, then the application can potentially be tricked into loading an assembly which +was not intended to be loaded, and executing arbitrary code. +

    +
    + +

    +Avoid loading assemblies based on user provided input. If this is not possible, ensure that the path +is validated before being used with Assembly. For example, compare the provided input +against a whitelist of known safe assemblies, or confirm that the path is restricted to a single +directory which only contains safe assemblies. +

    +
    + +

    In this example, user input is provided describing the path to an assembly, which is loaded +without validation. This is problematic because it allows the user to load any assembly installed +on the system, and is particularly problematic if an attacker can upload a custom DLL elsewhere on +the system.

    + +

    In the corrected version, user input is validated against one of two options, and the assembly +is only loaded if the user input matches one of those options.

    + +
    + + +
  • Microsoft: + System.Reflection.Assembly. +
  • +
    +
    diff --git a/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql new file mode 100644 index 00000000000..7adcaa22d09 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjection.ql @@ -0,0 +1,66 @@ +/** + * @name Assembly path injection + * @description Loading a .NET assembly based on a path constructed from user-controlled sources + * may allow a malicious user to load code which modifies the program in unintended + * ways. + * @kind problem + * @id cs/assembly-path-injection + * @problem.severity error + * @tags security + * external/cwe/cwe-114 + */ + +/* + * consider: @precision high + */ + +import csharp +import semmle.code.csharp.dataflow.flowsources.Remote + +class MainMethod extends Method { + MainMethod() { + this.hasName("Main") and + this.isStatic() and + (this.getReturnType() instanceof VoidType or this.getReturnType() instanceof IntType) and + if this.getNumberOfParameters() = 1 + then this.getParameter(0).getType().(ArrayType).getElementType() instanceof StringType + else this.getNumberOfParameters() = 0 + } +} + +/** + * A taint-tracking configuration for untrusted user input used to load a DLL. + */ +class TaintTrackingConfiguration extends TaintTracking::Configuration { + TaintTrackingConfiguration() { this = "DLLInjection" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RemoteFlowSource or + source.asExpr() = any(MainMethod main).getParameter(0).getAnAccess() + } + + override predicate isSink(DataFlow::Node sink) { + exists(MethodCall mc, string name, int arg | + mc.getTarget().getName().matches(name) and + mc + .getTarget() + .getDeclaringType() + .getABaseType*() + .hasQualifiedName("System.Reflection.Assembly") and + mc.getArgument(arg) = sink.asExpr() + | + name = "LoadFrom" and arg = 0 and mc.getNumberOfArguments() = [1 .. 2] + or + name = "LoadFile" and arg = 0 + or + name = "LoadWithPartialName" and arg = 0 + or + name = "UnsafeLoadFrom" and arg = 0 + ) + } +} + +from TaintTrackingConfiguration c, DataFlow::Node source, DataFlow::Node sink +where c.hasFlow(source, sink) +select sink, "$@ flows to here and is used as the path to dynamically load an assembly.", source, + "User-provided value" diff --git a/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjectionBad.cs b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjectionBad.cs new file mode 100644 index 00000000000..e037b34c95c --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjectionBad.cs @@ -0,0 +1,18 @@ +using System; +using System.Web; +using System.Reflection; + +public class AssemblyPathInjectionHandler : IHttpHandler { + public void ProcessRequest(HttpContext ctx) { + string assemblyPath = ctx.Request.QueryString["assemblyPath"]; + + // BAD: Load assembly based on user input + var badAssembly = Assembly.LoadFile(assemblyPath); + + // Method called on loaded assembly. If the user can control the loaded assembly, then this + // could result in a remote code execution vulnerability + MethodInfo m = badAssembly.GetType("Config").GetMethod("GetCustomPath"); + Object customPath = m.Invoke(null, null); + // ... + } +} \ No newline at end of file diff --git a/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjectionGood.cs b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjectionGood.cs new file mode 100644 index 00000000000..c980e8514fb --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-114/AssemblyPathInjectionGood.cs @@ -0,0 +1,19 @@ +using System; +using System.Web; +using System.Reflection; + +public class AssemblyPathInjectionHandler : IHttpHandler { + public void ProcessRequest(HttpContext ctx) { + string configType = ctx.Request.QueryString["configType"]; + + if (configType.equals("configType1") || configType.equals("configType2")) { + // GOOD: Loaded assembly is one of the two known safe options + var safeAssembly = Assembly.LoadFile(@"C:\SafeLibraries\" + configType + ".dll"); + + // Code execution is limited to one of two known and vetted assemblies + MethodInfo m = safeAssembly.GetType("Config").GetMethod("GetCustomPath"); + Object customPath = m.Invoke(null, null); + // ... + } + } +} \ No newline at end of file diff --git a/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql b/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql new file mode 100644 index 00000000000..cce122ffa62 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKey.ql @@ -0,0 +1,39 @@ +/** + * @name Hard-coded encryption key + * @description The .Key property or rgbKey parameter of a SymmetricAlgorithm should never be a hard-coded value. + * @kind problem + * @id cs/hardcoded-key + * @problem.severity error + * @tags security + */ + +/* + * consider: @precision high + */ + +import csharp +import semmle.code.csharp.security.cryptography.EncryptionKeyDataFlow::EncryptionKeyDataFlow + +/** + * The creation of a literal byte array. + */ +class ByteArrayLiteralSource extends KeySource { + ByteArrayLiteralSource() { + this.asExpr() = any(ArrayCreation ac | + ac.getArrayType().getElementType() instanceof ByteType and + ac.hasInitializer() + ) + } +} + +/** + * Any string literal as a source + */ +class StringLiteralSource extends KeySource { + StringLiteralSource() { this.asExpr() instanceof StringLiteral } +} + +from SymmetricKeyTaintTrackingConfiguration keyFlow, KeySource src, SymmetricEncryptionKeySink sink +where keyFlow.hasFlow(src, sink) +select sink, "Hard-coded symmetric $@ is used in symmetric algorithm in " + sink.getDescription(), + src, "key" diff --git a/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKeyBad.cs b/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKeyBad.cs new file mode 100644 index 00000000000..4ce81a349f7 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-321/HardcodedEncryptionKeyBad.cs @@ -0,0 +1,8 @@ +using System.Security.Cryptography; + +var b = new AesCryptoServiceProvider() +{ + // BAD: explicit key assignment, hard-coded value + Key = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 } +}; + diff --git a/csharp/ql/src/Security Features/CWE-321/HardcodedSymmetricEncryptionKey.ql b/csharp/ql/src/Security Features/CWE-321/HardcodedSymmetricEncryptionKey.ql new file mode 100644 index 00000000000..4113651677b --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-321/HardcodedSymmetricEncryptionKey.ql @@ -0,0 +1,22 @@ +/** + * @name Hard-coded symmetric encryption key + * @description The .Key property or rgbKey parameter of a SymmetricAlgorithm should never be a hardcoded value. + * @kind path-problem + * @id cs/hard-coded-symmetric-encryption-key + * @problem.severity error + * @tags security + */ + +/* + * consider: @precision high + */ + +import csharp +import semmle.code.csharp.security.cryptography.HardcodedSymmetricEncryptionKey::HardcodedSymmetricEncryptionKey +import DataFlow::PathGraph + +from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink +where c.hasFlowPath(source, sink) +select sink.getNode(), source, sink, + "Hard-coded symmetric $@ is used in symmetric algorithm in " + + sink.getNode().(Sink).getDescription() + ".", source.getNode(), "key" diff --git a/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.qhelp b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.qhelp new file mode 100644 index 00000000000..c59feeed61c --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.qhelp @@ -0,0 +1,44 @@ + + + + +

    + SQL Server connections where the client is not enforcing the encryption in transit are susceptible to multiple attacks, including a man-in-the-middle, that would potentially compromise the user credentials and/or the TDS session. +

    + +
    + + +

    Ensure that the client code enforces the Encrypt option by setting it to true in the connection string.

    + +
    + + +

    The following example shows a SQL connection string that is not explicitly enabling the Encrypt setting to force encryption.

    + + + +

    + The following example shows a SQL connection string that is explicitly enabling the Encrypt setting to force encryption in transit. +

    + + + +
    + +
  • Microsoft, SQL Protocols blog: + Selectively using secure connection to SQL Server. +
  • +
  • Microsoft: + SqlConnection.ConnectionString Property. +
  • +
  • Microsoft: + Using Connection String Keywords with SQL Server Native Client. +
  • +
  • Microsoft: + Setting the connection properties. +
  • +
    +
    diff --git a/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql new file mode 100644 index 00000000000..78bcc1c19e5 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnection.ql @@ -0,0 +1,47 @@ +/** + * @name Insecure SQL connection + * @description Using an SQL Server connection without enforcing encryption is a security vulnerability. + * @kind path-problem + * @id cs/insecure-sql-connection + * @problem.severity error + * @tags security + * external/cwe/cwe-327 + */ + +/* + * consider: @precision high + */ + +import csharp +import DataFlow::PathGraph + +/** + * A `DataFlow::Configuration` for tracking `Strings passed to SqlConnectionStringBuilder` instances. + */ +class TaintTrackingConfiguration extends TaintTracking::Configuration { + TaintTrackingConfiguration() { this = "TaintTrackingConfiguration" } + + override predicate isSource(DataFlow::Node source) { + exists(string s | s = source.asExpr().(StringLiteral).getValue().toLowerCase() | + s.matches("%encrypt=false%") + or + not s.matches("%encrypt=%") + ) + } + + override predicate isSink(DataFlow::Node sink) { + exists(ObjectCreation oc | + oc.getRuntimeArgument(0) = sink.asExpr() and + ( + oc.getType().getName() = "SqlConnectionStringBuilder" + or + oc.getType().getName() = "SqlConnection" + ) + ) + } +} + +from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink +where c.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ flows to here and does not specify `Encrypt=True`.", + source.getNode(), "Connection string" diff --git a/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnectionBad.cs b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnectionBad.cs new file mode 100644 index 00000000000..d90af93ece7 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnectionBad.cs @@ -0,0 +1,7 @@ +using System.Data.SqlClient; + +// BAD, Encrypt not specified +string connectString = + "Server=1.2.3.4;Database=Anything;Integrated Security=true;"; +SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectString); +var conn = new SqlConnection(builder.ConnectionString); \ No newline at end of file diff --git a/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnectionGood.cs b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnectionGood.cs new file mode 100644 index 00000000000..83e341e2f79 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-327/InsecureSQLConnectionGood.cs @@ -0,0 +1,6 @@ +using System.Data.SqlClient; + +string connectString = + "Server=1.2.3.4;Database=Anything;Integrated Security=true;;Encrypt=true;"; +SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectString); +var conn = new SqlConnection(builder.ConnectionString); \ No newline at end of file diff --git a/csharp/ql/src/Security Features/CWE-502/DeserializedDelegate.qhelp b/csharp/ql/src/Security Features/CWE-502/DeserializedDelegate.qhelp new file mode 100644 index 00000000000..323b8d918bf --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/DeserializedDelegate.qhelp @@ -0,0 +1,35 @@ + + + + +

    Deserializing a delegate object may result in remote code execution, when an +attacker can control the serialized data.

    + +
    + + +

    Avoid deserializing delegate objects, if possible, or make sure that the serialized +data cannot be controlled by an attacker.

    + +
    + + +

    In this example, a file stream is deserialized to a Func<int> +object, using a BinaryFormatter. The file stream is a parameter of a public +method, so depending on the calls to InvokeSerialized, this may or may not +pose a security problem.

    + + + +
    + + +
  • +Microsoft: +BinaryFormatter Class. +
  • + +
    +
    diff --git a/csharp/ql/src/Security Features/CWE-502/DeserializedDelegate.ql b/csharp/ql/src/Security Features/CWE-502/DeserializedDelegate.ql new file mode 100644 index 00000000000..31d28311908 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/DeserializedDelegate.ql @@ -0,0 +1,22 @@ +/** + * @name Deserialized delegate + * @description Deserializing a delegate allows for remote code execution when an + * attacker can control the serialized data. + * @kind problem + * @id cs/deserialized-delegate + * @problem.severity warning + * @precision high + * @tags security + * external/cwe/cwe-502 + */ + +import csharp +import semmle.code.csharp.frameworks.system.linq.Expressions +import semmle.code.csharp.serialization.Deserializers + +from Call deserialization, Cast cast +where + deserialization.getTarget() instanceof UnsafeDeserializer and + cast.getExpr() = deserialization and + cast.getTargetType() instanceof SystemLinqExpressions::DelegateExtType +select deserialization, "Deserialization of delegate type." diff --git a/csharp/ql/src/Security Features/CWE-502/DeserializedDelegateBad.cs b/csharp/ql/src/Security Features/CWE-502/DeserializedDelegateBad.cs new file mode 100644 index 00000000000..12b2dc76987 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/DeserializedDelegateBad.cs @@ -0,0 +1,14 @@ +using System; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; + +class Bad +{ + public static int InvokeSerialized(FileStream fs) + { + var formatter = new BinaryFormatter(); + // BAD + var f = (Func)formatter.Deserialize(fs); + return f(); + } +} diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.qhelp b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.qhelp new file mode 100644 index 00000000000..7acfd20fe3a --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.qhelp @@ -0,0 +1,38 @@ + + + + +

    Deserializing an object from untrusted input may result in security problems, such +as denial-of-service or remote code execution.

    + +
    + + +

    Avoid using an unsafe deserialization framework.

    + +
    + + +

    In this example, a string is deserialized using a +JavaScriptSerializer with a simple type resolver. Using a type resolver +means that arbitrary code may be executed.

    + + + +

    To fix this specific vulnerability, we avoid using a type resolver. In other cases, +it may be necessary to use a different deserialization framework.

    + + + +
    + + +
  • +Muñoz, Alvaro and Mirosh, Oleksandr: +JSON Attacks. +
  • + +
    +
    diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.ql b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.ql new file mode 100644 index 00000000000..edd0e15c247 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.ql @@ -0,0 +1,22 @@ +/** + * @name Unsafe deserializer + * @description Calling an unsafe deserializer with data controlled by an attacker + * can lead to denial of service and other security problems. + * @kind problem + * @id cs/unsafe-deserialization + * @problem.severity warning + * @tags security + * external/cwe/cwe-502 + */ + +/* + * consider: @precision low + */ + +import csharp +import UnsafeDeserialization::UnsafeDeserialization + +from Call deserializeCall, Sink sink +where deserializeCall.getAnArgument() = sink.asExpr() +select deserializeCall, + "Unsafe deserializer is used. Make sure the value being deserialized comes from a trusted source." diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.qll b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.qll new file mode 100644 index 00000000000..4e401eb4804 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserialization.qll @@ -0,0 +1,83 @@ +/** + * Provides a taint-tracking configuration for reasoning about uncontrolled data + * in calls to unsafe deserializers (XML, JSON, XAML). + */ + +import csharp + +module UnsafeDeserialization { + private import semmle.code.csharp.dataflow.flowsources.Remote + private import semmle.code.csharp.dataflow.flowsources.Remote + private import semmle.code.csharp.serialization.Deserializers + + /** + * A data flow source for unsafe deserialization vulnerabilities. + */ + abstract class Source extends DataFlow::Node { } + + /** + * A data flow sink for unsafe deserialization vulnerabilities. + */ + abstract class Sink extends DataFlow::Node { } + + /** + * A sanitizer for unsafe deserialization vulnerabilities. + */ + abstract class Sanitizer extends DataFlow::Node { } + + /** + * A taint-tracking configuration for reasoning about unsafe deserialization. + */ + class TaintTrackingConfig extends TaintTracking::Configuration { + TaintTrackingConfig() { this = "UnsafeDeserialization" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + } + + class RemoteSource extends Source { + RemoteSource() { this instanceof RemoteFlowSource } + } + + /** A call to an unsafe deserializer. */ + class UnsafeDeserializerSink extends Sink { + UnsafeDeserializerSink() { + exists(Call c | + this.asExpr() = c.getAnArgument() and + c.getTarget() instanceof UnsafeDeserializer + ) + } + } + + private class JavaScriptSerializerClass extends Class { + JavaScriptSerializerClass() { + this.hasQualifiedName("System.Web.Script.Serialization.JavaScriptSerializer") + } + } + + /** + * An unsafe use of a JavaScript deserializer. That is, a use with a custom type-resolver + * (constructor parameter). + */ + class JavaScriptSerializerSink extends Sink { + JavaScriptSerializerSink() { + exists(ObjectCreation oc | + oc.getTarget().getDeclaringType() instanceof JavaScriptSerializerClass and + oc.getTarget().getNumberOfParameters() > 0 and + exists(MethodCall mc, Method m | + m = mc.getTarget() and + m.getDeclaringType() instanceof JavaScriptSerializerClass and + ( + m.hasName("Deserialize") or + m.hasName("DeserializeObject") + ) and + this.asExpr() = mc.getAnArgument() and + DataFlow::localFlow(DataFlow::exprNode(oc), DataFlow::exprNode(mc.getQualifier())) + ) + ) + } + } +} diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationBad.cs b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationBad.cs new file mode 100644 index 00000000000..385e700a0bf --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationBad.cs @@ -0,0 +1,11 @@ +using System.Web.Script.Serialization; + +class Bad +{ + public static object Deserialize(string s) + { + JavaScriptSerializer sr = new JavaScriptSerializer(new SimpleTypeResolver()); + // BAD + return sr.DeserializeObject(s); + } +} diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationGood.cs b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationGood.cs new file mode 100644 index 00000000000..775862cd683 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationGood.cs @@ -0,0 +1,11 @@ +using System.Web.Script.Serialization; + +class Good +{ + public static object Deserialize(string s) + { + // GOOD + JavaScriptSerializer sr = new JavaScriptSerializer(); + return sr.DeserializeObject(s); + } +} diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.qhelp b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.qhelp new file mode 100644 index 00000000000..ef946f40136 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.qhelp @@ -0,0 +1,39 @@ + + + + +

    Deserializing an object from untrusted input may result in security problems, such +as denial-of-service or remote code execution.

    + +
    + + +

    Avoid deserializing objects from an untrusted source, and if not possible, make sure +to use a safe deserialization framework.

    + +
    + + +

    In this example, text from an HTML text box is deserialized using a +JavaScriptSerializer with a simple type resolver. Using a type resolver +means that arbitrary code may be executed.

    + + + +

    To fix this specific vulnerability, we avoid using a type resolver. In other cases, +it may be necessary to use a different deserialization framework.

    + + + +
    + + +
  • +Muñoz, Alvaro and Mirosh, Oleksandr: +JSON Attacks. +
  • + +
    +
    diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.ql b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.ql new file mode 100644 index 00000000000..b0b659857ab --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInput.ql @@ -0,0 +1,23 @@ +/** + * @name Deserialization of untrusted data + * @description Calling an unsafe deserializer with data controlled by an attacker + * can lead to denial of service and other security problems. + * @kind path-problem + * @id cs/unsafe-deserialization-untrusted-input + * @problem.severity error + * @tags security + * external/cwe/cwe-502 + */ + +/* + * consider: @precision high + */ + +import csharp +import UnsafeDeserialization::UnsafeDeserialization +import DataFlow::PathGraph + +from TaintTrackingConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, "$@ flows to unsafe deserializer.", source.getNode(), + "User-provided data" diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInputBad.cs b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInputBad.cs new file mode 100644 index 00000000000..db2b25097ba --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInputBad.cs @@ -0,0 +1,12 @@ +using System.Web.UI.WebControls; +using System.Web.Script.Serialization; + +class Bad +{ + public static object Deserialize(TextBox textBox) + { + JavaScriptSerializer sr = new JavaScriptSerializer(new SimpleTypeResolver()); + // BAD + return sr.DeserializeObject(textBox.Text); + } +} diff --git a/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInputGood.cs b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInputGood.cs new file mode 100644 index 00000000000..d1e2935b218 --- /dev/null +++ b/csharp/ql/src/Security Features/CWE-502/UnsafeDeserializationUntrustedInputGood.cs @@ -0,0 +1,12 @@ +using System.Web.UI.WebControls; +using System.Web.Script.Serialization; + +class Good +{ + public static object Deserialize(TextBox textBox) + { + JavaScriptSerializer sr = new JavaScriptSerializer(); + // GOOD + return sr.DeserializeObject(textBox.Text); + } +} diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index d1ca7596f30..e69d5d93bf5 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -86,6 +86,10 @@ module LocalFlow { scope = e2 and isSuccessor = true or + e1 = e2.(NullCoalescingExpr).getAnOperand() and + scope = e2 and + isSuccessor = false + or e1 = e2.(SuppressNullableWarningExpr).getExpr() and scope = e2 and isSuccessor = true diff --git a/csharp/ql/src/semmle/code/csharp/frameworks/system/security/cryptography/SymmetricAlgorithm.qll b/csharp/ql/src/semmle/code/csharp/frameworks/system/security/cryptography/SymmetricAlgorithm.qll new file mode 100644 index 00000000000..d0b9c2e5239 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/frameworks/system/security/cryptography/SymmetricAlgorithm.qll @@ -0,0 +1,137 @@ +/** + * Provides classes for working with symmetric cryptography algorithms. + */ + +import csharp + +/** + * Holds if the object creation `oc` is the creation of the reference type with the specified `qualifiedName`, or a class derived from + * the class with the specified `qualifiedName`. + */ +private predicate isCreatingObject(ObjectCreation oc, string qualifiedName) { + exists(RefType t | t = oc.getType() | t.getBaseClass*().hasQualifiedName(qualifiedName)) +} + +/** + * Holds if the method call `mc` is returning the reference type with the specified `qualifiedName`. + * and the target of the method call is a library method. + */ +private predicate isReturningObject(MethodCall mc, string qualifiedName) { + mc.getTarget().fromLibrary() and + exists(RefType t | t = mc.getType() | t.hasQualifiedName(qualifiedName)) +} + +/** + * Holds if the method call `mc` is a call on the library method target with the specified `qualifiedName` and `methodName`, and an argument at + * index `argumentIndex` has the specified value `argumentValue` (case-insensitive). + */ +bindingset[argumentValue] +private predicate isMethodCalledWithArg( + MethodCall mc, string qualifiedName, string methodName, int argumentIndex, string argumentValue +) { + mc.getTarget().fromLibrary() and + mc.getTarget().hasQualifiedName(qualifiedName, methodName) and + mc.getArgument(argumentIndex).getValue().toUpperCase() = argumentValue.toUpperCase() +} + +/** + * The class `System.Security.Cryptography.SymmetricAlgorithm` or any sub class of this class. + */ +class SymmetricAlgorithm extends Class { + SymmetricAlgorithm() { + this.getABaseType*().hasQualifiedName("System.Security.Cryptography", "SymmetricAlgorithm") + } + + /** Gets the `IV` property. */ + Property getIVProperty() { result = this.getProperty("IV") } + + /** Gets the 'Key' property. */ + Property getKeyProperty() { result = this.getProperty("Key") } + + /** Gets a method call that constructs a symmetric encryptor. */ + MethodCall getASymmetricEncryptor() { result.getTarget() = this.getAMethod("CreateEncryptor") } + + /** Gets a method call that constructs a symmetric decryptor. */ + MethodCall getASymmetricDecryptor() { result.getTarget() = this.getAMethod("CreateDecryptor") } +} + +/** + * Holds if the expression 'e' creates DES symmetric algorithm. + * Note: not all of the class names are supported on all platforms. + */ +predicate isCreatingDES(Expr e) { + isCreatingObject(e, "System.Security.Cryptography.DES") or + isReturningObject(e, "System.Security.Cryptography.DES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, "DES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, + "System.Security.Cryptography.DES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, "DES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, + "System.Security.Cryptography.DES") +} + +/** + * Holds if the expression 'e' creates Triple DES symmetric algorithm. + * Note: not all of the class names are supported on all platforms. + */ +predicate isCreatingTripleDES(Expr e) { + isCreatingObject(e, "System.Security.Cryptography.TripleDES") or + isReturningObject(e, "System.Security.Cryptography.TripleDES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, + "TripleDES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, "3DES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, + "Triple DES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, + "System.Security.Cryptography.TripleDES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, + "TripleDES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, "3DES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, + "Triple DES") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, + "System.Security.Cryptography.TripleDES") +} + +/** + * Holds if the expression 'e' creates RC2 symmetric algorithm. + * Note: not all of the class names are supported on all platforms. + */ +predicate isCreatingRC2(Expr e) { + isCreatingObject(e, "System.Security.Cryptography.RC2") or + isReturningObject(e, "System.Security.Cryptography.RC2") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, "RC2") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, + "System.Security.Cryptography.RC2") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, "RC2") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, + "System.Security.Cryptography.RC2") +} + +/** + * Holds if the expression 'e' creates Rijndael symmetric algorithm. + */ +predicate isCreatingRijndael(Expr e) { + isCreatingObject(e, "System.Security.Cryptography.Rijndael") or + isReturningObject(e, "System.Security.Cryptography.Rijndael") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, + "Rijndael") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, + "RijndaelManaged") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, + "System.Security.Cryptography.Rijndael") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, + "System.Security.Cryptography.RijndaelManaged") or + isMethodCalledWithArg(e, "System.Security.Cryptography.SymmetricAlgorithm", "Create", 0, + "System.Security.Cryptography.SymmetricAlgorithm") or // this creates Rijndael + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, + "Rijndael") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, + "System.Security.Cryptography.Rijndael") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, + "RijndaelManaged") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, + "System.Security.Cryptography.RijndaelManaged") or + isMethodCalledWithArg(e, "System.Security.Cryptography.CryptoConfig", "CreateFromName", 0, + "System.Security.Cryptography.SymmetricAlgorithm") // this creates Rijndael +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/PrintIR.ql b/csharp/ql/src/semmle/code/csharp/ir/PrintIR.ql index c9c836fa252..1f93a39ad6f 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/PrintIR.ql +++ b/csharp/ql/src/semmle/code/csharp/ir/PrintIR.ql @@ -1,7 +1,7 @@ /** * @name Print IR * @description Outputs a representation of the IR graph - * @id charp/print-ir + * @id csharp/print-ir * @kind graph */ diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll new file mode 100644 index 00000000000..0abfa14023d --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/IRType.qll @@ -0,0 +1,247 @@ +/** + * Minimal, language-neutral type system for the IR. + */ + +private import internal.IRTypeInternal + +private newtype TIRType = + TIRVoidType() or + TIRUnknownType() or + TIRErrorType() { Language::hasErrorType() } or + TIRBooleanType(int byteSize) { Language::hasBooleanType(byteSize) } or + TIRSignedIntegerType(int byteSize) { Language::hasSignedIntegerType(byteSize) } or + TIRUnsignedIntegerType(int byteSize) { Language::hasUnsignedIntegerType(byteSize) } or + TIRFloatingPointType(int byteSize) { Language::hasFloatingPointType(byteSize) } or + TIRAddressType(int byteSize) { Language::hasAddressType(byteSize) } or + TIRFunctionAddressType(int byteSize) { Language::hasFunctionAddressType(byteSize) } or + TIROpaqueType(Language::OpaqueTypeTag tag, int byteSize) { + Language::hasOpaqueType(tag, byteSize) + } + +/** + * The language-neutral type of an IR `Instruction`, `Operand`, or `IRVariable`. + * The interface to `IRType` and its subclasses is the same across all languages for which the IR + * is supported, so analyses that expect to be used for multiple languages should generally use + * `IRType` rather than a language-specific type. + * + * Many types from the language-specific type system will map to a single canonical `IRType`. Two + * types that map to the same `IRType` are considered equivalent by the IR. As an example, in C++, + * all pointer types map to the same instance of `IRAddressType`. + */ +class IRType extends TIRType { + string toString() { none() } + + /** + * Gets a string that uniquely identifies this `IRType`. This string is often the same as the + * result of `IRType.toString()`, but for some types it may be more verbose to ensure uniqueness. + */ + string getIdentityString() { result = toString() } + + /** + * Gets the size of the type, in bytes, if known. + * + * This will hold for all `IRType` objects except `IRUnknownType`. + */ + int getByteSize() { none() } + + /** + * Gets a single instance of `LanguageType` that maps to this `IRType`. + */ + Language::LanguageType getCanonicalLanguageType() { none() } +} + +/** + * An unknown type. Generally used to represent results and operands that access an unknown set of + * memory locations, such as the side effects of a function call. + */ +class IRUnknownType extends IRType, TIRUnknownType { + final override string toString() { result = "unknown" } + + final override int getByteSize() { none() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalUnknownType() + } +} + +/** + * A void type, which has no values. Used to represent the result type of an instruction that does + * not produce a result. + */ +class IRVoidType extends IRType, TIRVoidType { + final override string toString() { result = "void" } + + final override int getByteSize() { result = 0 } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalVoidType() + } +} + +/** + * An error type. Used when an error in the source code prevents the extractor from determining the + * proper type. + */ +class IRErrorType extends IRType, TIRErrorType { + final override string toString() { result = "error" } + + final override int getByteSize() { result = 0 } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalErrorType() + } +} + +private class IRSizedType extends IRType { + int byteSize; + + IRSizedType() { + this = TIRBooleanType(byteSize) or + this = TIRSignedIntegerType(byteSize) or + this = TIRUnsignedIntegerType(byteSize) or + this = TIRFloatingPointType(byteSize) or + this = TIRAddressType(byteSize) or + this = TIRFunctionAddressType(byteSize) or + this = TIROpaqueType(_, byteSize) + } + + final override int getByteSize() { result = byteSize } +} + +/** + * A Boolean type, which can hold the values `true` (non-zero) or `false` (zero). + */ +class IRBooleanType extends IRSizedType, TIRBooleanType { + final override string toString() { result = "bool" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalBooleanType(byteSize) + } +} + +/** + * A numberic type. This includes `IRSignedIntegerType`, `IRUnsignedIntegerType`, and + * `IRFloatingPointType`. + */ +class IRNumericType extends IRSizedType { + IRNumericType() { + this = TIRSignedIntegerType(byteSize) or + this = TIRUnsignedIntegerType(byteSize) or + this = TIRFloatingPointType(byteSize) + } +} + +/** + * A signed two's-complement integer. Also used to represent enums whose underlying type is a signed + * integer, as well as character types whose representation is signed. + */ +class IRSignedIntegerType extends IRNumericType, TIRSignedIntegerType { + final override string toString() { result = "int" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalSignedIntegerType(byteSize) + } +} + +/** + * An unsigned two's-complement integer. Also used to represent enums whose underlying type is an + * unsigned integer, as well as character types whose representation is unsigned. + */ +class IRUnsignedIntegerType extends IRNumericType, TIRUnsignedIntegerType { + final override string toString() { result = "uint" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalUnsignedIntegerType(byteSize) + } +} + +/** + * A floating-point type. + */ +class IRFloatingPointType extends IRNumericType, TIRFloatingPointType { + final override string toString() { result = "float" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalFloatingPointType(byteSize) + } +} + +/** + * An address type, representing the memory address of data. Used to represent pointers, references, + * and lvalues, include those that are garbage collected. + * + * The address of a function is represented by the separate `IRFunctionAddressType`. + */ +class IRAddressType extends IRSizedType, TIRAddressType { + final override string toString() { result = "addr" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalAddressType(byteSize) + } +} + +/** + * An address type, representing the memory address of code. Used to represent function pointers, + * function references, and the target of a direct function call. + */ +class IRFunctionAddressType extends IRSizedType, TIRFunctionAddressType { + final override string toString() { result = "func" + byteSize.toString() } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalFunctionAddressType(byteSize) + } +} + +/** + * A type with known size that does not fit any of the other kinds of type. Used to represent + * classes, structs, unions, fixed-size arrays, pointers-to-member, and more. + */ +class IROpaqueType extends IRSizedType, TIROpaqueType { + Language::OpaqueTypeTag tag; + + IROpaqueType() { this = TIROpaqueType(tag, byteSize) } + + final override string toString() { + result = "opaque" + byteSize.toString() + "{" + tag.toString() + "}" + } + + final override string getIdentityString() { + result = "opaque" + byteSize.toString() + "{" + Language::getOpaqueTagIdentityString(tag) + "}" + } + + final override Language::LanguageType getCanonicalLanguageType() { + result = Language::getCanonicalOpaqueType(tag, byteSize) + } + + /** + * Gets the "tag" that differentiates this type from other incompatible opaque types that have the + * same size. + */ + final Language::OpaqueTypeTag getTag() { result = tag } +} + +module IRTypeSanity { + query predicate missingCanonicalLanguageType(IRType type, string message) { + not exists(type.getCanonicalLanguageType()) and + message = "Type does not have a canonical `LanguageType`" + } + + query predicate multipleCanonicalLanguageTypes(IRType type, string message) { + strictcount(type.getCanonicalLanguageType()) > 1 and + message = "Type has multiple canonical `LanguageType`s: " + + concat(type.getCanonicalLanguageType().toString(), ", ") + } + + query predicate missingIRType(Language::LanguageType type, string message) { + not exists(type.getIRType()) and + message = "`LanguageType` does not have a corresponding `IRType`." + } + + query predicate multipleIRTypes(Language::LanguageType type, string message) { + strictcount(type.getIRType()) > 1 and + message = "`LanguageType` " + type.getAQlClass() + " has multiple `IRType`s: " + + concat(type.getIRType().toString(), ", ") + } + + import Language::LanguageTypeSanity +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll index d4f1599d78e..a71608ee855 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/MemoryAccessKind.qll @@ -5,6 +5,7 @@ private newtype TMemoryAccessKind = TBufferMayMemoryAccess() or TEscapedMemoryAccess() or TEscapedMayMemoryAccess() or + TNonLocalMayMemoryAccess() or TPhiMemoryAccess() or TUnmodeledMemoryAccess() or TChiTotalMemoryAccess() or @@ -80,6 +81,14 @@ class EscapedMayMemoryAccess extends MemoryAccessKind, TEscapedMayMemoryAccess { override string toString() { result = "escaped(may)" } } +/** + * The operand or result may access all memory whose address has escaped, other than data on the + * stack frame of the current function. + */ +class NonLocalMayMemoryAccess extends MemoryAccessKind, TNonLocalMayMemoryAccess { + override string toString() { result = "nonlocal(may)" } +} + /** * The operand is a Phi operand, which accesses the same memory as its * definition. diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll index 0c1009b4fac..eb07be7f30c 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/Opcode.qll @@ -57,6 +57,7 @@ private newtype TOpcode = TUnmodeledDefinition() or TUnmodeledUse() or TAliasedDefinition() or + TAliasedUse() or TPhi() or TBuiltIn() or TVarArgsStart() or @@ -393,6 +394,10 @@ module Opcode { final override string toString() { result = "AliasedDefinition" } } + class AliasedUse extends Opcode, TAliasedUse { + final override string toString() { result = "AliasedUse" } + } + class Phi extends Opcode, TPhi { final override string toString() { result = "Phi" } } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRTypeInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRTypeInternal.qll new file mode 100644 index 00000000000..0ee01a30ac3 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/IRTypeInternal.qll @@ -0,0 +1 @@ +import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll index 908a208b83a..fea67ef5ecd 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/internal/TIRVariable.qll @@ -2,11 +2,11 @@ private import TIRVariableInternal private import Imports::TempVariableTag newtype TIRVariable = - TIRUserVariable(Language::Variable var, Language::Type type, Language::Function func) { + TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Function func) { Construction::hasUserVariable(func, var, type) } or TIRTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::Type type + Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type ) { Construction::hasTempVariable(func, ast, tag, type) } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll index 278040f8ab8..badd48552a5 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IR.qll @@ -5,6 +5,7 @@ import IRVariable import Operand private import internal.IRImports as Imports import Imports::EdgeKind +import Imports::IRType import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll index 3921472dc8e..de66a3c99dc 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRSanity.qll @@ -1,2 +1,3 @@ private import IR import InstructionSanity +import IRTypeSanity diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll index a7ca4593ac4..1a607f82c29 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/IRVariable.qll @@ -5,6 +5,7 @@ import Imports::TempVariableTag private import Imports::IRUtilities private import Imports::TTempVariableTag private import Imports::TIRVariable +private import Imports::IRType IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { result.getVariable() = var and @@ -24,7 +25,17 @@ abstract class IRVariable extends TIRVariable { /** * Gets the type of the variable. */ - abstract Language::Type getType(); + final Language::Type getType() { getLanguageType().hasType(result, false) } + + /** + * Gets the language-neutral type of the variable. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the variable. + */ + abstract Language::LanguageType getLanguageType(); /** * Gets the AST node that declared this variable, or that introduced this @@ -59,7 +70,7 @@ abstract class IRVariable extends TIRVariable { */ class IRUserVariable extends IRVariable, TIRUserVariable { Language::Variable var; - Language::Type type; + Language::LanguageType type; IRUserVariable() { this = TIRUserVariable(var, type, func) } @@ -71,7 +82,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable { result = getVariable().toString() + " " + getVariable().getLocation().toString() } - final override Language::Type getType() { result = type } + final override Language::LanguageType getLanguageType() { result = type } /** * Gets the original user-declared variable. @@ -110,11 +121,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable { Language::AST ast; TempVariableTag tag; - Language::Type type; + Language::LanguageType type; IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) } - final override Language::Type getType() { result = type } + final override Language::LanguageType getLanguageType() { result = type } final override Language::AST getAST() { result = ast } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll index 6d5e697150e..049983c9126 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Instruction.qll @@ -5,6 +5,7 @@ import IRVariable import Operand private import internal.InstructionImports as Imports import Imports::EdgeKind +import Imports::IRType import Imports::MemoryAccessKind import Imports::Opcode private import Imports::OperandTag @@ -49,7 +50,8 @@ module InstructionSanity { ( opcode instanceof ReadSideEffectOpcode or opcode instanceof Opcode::InlineAsm or - opcode instanceof Opcode::CallSideEffect + opcode instanceof Opcode::CallSideEffect or + opcode instanceof Opcode::AliasedUse ) and tag instanceof SideEffectOperandTag ) @@ -113,10 +115,12 @@ module InstructionSanity { } query predicate missingOperandType(Operand operand, string message) { - exists(Language::Function func | + exists(Language::Function func, Instruction use | not exists(operand.getType()) and - func = operand.getUse().getEnclosingFunction() and - message = "Operand missing type in function '" + Language::getIdentityString(func) + "'." + use = operand.getUse() and + func = use.getEnclosingFunction() and + message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + + "' missing type in function '" + Language::getIdentityString(func) + "'." ) } @@ -260,6 +264,7 @@ module InstructionSanity { ) { exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | not useOperand.getUse() instanceof UnmodeledUseInstruction and + not defInstr instanceof UnmodeledDefinitionInstruction and pointOfEvaluation(useOperand, useBlock, useIndex) and defInstr = useOperand.getAnyDef() and ( @@ -321,7 +326,7 @@ class Instruction extends Construction::TInstruction { } private string getResultPrefix() { - if getResultType() instanceof Language::VoidType + if getResultIRType() instanceof IRVoidType then result = "v" else if hasMemoryResult() @@ -353,23 +358,6 @@ class Instruction extends Construction::TInstruction { ) } - bindingset[type] - private string getValueCategoryString(string type) { - if isGLValue() then result = "glval<" + type + ">" else result = type - } - - string getResultTypeString() { - exists(string valcat | - valcat = getValueCategoryString(getResultType().toString()) and - if - getResultType() instanceof Language::UnknownType and - not isGLValue() and - exists(getResultSize()) - then result = valcat + "[" + getResultSize().toString() + "]" - else result = valcat - ) - } - /** * Gets a human-readable string that uniquely identifies this instruction * within the function. This string is used to refer to this instruction when @@ -389,7 +377,9 @@ class Instruction extends Construction::TInstruction { * * Example: `r1_1(int*)` */ - final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" } + final string getResultString() { + result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + } /** * Gets a string describing the operands of this instruction, suitable for @@ -457,6 +447,16 @@ class Instruction extends Construction::TInstruction { result = Construction::getInstructionUnconvertedResultExpression(this) } + final Language::LanguageType getResultLanguageType() { + result = Construction::getInstructionResultType(this) + } + + /** + * Gets the type of the result produced by this instruction. If the instruction does not produce + * a result, its result type will be `IRVoidType`. + */ + final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + /** * Gets the type of the result produced by this instruction. If the * instruction does not produce a result, its result type will be `VoidType`. @@ -464,7 +464,16 @@ class Instruction extends Construction::TInstruction { * If `isGLValue()` holds, then the result type of this instruction should be * thought of as "pointer to `getResultType()`". */ - final Language::Type getResultType() { Construction::instructionHasType(this, result, _) } + final Language::Type getResultType() { + exists(Language::LanguageType resultType | + resultType = getResultLanguageType() and + ( + resultType.hasUnspecifiedType(result, _) + or + not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType + ) + ) + } /** * Holds if the result produced by this instruction is a glvalue. If this @@ -484,7 +493,7 @@ class Instruction extends Construction::TInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::instructionHasType(this, _, true) } + final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -493,16 +502,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { - if isGLValue() - then - // a glvalue is always pointer-sized. - result = Language::getPointerSize() - else - if getResultType() instanceof Language::UnknownType - then result = Construction::getInstructionResultSize(this) - else result = Language::getTypeSize(getResultType()) - } + final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -1384,7 +1384,7 @@ class CatchInstruction extends Instruction { * An instruction that catches an exception of a specific type. */ class CatchByTypeInstruction extends CatchInstruction { - Language::Type exceptionType; + Language::LanguageType exceptionType; CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and @@ -1396,7 +1396,7 @@ class CatchByTypeInstruction extends CatchInstruction { /** * Gets the type of exception to be caught. */ - final Language::Type getExceptionType() { result = exceptionType } + final Language::LanguageType getExceptionType() { result = exceptionType } } /** @@ -1423,6 +1423,13 @@ class AliasedDefinitionInstruction extends Instruction { final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess } } +/** + * An instruction that consumes all escaped memory on exit from the function. + */ +class AliasedUseInstruction extends Instruction { + AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } +} + class UnmodeledUseInstruction extends Instruction { UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll index a23fb081afe..07dd107bf12 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/Operand.qll @@ -1,9 +1,10 @@ private import internal.IRInternal -import Instruction -import IRBlock +private import Instruction +private import IRBlock private import internal.OperandImports as Imports -import Imports::MemoryAccessKind -import Imports::Overlap +private import Imports::MemoryAccessKind +private import Imports::IRType +private import Imports::Overlap private import Imports::OperandTag cached @@ -143,22 +144,40 @@ class Operand extends TOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - Language::Type getType() { result = getAnyDef().getResultType() } + Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + + /** + * Gets the language-neutral type of the value consumed by this operand. This is usually the same + * as the result type of the definition instruction consumed by this operand. For register + * operands, this is always the case. For some memory operands, the operand type may be different + * from the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the value consumed by this operand. This is usually the same as the + * result type of the definition instruction consumed by this operand. For register operands, + * this is always the case. For some memory operands, the operand type may be different from + * the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final Language::Type getType() { getLanguageType().hasType(result, _) } /** * Holds if the value consumed by this operand is a glvalue. If this * holds, the value of the operand represents the address of a location, * and the type of the location is given by `getType()`. If this does * not hold, the value of the operand represents a value whose type is - * given by `getResultType()`. + * given by `getType()`. */ - predicate isGLValue() { getAnyDef().isGLValue() } + final predicate isGLValue() { getLanguageType().hasType(_, true) } /** * Gets the size of the value consumed by this operand, in bytes. If the operand does not have * a known constant size, this predicate does not hold. */ - int getSize() { result = Language::getTypeSize(getType()) } + final int getSize() { result = getLanguageType().getByteSize() } } /** @@ -170,11 +189,6 @@ class MemoryOperand extends Operand { this = TPhiOperand(_, _, _, _) } - override predicate isGLValue() { - // A `MemoryOperand` can never be a glvalue - none() - } - /** * Gets the kind of memory access performed by the operand. */ @@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; - final override Language::Type getType() { + final override Language::LanguageType getLanguageType() { result = Construction::getInstructionOperandType(useInstr, tag) } } @@ -381,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand { class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; - final override int getSize() { - if getType() instanceof Language::UnknownType - then result = Construction::getInstructionOperandSize(useInstr, tag) - else result = Language::getTypeSize(getType()) - } - override MemoryAccessKind getMemoryAccess() { + useInstr instanceof AliasedUseInstruction and + result instanceof NonLocalMayMemoryAccess + or useInstr instanceof CallSideEffectInstruction and result instanceof EscapedMayMemoryAccess or diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll index c914f731e4d..79e5a2b6713 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/ValueNumbering.qll @@ -1,5 +1,5 @@ private import internal.ValueNumberingInternal -import csharp +private import internal.ValueNumberingImports private import IR /** @@ -23,31 +23,32 @@ newtype TValueNumber = initializeParameterValueNumber(_, irFunc, var) } or TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or - TConstantValueNumber(IRFunction irFunc, Type type, string value) { + TConstantValueNumber(IRFunction irFunc, IRType type, string value) { constantValueNumber(_, irFunc, type, value) } or - TStringConstantValueNumber(IRFunction irFunc, Type type, string value) { + TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) { stringConstantValueNumber(_, irFunc, type, value) } or - TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) { + TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) { fieldAddressValueNumber(_, irFunc, field, objectAddress) } or TBinaryValueNumber( - IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand + IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand ) { binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand) } or TPointerArithmeticValueNumber( - IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand ) { pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand) } or - TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) { + TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) { unaryValueNumber(_, irFunc, opcode, type, operand) } or TInheritanceConversionValueNumber( - IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand + IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass, + ValueNumber operand ) { inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand) } or @@ -59,7 +60,7 @@ newtype TValueNumber = class ValueNumber extends TValueNumber { final string toString() { result = getExampleInstruction().getResultId() } - final Location getLocation() { result = getExampleInstruction().getLocation() } + final Language::Location getLocation() { result = getExampleInstruction().getLocation() } /** * Gets the instructions that have been assigned this value number. This will always produce at @@ -79,7 +80,10 @@ class ValueNumber extends TValueNumber { ) } - final Operand getAUse() { this = valueNumber(result.getDefinitionInstruction()) } + /** + * Gets an `Operand` whose definition is exact and has this value number. + */ + final Operand getAUse() { this = valueNumber(result.getDef()) } } /** @@ -107,15 +111,24 @@ private class CongruentCopyInstruction extends CopyInstruction { * a `unique` value number that is never shared by multiple instructions. */ private predicate numberableInstruction(Instruction instr) { - instr instanceof VariableAddressInstruction or - instr instanceof InitializeParameterInstruction or - instr instanceof InitializeThisInstruction or - instr instanceof ConstantInstruction or - instr instanceof StringConstantInstruction or - instr instanceof FieldAddressInstruction or - instr instanceof BinaryInstruction or - instr instanceof UnaryInstruction or - instr instanceof PointerArithmeticInstruction or + instr instanceof VariableAddressInstruction + or + instr instanceof InitializeParameterInstruction + or + instr instanceof InitializeThisInstruction + or + instr instanceof ConstantInstruction + or + instr instanceof StringConstantInstruction + or + instr instanceof FieldAddressInstruction + or + instr instanceof BinaryInstruction + or + instr instanceof UnaryInstruction and not instr instanceof CopyInstruction + or + instr instanceof PointerArithmeticInstruction + or instr instanceof CongruentCopyInstruction } @@ -123,14 +136,14 @@ private predicate variableAddressValueNumber( VariableAddressInstruction instr, IRFunction irFunc, IRVariable var ) { instr.getEnclosingIRFunction() = irFunc and - instr.getVariable() = var + instr.getIRVariable() = var } private predicate initializeParameterValueNumber( InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var ) { instr.getEnclosingIRFunction() = irFunc and - instr.getVariable() = var + instr.getIRVariable() = var } private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) { @@ -138,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF } private predicate constantValueNumber( - ConstantInstruction instr, IRFunction irFunc, Type type, string value + ConstantInstruction instr, IRFunction irFunc, IRType type, string value ) { instr.getEnclosingIRFunction() = irFunc and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getValue() = value } private predicate stringConstantValueNumber( - StringConstantInstruction instr, IRFunction irFunc, Type type, string value + StringConstantInstruction instr, IRFunction irFunc, IRType type, string value ) { instr.getEnclosingIRFunction() = irFunc and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getValue().getValue() = value } private predicate fieldAddressValueNumber( - FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress + FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress ) { instr.getEnclosingIRFunction() = irFunc and instr.getField() = field and @@ -162,42 +175,43 @@ private predicate fieldAddressValueNumber( } private predicate binaryValueNumber( - BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, + BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand ) { instr.getEnclosingIRFunction() = irFunc and not instr instanceof PointerArithmeticInstruction and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and valueNumber(instr.getLeft()) = leftOperand and valueNumber(instr.getRight()) = rightOperand } private predicate pointerArithmeticValueNumber( - PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize, - ValueNumber leftOperand, ValueNumber rightOperand + PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, + int elementSize, ValueNumber leftOperand, ValueNumber rightOperand ) { instr.getEnclosingIRFunction() = irFunc and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getElementSize() = elementSize and valueNumber(instr.getLeft()) = leftOperand and valueNumber(instr.getRight()) = rightOperand } private predicate unaryValueNumber( - UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand + UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand ) { instr.getEnclosingIRFunction() = irFunc and not instr instanceof InheritanceConversionInstruction and + not instr instanceof CopyInstruction and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and valueNumber(instr.getUnary()) = operand } private predicate inheritanceConversionValueNumber( - InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass, - Class derivedClass, ValueNumber operand + InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, + Language::Class baseClass, Language::Class derivedClass, ValueNumber operand ) { instr.getEnclosingIRFunction() = irFunc and instr.getOpcode() = opcode and @@ -212,7 +226,7 @@ private predicate inheritanceConversionValueNumber( */ private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) { instr.getEnclosingIRFunction() = irFunc and - not instr.getResultType() instanceof VoidType and + not instr.getResultIRType() instanceof IRVoidType and not numberableInstruction(instr) } @@ -230,9 +244,10 @@ ValueNumber valueNumber(Instruction instr) { } /** - * Gets the value number assigned to `instr`, if any. Returns at most one result. + * Gets the value number assigned to the exact definition of `op`, if any. + * Returns at most one result. */ -ValueNumber valueNumberOfOperand(Operand op) { result = valueNumber(op.getDefinitionInstruction()) } +ValueNumber valueNumberOfOperand(Operand op) { result = valueNumber(op.getDef()) } /** * Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique @@ -255,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) { initializeThisValueNumber(instr, irFunc) and result = TInitializeThisValueNumber(irFunc) or - exists(Type type, string value | + exists(IRType type, string value | constantValueNumber(instr, irFunc, type, value) and result = TConstantValueNumber(irFunc, type, value) ) or - exists(Type type, string value | + exists(IRType type, string value | stringConstantValueNumber(instr, irFunc, type, value) and result = TStringConstantValueNumber(irFunc, type, value) ) or - exists(Field field, ValueNumber objectAddress | + exists(Language::Field field, ValueNumber objectAddress | fieldAddressValueNumber(instr, irFunc, field, objectAddress) and result = TFieldAddressValueNumber(irFunc, field, objectAddress) ) or - exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand | + exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand | binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand) ) or - exists(Opcode opcode, Type type, ValueNumber operand | + exists(Opcode opcode, IRType type, ValueNumber operand | unaryValueNumber(instr, irFunc, opcode, type, operand) and result = TUnaryValueNumber(irFunc, opcode, type, operand) ) or - exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand | + exists( + Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand + | inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand) ) or exists( - Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand + Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand | pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand, rightOperand) and diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll new file mode 100644 index 00000000000..555cb581d37 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll @@ -0,0 +1 @@ +import semmle.code.csharp.ir.internal.Overlap diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll index b49dacc701b..e25693a0207 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll @@ -1 +1,2 @@ import semmle.code.csharp.ir.implementation.raw.IR as IR +import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll index a7a5943972e..0d2627e183f 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRConstruction.qll @@ -1,6 +1,8 @@ import csharp import semmle.code.csharp.ir.implementation.raw.IR private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import semmle.code.csharp.ir.internal.CSharpType +private import semmle.code.csharp.ir.internal.Overlap private import semmle.code.csharp.ir.internal.TempVariableTag private import InstructionTag private import TranslatedCondition @@ -31,16 +33,18 @@ private module Cached { cached newtype TInstruction = MkInstruction(TranslatedElement element, InstructionTag tag) { - element.hasInstruction(_, tag, _, _) + element.hasInstruction(_, tag, _) } cached - predicate hasUserVariable(Callable callable, Variable var, Type type) { + predicate hasUserVariable(Callable callable, Variable var, CSharpType type) { getTranslatedFunction(callable).hasUserVariable(var, type) } cached - predicate hasTempVariable(Callable callable, Language::AST ast, TempVariableTag tag, Type type) { + predicate hasTempVariable( + Callable callable, Language::AST ast, TempVariableTag tag, CSharpType type + ) { exists(TranslatedElement element | element.getAST() = ast and callable = element.getFunction() and @@ -83,22 +87,16 @@ private module Cached { } cached - Type getInstructionOperandType(Instruction instruction, TypedOperandTag tag) { + CSharpType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) { // For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as // the result type of the load. if instruction instanceof LoadInstruction - then result = instruction.(LoadInstruction).getResultType() + then result = instruction.(LoadInstruction).getResultLanguageType() else result = getInstructionTranslatedElement(instruction) .getInstructionOperandType(getInstructionTag(instruction), tag) } - cached - int getInstructionOperandSize(Instruction instruction, SideEffectOperandTag tag) { - result = getInstructionTranslatedElement(instruction) - .getInstructionOperandSize(getInstructionTag(instruction), tag) - } - cached Instruction getPhiOperandDefinition( PhiInstruction instruction, IRBlock predecessorBlock, Overlap overlap @@ -216,15 +214,15 @@ private module Cached { } cached - predicate instructionHasType(Instruction instruction, Type type, boolean isLValue) { + CSharpType getInstructionResultType(Instruction instruction) { getInstructionTranslatedElement(instruction) - .hasInstruction(_, getInstructionTag(instruction), type, isLValue) + .hasInstruction(_, getInstructionTag(instruction), result) } cached Opcode getInstructionOpcode(Instruction instruction) { getInstructionTranslatedElement(instruction) - .hasInstruction(result, getInstructionTag(instruction), _, _) + .hasInstruction(result, getInstructionTag(instruction), _) } cached @@ -274,7 +272,7 @@ private module Cached { } cached - Type getInstructionExceptionType(Instruction instruction) { + CSharpType getInstructionExceptionType(Instruction instruction) { result = getInstructionTranslatedElement(instruction) .getInstructionExceptionType(getInstructionTag(instruction)) } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll index b5abc1049f3..3b716c201ac 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRImports.qll @@ -1,2 +1,3 @@ import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind +import semmle.code.csharp.ir.implementation.IRType as IRType import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll index 20c299416ef..9ab8de41259 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/IRVariableImports.qll @@ -1,3 +1,4 @@ +import semmle.code.csharp.ir.implementation.IRType as IRType import semmle.code.csharp.ir.implementation.TempVariableTag as TempVariableTag import semmle.code.csharp.ir.internal.IRUtilities as IRUtilities import semmle.code.csharp.ir.internal.TempVariableTag as TTempVariableTag diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll index da9b8e6e877..8628f5be373 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionImports.qll @@ -1,4 +1,5 @@ import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind +import semmle.code.csharp.ir.implementation.IRType as IRType import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind import semmle.code.csharp.ir.implementation.Opcode as Opcode import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionTag.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionTag.qll index 4130169b0cf..818d37cf803 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionTag.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/InstructionTag.qll @@ -32,6 +32,7 @@ newtype TInstructionTag = UnmodeledDefinitionTag() or UnmodeledUseTag() or AliasedDefinitionTag() or + AliasedUseTag() or SwitchBranchTag() or CallTargetTag() or CallTag() or @@ -131,6 +132,8 @@ string getInstructionTagId(TInstructionTag tag) { or tag = AliasedDefinitionTag() and result = "AliasedDef" or + tag = AliasedUseTag() and result = "AliasedUse" + or tag = SwitchBranchTag() and result = "SwitchBranch" or tag = CallTargetTag() and result = "CallTarget" diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll index 13667df7276..997185fb9f7 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/OperandImports.qll @@ -1,3 +1,4 @@ import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind +import semmle.code.csharp.ir.implementation.IRType as IRType import semmle.code.csharp.ir.internal.Overlap as Overlap import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCondition.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCondition.qll index 93e8e67200a..cc398a86011 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCondition.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedCondition.qll @@ -1,6 +1,7 @@ import csharp private import semmle.code.csharp.ir.implementation.Opcode private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import semmle.code.csharp.ir.internal.CSharpType private import InstructionTag private import TranslatedElement private import TranslatedExpr @@ -33,9 +34,7 @@ abstract class TranslatedFlexibleCondition extends TranslatedCondition, Conditio result = this.getOperand().getFirstInstruction() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -99,9 +98,7 @@ abstract class TranslatedBinaryLogicalOperation extends TranslatedNativeConditio result = this.getLeftOperand().getFirstInstruction() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedElement.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedElement.qll index f98527e084f..4403ab07f08 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedElement.qll @@ -3,6 +3,7 @@ import semmle.code.csharp.ir.implementation.raw.IR private import semmle.code.csharp.ir.IRConfiguration private import semmle.code.csharp.ir.implementation.Opcode private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.TempVariableTag private import InstructionTag private import TranslatedCondition @@ -15,11 +16,6 @@ private import desugar.Foreach private import desugar.Delegate private import desugar.Lock -/** - * Gets the built-in `int` type. - */ -IntType getIntType() { any() } - ArrayType getArrayOfDim(int dim, Type type) { result.getRank() = dim and result.getElementType() = type @@ -420,9 +416,7 @@ abstract class TranslatedElement extends TTranslatedElement { * If the instruction does not return a result, `resultType` should be * `VoidType`. */ - abstract predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ); + abstract predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType); /** * Gets the `Function` that contains this element. @@ -462,7 +456,7 @@ abstract class TranslatedElement extends TTranslatedElement { * `tag` must be unique for each variable generated from the same AST node * (not just from the same `TranslatedElement`). */ - predicate hasTempVariable(TempVariableTag tag, Type type) { none() } + predicate hasTempVariable(TempVariableTag tag, CSharpType type) { none() } /** * If the instruction specified by `tag` is a `FunctionInstruction`, gets the @@ -517,7 +511,7 @@ abstract class TranslatedElement extends TTranslatedElement { * If the instruction specified by `tag` is a `CatchByTypeInstruction`, * gets the type of the exception to be caught. */ - Type getInstructionExceptionType(InstructionTag tag) { none() } + CSharpType getInstructionExceptionType(InstructionTag tag) { none() } /** * If the instruction specified by `tag` is an `InheritanceConversionInstruction`, @@ -536,13 +530,7 @@ abstract class TranslatedElement extends TTranslatedElement { /** * Gets the type of the memory operand specified by `operandTag` on the the instruction specified by `tag`. */ - Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() } - - /** - * Gets the size of the memory operand specified by `operandTag` on the the instruction specified by `tag`. - * Only holds for operands whose type is `UnknownType`. - */ - int getInstructionOperandSize(InstructionTag tag, SideEffectOperandTag operandTag) { none() } + CSharpType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { none() } /** * Gets the instruction generated by this element with tag `tag`. diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedExpr.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedExpr.qll index 3476484e413..2ccac5af4e0 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedExpr.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedExpr.qll @@ -2,6 +2,7 @@ import csharp private import semmle.code.csharp.ir.implementation.Opcode private import semmle.code.csharp.ir.implementation.internal.OperandTag private import semmle.code.csharp.ir.internal.TempVariableTag +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.IRUtilities private import InstructionTag private import TranslatedCondition @@ -96,6 +97,12 @@ abstract class TranslatedCoreExpr extends TranslatedExpr { not needsLoad(expr) } + final CSharpType getResultCSharpType() { + if isResultLValue() = true + then result = getTypeForGLValue(expr.getType()) + else result = getTypeForPRValue(expr.getType()) + } + /** * Returns `true` if the result of this `TranslatedExpr` is a lvalue, or * `false` if the result is a rvalue. @@ -117,38 +124,32 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, override Instruction getFirstInstruction() { result = this.getCondition().getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { ( tag = ConditionValueTrueTempAddressTag() or tag = ConditionValueFalseTempAddressTag() or tag = ConditionValueResultTempAddressTag() ) and opcode instanceof Opcode::VariableAddress and - resultType = this.getResultType() and - isLValue = true + resultType = getTypeForGLValue(expr.getType()) or ( tag = ConditionValueTrueConstantTag() or tag = ConditionValueFalseConstantTag() ) and opcode instanceof Opcode::Constant and - resultType = this.getResultType() and - isLValue = this.isResultLValue() + resultType = getResultCSharpType() or ( tag = ConditionValueTrueStoreTag() or tag = ConditionValueFalseStoreTag() ) and opcode instanceof Opcode::Store and - resultType = this.getResultType() and - isLValue = this.isResultLValue() + resultType = getResultCSharpType() or tag = ConditionValueResultLoadTag() and opcode instanceof Opcode::Load and - resultType = this.getResultType() and - isLValue = this.isResultLValue() + resultType = getResultCSharpType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -209,9 +210,9 @@ class TranslatedConditionValue extends TranslatedCoreExpr, ConditionContext, ) } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ConditionValueTempVar() and - type = this.getResultType() + type = this.getResultCSharpType() } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -259,13 +260,12 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad { override TranslatedElement getChild(int id) { id = 0 and result = this.getOperand() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = LoadTag() and opcode instanceof Opcode::Load and - resultType = expr.getType() and - if not this.producesExprResult() then isLValue = true else isLValue = false + if producesExprResult() + then resultType = getTypeForPRValue(expr.getType()) + else resultType = getTypeForGLValue(expr.getType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -326,32 +326,27 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr { or resultType instanceof FloatingPointType and result = this.getResultType() or - resultType instanceof PointerType and result = getIntType() + resultType instanceof PointerType and result = any(IntType t) ) ) } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { - isLValue = false and - ( - tag = CrementLoadTag() and - opcode instanceof Opcode::Load and - resultType = this.getResultType() - or - tag = CrementConstantTag() and - opcode instanceof Opcode::Constant and - resultType = this.getConstantType() - or - tag = CrementOpTag() and - opcode = this.getOpcode() and - resultType = this.getResultType() - or - tag = CrementStoreTag() and - opcode instanceof Opcode::Store and - resultType = this.getResultType() - ) + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { + tag = CrementLoadTag() and + opcode instanceof Opcode::Load and + resultType = getTypeForPRValue(expr.getType()) + or + tag = CrementConstantTag() and + opcode instanceof Opcode::Constant and + resultType = getTypeForPRValue(this.getConstantType()) + or + tag = CrementOpTag() and + opcode = this.getOpcode() and + resultType = getTypeForPRValue(expr.getType()) + or + tag = CrementStoreTag() and + opcode instanceof Opcode::Store and + resultType = getTypeForPRValue(expr.getType()) } final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -466,9 +461,7 @@ class TranslatedObjectInitializerExpr extends TranslatedNonConstantExpr, Initial override Instruction getFirstInstruction() { result = this.getChild(0).getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -505,9 +498,7 @@ class TranslatedCollectionInitializer extends TranslatedNonConstantExpr, Initial override Instruction getFirstInstruction() { result = this.getChild(0).getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -608,23 +599,19 @@ class TranslatedArrayAccess extends TranslatedNonConstantExpr { override Instruction getResult() { result = this.getInstruction(PointerAddTag(getRank() - 1)) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { exists(int index | inBounds(index) and tag = PointerAddTag(index) and opcode instanceof Opcode::PointerAdd and - resultType = this.getInstruction(ElementsAddressTag(index)).getResultType() and - isLValue = false + resultType = getTypeForPRValue(getArrayOfDim(getRank() - index, expr.getType())) ) or exists(int index | inBounds(index) and tag = ElementsAddressTag(index) and opcode instanceof Opcode::ElementsAddress and - resultType = getArrayOfDim(getRank() - index, expr.getType()) and - isLValue = false + resultType = getTypeForPRValue(getArrayOfDim(getRank() - index, expr.getType())) ) } @@ -693,9 +680,7 @@ abstract class TranslatedPointerOps extends TranslatedNonConstantExpr { child = this.getOperand() and result = this.getParent().getChildSuccessor(this) } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -725,13 +710,10 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr { final override TranslatedElement getChild(int id) { none() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::CopyValue and - resultType = expr.getType() and - isLValue = false + resultType = getTypeForPRValue(expr.getType()) } final override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } @@ -777,14 +759,11 @@ abstract class TranslatedVariableAccess extends TranslatedNonConstantExpr { else result = this.getInstruction(AddressTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { this.needsExtraLoad() and tag = LoadTag() and opcode instanceof Opcode::Load and - resultType = this.getResultType() and - isLValue = false + resultType = getTypeForPRValue(expr.getType()) } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -858,15 +837,12 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess { result = TranslatedVariableAccess.super.getInstructionOperand(tag, operandTag) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { - TranslatedVariableAccess.super.hasInstruction(opcode, tag, resultType, isLValue) + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { + TranslatedVariableAccess.super.hasInstruction(opcode, tag, resultType) or tag = AddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = this.getResultType() and - isLValue = true + resultType = getTypeForGLValue(this.getResultType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -909,13 +885,10 @@ class TranslatedFieldAccess extends TranslatedVariableAccess { result = this.getParent().getParent().(InitializationContext).getTargetAddress() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = AddressTag() and opcode instanceof Opcode::FieldAddress and - resultType = this.getResultType() and - isLValue = true + resultType = getTypeForGLValue(expr.getType()) } override Field getInstructionField(InstructionTag tag) { @@ -939,13 +912,10 @@ class TranslatedFunctionAccess extends TranslatedNonConstantExpr { kind instanceof GotoEdge } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::FunctionAddress and - resultType = expr.getType() and - isLValue = true + resultType = getTypeForGLValue(expr.getType()) } override Callable getInstructionFunction(InstructionTag tag) { @@ -989,13 +959,10 @@ abstract class TranslatedConstantExpr extends TranslatedCoreExpr, TTranslatedVal none() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = OnlyInstructionTag() and opcode = this.getOpcode() and - resultType = this.getResultType() and - isLValue = false + resultType = getTypeForPRValue(expr.getType()) } final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -1041,13 +1008,10 @@ abstract class TranslatedSingleInstructionExpr extends TranslatedNonConstantExpr */ abstract Opcode getOpcode(); - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { opcode = getOpcode() and tag = OnlyInstructionTag() and - resultType = this.getResultType() and - isLValue = this.isResultLValue() + resultType = getResultCSharpType() } final override Instruction getResult() { result = this.getInstruction(OnlyInstructionTag()) } @@ -1119,14 +1083,11 @@ class TranslatedCast extends TranslatedNonConstantExpr { child = this.getOperand() and result = this.getInstruction(ConvertTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { ( tag = ConvertTag() and opcode = this.getOpcode() and - resultType = this.getResultType() and - isLValue = false + resultType = getTypeForPRValue(expr.getType()) ) } @@ -1296,9 +1257,7 @@ abstract class TranslatedAssignment extends TranslatedNonConstantExpr { result = this.getRightOperand().getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { ( this.needsConversion() and tag = AssignmentConvertRightTag() and @@ -1306,8 +1265,7 @@ abstract class TranslatedAssignment extends TranslatedNonConstantExpr { // crudely represent conversions. Could // be useful to represent the whole chain of conversions opcode instanceof Opcode::Convert and - resultType = expr.getLValue().getType() and - isLValue = false + resultType = getTypeForPRValue(expr.getLValue().getType()) ) } @@ -1354,15 +1312,12 @@ class TranslatedAssignExpr extends TranslatedAssignment { result = this.getInstruction(AssignmentStoreTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { - TranslatedAssignment.super.hasInstruction(opcode, tag, resultType, isLValue) + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { + TranslatedAssignment.super.hasInstruction(opcode, tag, resultType) or tag = AssignmentStoreTag() and opcode instanceof Opcode::Store and - resultType = this.getResultType() and - isLValue = false + resultType = getTypeForPRValue(expr.getType()) } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -1484,32 +1439,27 @@ class TranslatedAssignOperation extends TranslatedAssignment { expr instanceof AssignRShiftExpr and result instanceof Opcode::ShiftRight } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { - isLValue = false and + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { + tag = AssignOperationLoadTag() and + opcode instanceof Opcode::Load and + resultType = getTypeForPRValue(this.getLeftOperand().getResultType()) + or + tag = AssignOperationOpTag() and + opcode = getOpcode() and + resultType = getTypeForPRValue(this.getConvertedLeftOperandType()) + or + tag = AssignmentStoreTag() and + opcode instanceof Opcode::Store and + resultType = getTypeForPRValue(expr.getType()) + or + this.leftOperandNeedsConversion() and + opcode instanceof Opcode::Convert and ( - tag = AssignOperationLoadTag() and - opcode instanceof Opcode::Load and - resultType = this.getLeftOperand().getResultType() + tag = AssignOperationConvertLeftTag() and + resultType = getTypeForPRValue(this.getConvertedLeftOperandType()) or - tag = AssignOperationOpTag() and - opcode = getOpcode() and - resultType = this.getConvertedLeftOperandType() - or - tag = AssignmentStoreTag() and - opcode instanceof Opcode::Store and - resultType = this.getResultType() - or - this.leftOperandNeedsConversion() and - opcode instanceof Opcode::Convert and - ( - tag = AssignOperationConvertLeftTag() and - resultType = this.getConvertedLeftOperandType() - or - tag = AssignOperationConvertResultTag() and - resultType = this.getLeftOperand().getResultType() - ) + tag = AssignOperationConvertResultTag() and + resultType = getTypeForPRValue(this.getLeftOperand().getResultType()) ) } @@ -1595,9 +1545,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont override Instruction getFirstInstruction() { result = this.getCondition().getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { not this.resultIsVoid() and ( ( @@ -1608,8 +1556,7 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont tag = ConditionValueResultTempAddressTag() ) and opcode instanceof Opcode::VariableAddress and - resultType = this.getResultType() and - isLValue = true + resultType = getTypeForGLValue(this.getResultType()) or ( not this.thenIsVoid() and tag = ConditionValueTrueStoreTag() @@ -1617,13 +1564,11 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont not this.elseIsVoid() and tag = ConditionValueFalseStoreTag() ) and opcode instanceof Opcode::Store and - resultType = this.getResultType() and - isLValue = false + resultType = getTypeForPRValue(this.getResultType()) or tag = ConditionValueResultLoadTag() and opcode instanceof Opcode::Load and - resultType = this.getResultType() and - isLValue = this.isResultLValue() + resultType = this.getResultCSharpType() ) } @@ -1691,10 +1636,10 @@ class TranslatedConditionalExpr extends TranslatedNonConstantExpr, ConditionCont ) } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { not this.resultIsVoid() and tag = ConditionValueTempVar() and - type = this.getResultType() + type = getTypeForPRValue(this.getResultType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -1828,35 +1773,28 @@ class TranslatedIsExpr extends TranslatedNonConstantExpr { result = this.getInstruction(GeneratedNEQTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { this.hasVar() and tag = InitializerStoreTag() and opcode instanceof Opcode::Store and - resultType = expr.getPattern().getType() and - isLValue = false + resultType = getTypeForPRValue(expr.getPattern().getType()) or tag = ConvertTag() and opcode instanceof Opcode::CheckedConvertOrNull and - resultType = expr.getPattern().getType() and - isLValue = false + resultType = getTypeForPRValue(expr.getPattern().getType()) or tag = GeneratedNEQTag() and opcode instanceof Opcode::CompareNE and - resultType = expr.getType() and - isLValue = false + resultType = getTypeForPRValue(expr.getType()) or tag = GeneratedConstantTag() and opcode instanceof Opcode::Constant and - resultType = expr.getPattern().getType() and - isLValue = false + resultType = getTypeForPRValue(expr.getPattern().getType()) or this.hasVar() and tag = GeneratedBranchTag() and opcode instanceof Opcode::ConditionalBranch and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() } override string getInstructionConstantValue(InstructionTag tag) { @@ -1906,6 +1844,99 @@ class TranslatedIsExpr extends TranslatedNonConstantExpr { } } +/** + * 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 LambdaExpr expr; + + final override Instruction getFirstInstruction() { + result = this.getInstruction(InitializerVariableAddressTag()) + } + + final override TranslatedElement getChild(int id) { id = 0 and result = this.getInitialization() } + + override Instruction getResult() { result = this.getInstruction(LoadTag()) } + + override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { + tag = InitializerVariableAddressTag() and + kind instanceof GotoEdge and + result = this.getInstruction(InitializerStoreTag()) + or + tag = InitializerStoreTag() and + kind instanceof GotoEdge and + ( + result = this.getInitialization().getFirstInstruction() + or + not this.hasInitializer() and result = this.getInstruction(LoadTag()) + ) + or + tag = LoadTag() and + kind instanceof GotoEdge and + result = this.getParent().getChildSuccessor(this) + } + + override Instruction getChildSuccessor(TranslatedElement child) { + child = getInitialization() and + result = this.getInstruction(LoadTag()) + } + + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { + tag = InitializerVariableAddressTag() and + opcode instanceof Opcode::VariableAddress and + resultType = getTypeForGLValue(expr.getType()) + or + tag = InitializerStoreTag() and + opcode instanceof Opcode::Uninitialized and + resultType = getTypeForPRValue(expr.getType()) + or + tag = LoadTag() and + opcode instanceof Opcode::Load and + resultType = getTypeForPRValue(expr.getType()) + } + + override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { + tag = InitializerStoreTag() and + operandTag instanceof AddressOperandTag and + result = this.getInstruction(InitializerVariableAddressTag()) + or + tag = LoadTag() and + ( + operandTag instanceof AddressOperandTag and + result = this.getInstruction(InitializerVariableAddressTag()) + or + operandTag instanceof LoadOperandTag and + result = this.getEnclosingFunction().getUnmodeledDefinitionInstruction() + ) + } + + override IRVariable getInstructionVariable(InstructionTag tag) { + ( + tag = InitializerVariableAddressTag() or + tag = InitializerStoreTag() + ) and + result = this.getTempVariable(LambdaTempVar()) + } + + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { + tag = LambdaTempVar() and + type = getTypeForPRValue(this.getResultType()) + } + + final override Instruction getTargetAddress() { + result = this.getInstruction(InitializerVariableAddressTag()) + } + + final override Type getTargetType() { result = this.getResultType() } + + private predicate hasInitializer() { exists(this.getInitialization()) } + + private TranslatedInitialization getInitialization() { + result = getTranslatedInitialization(expr.getChild(0)) + } +} + /** * The translation of a `DelegateCall`. Since this type of call needs * desugaring, we treat it as a special case. The AST node of the @@ -1929,9 +1960,7 @@ class TranslatedDelegateCall extends TranslatedNonConstantExpr { result = getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -1958,21 +1987,17 @@ abstract class TranslatedCreation extends TranslatedCoreExpr, TTranslatedCreatio id = 1 and result = this.getInitializerExpr() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { // Instruction that allocated space for a new object, // and returns its address tag = NewObjTag() and opcode instanceof Opcode::NewObj and - resultType = expr.getType() and - isLValue = false + resultType = getTypeForPRValue(expr.getType()) or this.needsLoad() and tag = LoadTag() and opcode instanceof Opcode::Load and - resultType = expr.getType() and - isLValue = false + resultType = getTypeForPRValue(expr.getType()) } final override Instruction getFirstInstruction() { result = this.getInstruction(NewObjTag()) } @@ -2083,9 +2108,7 @@ class TranslatedEventAccess extends TranslatedNonConstantExpr { // accessor call. override TranslatedElement getChild(int id) { id = 0 and result = this.getLValue() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedFunction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedFunction.qll index ef0e5fb6a67..9d63f7060e9 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedFunction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedFunction.qll @@ -1,6 +1,7 @@ import csharp import semmle.code.csharp.ir.implementation.raw.IR private import semmle.code.csharp.ir.implementation.Opcode +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.IRUtilities private import semmle.code.csharp.ir.implementation.internal.OperandTag private import semmle.code.csharp.ir.internal.TempVariableTag @@ -98,6 +99,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { result = this.getInstruction(UnmodeledUseTag()) or tag = UnmodeledUseTag() and + result = getInstruction(AliasedUseTag()) + or + tag = AliasedUseTag() and result = this.getInstruction(ExitFunctionTag()) ) } @@ -126,40 +130,32 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { else result = this.getReturnSuccessorInstruction() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { ( tag = EnterFunctionTag() and opcode instanceof Opcode::EnterFunction and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() or tag = UnmodeledDefinitionTag() and opcode instanceof Opcode::UnmodeledDefinition and - resultType instanceof Language::UnknownType and - isLValue = false + resultType = getUnknownType() or tag = AliasedDefinitionTag() and opcode instanceof Opcode::AliasedDefinition and - resultType instanceof Language::UnknownType and - isLValue = false + resultType = getUnknownType() or tag = InitializeThisTag() and opcode instanceof Opcode::InitializeThis and - resultType = getThisType() and - isLValue = true + resultType = getTypeForGLValue(getThisType()) or tag = ReturnValueAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = this.getReturnType() and - not resultType instanceof VoidType and - isLValue = true + not getReturnType() instanceof VoidType and + resultType = getTypeForGLValue(getReturnType()) or ( tag = ReturnTag() and - resultType instanceof VoidType and - isLValue = false and + resultType = getVoidType() and if this.getReturnType() instanceof VoidType then opcode instanceof Opcode::ReturnVoid else opcode instanceof Opcode::ReturnValue @@ -167,8 +163,7 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { or tag = UnwindTag() and opcode instanceof Opcode::Unwind and - resultType instanceof VoidType and - isLValue = false and + resultType = getVoidType() and ( // Only generate the `Unwind` instruction if there is any exception // handling present in the function (compiler generated or not). @@ -178,13 +173,15 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { or tag = UnmodeledUseTag() and opcode instanceof Opcode::UnmodeledUse and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() + or + tag = AliasedUseTag() and + opcode instanceof Opcode::AliasedUse and + resultType = getVoidType() or tag = ExitFunctionTag() and opcode instanceof Opcode::ExitFunction and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() ) } @@ -202,6 +199,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { operandTag instanceof UnmodeledUseOperandTag and result = getUnmodeledDefinitionInstruction() or + tag = AliasedUseTag() and + operandTag instanceof SideEffectOperandTag and + result = getUnmodeledDefinitionInstruction() + or tag = ReturnTag() and not this.getReturnType() instanceof VoidType and ( @@ -213,11 +214,15 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { ) } - final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { + final override CSharpType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { tag = ReturnTag() and not this.getReturnType() instanceof VoidType and operandTag instanceof LoadOperandTag and - result = this.getReturnType() + result = getTypeForPRValue(this.getReturnType()) + or + tag = AliasedUseTag() and + operandTag instanceof SideEffectOperandTag and + result = getUnknownType() } final override IRVariable getInstructionVariable(InstructionTag tag) { @@ -225,10 +230,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { result = this.getReturnVariable() } - final override predicate hasTempVariable(TempVariableTag tag, Type type) { + final override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ReturnValueTempVar() and - type = this.getReturnType() and - not type instanceof VoidType + type = getTypeForPRValue(this.getReturnType()) and + not getReturnType() instanceof VoidType } /** @@ -284,7 +289,7 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { * parameters and local variables, plus any static fields that are directly accessed by the * function. */ - final predicate hasUserVariable(Variable var, Type type) { + final predicate hasUserVariable(Variable var, CSharpType type) { ( var.(Member).isStatic() and exists(VariableAccess access | @@ -294,7 +299,7 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction { or var.(LocalScopeVariable).getCallable() = callable ) and - type = getVariableType(var) + type = getTypeForPRValue(getVariableType(var)) } final private Type getReturnType() { result = callable.getReturnType() } @@ -339,18 +344,14 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter { final override Instruction getChildSuccessor(TranslatedElement child) { none() } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = InitializerVariableAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getVariableType(param) and - isLValue = true + resultType = getTypeForGLValue(param.getType()) or tag = InitializerStoreTag() and opcode instanceof Opcode::InitializeParameter and - resultType = getVariableType(param) and - isLValue = false + resultType = getTypeForPRValue(param.getType()) } final override IRVariable getInstructionVariable(InstructionTag tag) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedInitialization.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedInitialization.qll index d0d98a4918a..0f6094f7a93 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedInitialization.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedInitialization.qll @@ -6,6 +6,7 @@ import csharp private import semmle.code.csharp.ir.implementation.Opcode private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import semmle.code.csharp.ir.internal.CSharpType private import InstructionTag private import TranslatedElement private import TranslatedExpr @@ -87,9 +88,7 @@ abstract class TranslatedListInitialization extends TranslatedInitialization, In ) } - final override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -134,13 +133,10 @@ class TranslatedDirectInitialization extends TranslatedInitialization { result = this.getInitializer().getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = InitializerStoreTag() and opcode instanceof Opcode::Store and - resultType = this.getContext().getTargetType() and - isLValue = false + resultType = getTypeForPRValue(this.getContext().getTargetType()) or needsConversion() and tag = AssignmentConvertRightTag() and @@ -148,8 +144,7 @@ class TranslatedDirectInitialization extends TranslatedInitialization { // crudely represent conversions. Could // be useful to represent the whole chain of conversions opcode instanceof Opcode::Convert and - resultType = this.getContext().getTargetType() and - isLValue = false + resultType = getTypeForPRValue(this.getContext().getTargetType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -218,18 +213,14 @@ abstract class TranslatedElementInitialization extends TranslatedElement { result = this.getInstruction(getElementIndexTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = getElementIndexTag() and opcode instanceof Opcode::Constant and - resultType = getIntType() and - isLValue = false + resultType = getIntType() or tag = getElementAddressTag() and opcode instanceof Opcode::PointerAdd and - resultType = getElementType() and - isLValue = true + resultType = getTypeForGLValue(getElementType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -353,14 +344,11 @@ class TranslatedConstructorInitializer extends TranslatedConstructorCallFromCons else result = this.getConstructorCall().getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { this.needsConversion() and tag = OnlyInstructionTag() and opcode instanceof Opcode::Convert and - resultType = call.getTarget().getDeclaringType() and - isLValue = true + resultType = getTypeForGLValue(call.getTarget().getDeclaringType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedStmt.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedStmt.qll index 3e4934987a3..ff6a7597ad2 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedStmt.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/TranslatedStmt.qll @@ -1,4 +1,5 @@ import csharp +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.TempVariableTag private import semmle.code.csharp.ir.implementation.internal.OperandTag private import InstructionTag @@ -39,13 +40,10 @@ class TranslatedEmptyStmt extends TranslatedStmt { override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::NoOp and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -62,9 +60,7 @@ class TranslatedDeclStmt extends TranslatedStmt { override TranslatedElement getChild(int id) { result = this.getLocalDeclaration(id) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -97,9 +93,7 @@ class TranslatedExprStmt extends TranslatedStmt { override TranslatedElement getChild(int id) { id = 0 and result = this.getExpr() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -161,13 +155,10 @@ class TranslatedReturnValueStmt extends TranslatedReturnStmt, InitializationCont result = this.getInstruction(InitializerVariableAddressTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = InitializerVariableAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = this.getEnclosingFunction().getReturnVariable().getType() and - isLValue = true + resultType = getTypeForGLValue(this.getEnclosingFunction().getFunction().getReturnType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -206,13 +197,10 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt { override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::NoOp and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -240,9 +228,7 @@ class TranslatedTryStmt extends TranslatedStmt { result = this.getCatchClause(id - 2) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -289,14 +275,11 @@ class TranslatedBlock extends TranslatedStmt { override TranslatedElement getChild(int id) { result = this.getStmt(id) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { isEmpty() and opcode instanceof Opcode::NoOp and tag = OnlyInstructionTag() and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() } override Instruction getFirstInstruction() { @@ -357,13 +340,10 @@ abstract class TranslatedClause extends TranslatedStmt { class TranslatedCatchByTypeClause extends TranslatedClause { TranslatedCatchByTypeClause() { stmt instanceof SpecificCatchClause } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = CatchTag() and opcode instanceof Opcode::CatchByType and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() } override TranslatedElement getChild(int id) { @@ -389,9 +369,9 @@ class TranslatedCatchByTypeClause extends TranslatedClause { ) } - override Type getInstructionExceptionType(InstructionTag tag) { + override CSharpType getInstructionExceptionType(InstructionTag tag) { tag = CatchTag() and - result = stmt.(SpecificCatchClause).getVariable().getType() + result = getTypeForPRValue(stmt.(SpecificCatchClause).getVariable().getType()) } private TranslatedLocalDeclaration getParameter() { @@ -405,13 +385,10 @@ class TranslatedCatchByTypeClause extends TranslatedClause { class TranslatedGeneralCatchClause extends TranslatedClause { TranslatedGeneralCatchClause() { stmt instanceof GeneralCatchClause } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = CatchTag() and opcode instanceof Opcode::CatchAny and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -439,18 +416,14 @@ class TranslatedThrowExceptionStmt extends TranslatedStmt, InitializationContext result = this.getInstruction(InitializerVariableAddressTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = ThrowTag() and opcode instanceof Opcode::ThrowValue and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() or tag = InitializerVariableAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = this.getExceptionType() and - isLValue = true + resultType = getTypeForGLValue(this.getExceptionType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -473,9 +446,9 @@ class TranslatedThrowExceptionStmt extends TranslatedStmt, InitializationContext result = getIRTempVariable(stmt, ThrowTempVar()) } - final override predicate hasTempVariable(TempVariableTag tag, Type type) { + final override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ThrowTempVar() and - type = this.getExceptionType() + type = getTypeForPRValue(this.getExceptionType()) } final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -490,10 +463,10 @@ class TranslatedThrowExceptionStmt extends TranslatedStmt, InitializationContext ) } - final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { + final override CSharpType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { tag = ThrowTag() and operandTag instanceof LoadOperandTag and - result = this.getExceptionType() + result = getTypeForPRValue(this.getExceptionType()) } override Instruction getTargetAddress() { @@ -522,13 +495,10 @@ class TranslatedEmptyThrowStmt extends TranslatedStmt { override Instruction getFirstInstruction() { result = this.getInstruction(ThrowTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = ThrowTag() and opcode instanceof Opcode::ReThrow and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -582,9 +552,7 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext { result = this.getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } } @@ -610,9 +578,7 @@ abstract class TranslatedLoop extends TranslatedStmt, ConditionContext { id = 1 and result = this.getBody() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -724,13 +690,10 @@ abstract class TranslatedSpecificJump extends TranslatedStmt { override TranslatedElement getChild(int id) { none() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = OnlyInstructionTag() and opcode instanceof Opcode::NoOp and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -852,13 +815,10 @@ class TranslatedSwitchStmt extends TranslatedStmt { result = getTranslatedStmt(stmt.getChild(id)) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = SwitchBranchTag() and opcode instanceof Opcode::Switch and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() } override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { @@ -935,9 +895,7 @@ class TranslatedUnsafeStmt extends TranslatedStmt { result = this.getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -971,9 +929,7 @@ class TranslatedFixedStmt extends TranslatedStmt { child = this.getBody() and result = this.getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -1012,9 +968,7 @@ class TranslatedLockStmt extends TranslatedStmt { result = getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -1046,9 +1000,7 @@ class TranslatedCheckedUncheckedStmt extends TranslatedStmt { result = this.getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -1089,9 +1041,7 @@ class TranslatedUsingBlockStmt extends TranslatedStmt { child = this.getBody() and result = this.getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -1125,9 +1075,7 @@ class TranslatedUsingDeclStmt extends TranslatedStmt { child = this.getDecl(this.noDecls() - 1) and result = this.getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedCallBase.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedCallBase.qll index b6f708d04d9..db02eadd419 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedCallBase.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedCallBase.qll @@ -10,6 +10,7 @@ private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr private import semmle.code.csharp.ir.Util +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language private import TranslatedExprBase @@ -32,13 +33,10 @@ abstract class TranslatedCallBase extends TranslatedElement { else result = getInstruction(CallTargetTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = CallTag() and opcode instanceof Opcode::Call and - resultType = getCallResultType() and - isLValue = false + resultType = getTypeForPRValue(getCallResultType()) or hasSideEffect() and tag = CallSideEffectTag() and @@ -46,20 +44,18 @@ abstract class TranslatedCallBase extends TranslatedElement { if hasWriteSideEffect() then ( opcode instanceof Opcode::CallSideEffect and - resultType instanceof Language::UnknownType + resultType = getUnknownType() ) else ( opcode instanceof Opcode::CallReadSideEffect and - resultType instanceof Language::UnknownType + resultType = getUnknownType() ) - ) and - isLValue = false + ) or tag = CallTargetTag() and opcode instanceof Opcode::FunctionAddress and // Since the DB does not have a function type, // we just use the UnknownType - resultType instanceof Language::UnknownType and - isLValue = true + resultType = getFunctionAddressType() } override Instruction getChildSuccessor(TranslatedElement child) { @@ -115,11 +111,11 @@ abstract class TranslatedCallBase extends TranslatedElement { result = getUnmodeledDefinitionInstruction() } - final override Type getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { + final override CSharpType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) { tag = CallSideEffectTag() and hasSideEffect() and operandTag instanceof SideEffectOperandTag and - result instanceof Language::UnknownType + result = getUnknownType() } Instruction getResult() { result = getInstruction(CallTag()) } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedConditionBase.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedConditionBase.qll index 7503e3b41e2..8d4c5202d34 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedConditionBase.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedConditionBase.qll @@ -9,6 +9,7 @@ private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedCondition +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language /** @@ -38,13 +39,10 @@ abstract class ValueConditionBase extends ConditionBase { override Instruction getFirstInstruction() { result = getValueExpr().getFirstInstruction() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = ValueConditionConditionalBranchTag() and opcode instanceof Opcode::ConditionalBranch and - resultType instanceof VoidType and - isLValue = false + resultType = getVoidType() } override Instruction getChildSuccessor(TranslatedElement child) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedDeclarationBase.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedDeclarationBase.qll index 2dfd1b745e0..549b554ee94 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedDeclarationBase.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/common/TranslatedDeclarationBase.qll @@ -11,6 +11,7 @@ private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedInitialization +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language abstract class LocalVariableDeclarationBase extends TranslatedElement { @@ -18,19 +19,15 @@ abstract class LocalVariableDeclarationBase extends TranslatedElement { override Instruction getFirstInstruction() { result = getVarAddress() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = InitializerVariableAddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getVarType() and - isLValue = true + resultType = getTypeForGLValue(getVarType()) or hasUninitializedInstruction() and tag = InitializerStoreTag() and opcode instanceof Opcode::Uninitialized and - resultType = getVarType() and - isLValue = false + resultType = getTypeForPRValue(getVarType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Common.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Common.qll index 1b29ac7c543..fc8665efedf 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Common.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Common.qll @@ -8,6 +8,7 @@ import csharp private import semmle.code.csharp.ir.implementation.Opcode private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.TempVariableTag private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedFunction @@ -30,9 +31,7 @@ private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language abstract class TranslatedCompilerGeneratedTry extends TranslatedCompilerGeneratedStmt { override Stmt generatedBy; - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -72,13 +71,10 @@ abstract class TranslatedCompilerGeneratedTry extends TranslatedCompilerGenerate * The concrete implementation needs to specify the immediate operand that represents the constant. */ abstract class TranslatedCompilerGeneratedConstant extends TranslatedCompilerGeneratedExpr { - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { opcode instanceof Opcode::Constant and tag = OnlyInstructionTag() and - resultType = getResultType() and - isLValue = false + resultType = getTypeForPRValue(getResultType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { @@ -117,9 +113,7 @@ abstract class TranslatedCompilerGeneratedBlock extends TranslatedCompilerGenera ) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -170,9 +164,7 @@ abstract class TranslatedCompilerGeneratedIfStmt extends TranslatedCompilerGener result = getParent().getChildSuccessor(this) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } } @@ -190,19 +182,15 @@ abstract class TranslatedCompilerGeneratedVariableAccess extends TranslatedCompi override Instruction getChildSuccessor(TranslatedElement child) { none() } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { tag = AddressTag() and opcode instanceof Opcode::VariableAddress and - resultType = getResultType() and - isLValue = true + resultType = getTypeForGLValue(getResultType()) or needsLoad() and tag = LoadTag() and opcode instanceof Opcode::Load and - resultType = getResultType() and - isLValue = false + resultType = getTypeForPRValue(getResultType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Foreach.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Foreach.qll index 06a001e8912..4a4468b0bd3 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Foreach.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Foreach.qll @@ -36,6 +36,7 @@ import csharp private import semmle.code.csharp.ir.implementation.Opcode private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.TempVariableTag private import semmle.code.csharp.ir.implementation.raw.internal.InstructionTag private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr @@ -112,9 +113,7 @@ class TranslatedForeachWhile extends TranslatedCompilerGeneratedStmt, ConditionC TranslatedForeachWhile() { this = TTranslatedCompilerGeneratedElement(generatedBy, 2) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { none() } @@ -329,9 +328,9 @@ private class TranslatedForeachEnumerator extends TranslatedCompilerGeneratedDec TranslatedForeachEnumerator() { this = TTranslatedCompilerGeneratedElement(generatedBy, 8) } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ForeachEnumTempVar() and - type = getInitialization().getCallResultType() + type = getTypeForPRValue(getInitialization().getCallResultType()) } override IRTempVariable getIRVariable() { @@ -388,9 +387,9 @@ private class TranslatedMoveNextEnumAcc extends TTranslatedCompilerGeneratedElem override Type getResultType() { result instanceof BoolType } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ForeachEnumTempVar() and - type = getResultType() + type = getTypeForPRValue(getResultType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -413,9 +412,9 @@ private class TranslatedForeachCurrentEnumAcc extends TTranslatedCompilerGenerat override Type getResultType() { result instanceof BoolType } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ForeachEnumTempVar() and - type = getResultType() + type = getTypeForPRValue(getResultType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -438,9 +437,9 @@ private class TranslatedForeachDisposeEnumAcc extends TTranslatedCompilerGenerat override Type getResultType() { result instanceof BoolType } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = ForeachEnumTempVar() and - type = getResultType() + type = getTypeForPRValue(getResultType()) } override IRVariable getInstructionVariable(InstructionTag tag) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Lock.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Lock.qll index 1fb0167b4cc..7a0ec9d5cbc 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Lock.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/Lock.qll @@ -21,6 +21,7 @@ import csharp private import semmle.code.csharp.ir.implementation.Opcode private import semmle.code.csharp.ir.implementation.internal.OperandTag +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.TempVariableTag private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedExpr private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedElement @@ -259,9 +260,9 @@ private class TranslatedLockWasTakenDecl extends TranslatedCompilerGeneratedDecl TranslatedLockWasTakenDecl() { this = TTranslatedCompilerGeneratedElement(generatedBy, 8) } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = LockWasTakenTemp() and - type instanceof BoolType + type = getBoolType() } override IRTempVariable getIRVariable() { @@ -290,9 +291,9 @@ private class TranslatedLockedVarDecl extends TranslatedCompilerGeneratedDeclara TranslatedLockedVarDecl() { this = TTranslatedCompilerGeneratedElement(generatedBy, 9) } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = LockedVarTemp() and - type = generatedBy.getExpr().getType() + type = getTypeForPRValue(generatedBy.getExpr().getType()) } override IRTempVariable getIRVariable() { @@ -321,9 +322,9 @@ private class TranslatedMonitorEnterVarAcc extends TTranslatedCompilerGeneratedE override Type getResultType() { result = generatedBy.getExpr().getType() } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = LockedVarTemp() and - type = getResultType() + type = getTypeForPRValue(getResultType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -352,9 +353,9 @@ private class TranslatedMonitorExitVarAcc extends TTranslatedCompilerGeneratedEl result = getTempVariable(LockedVarTemp()) } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = LockedVarTemp() and - type = getResultType() + type = getTypeForPRValue(getResultType()) } override predicate needsLoad() { any() } @@ -372,9 +373,9 @@ private class TranslatedLockWasTakenCondVarAcc extends TTranslatedCompilerGenera override Type getResultType() { result instanceof BoolType } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = LockWasTakenTemp() and - type = getResultType() + type = getTypeForPRValue(getResultType()) } override IRVariable getInstructionVariable(InstructionTag tag) { @@ -397,9 +398,9 @@ private class TranslatedLockWasTakenRefArg extends TTranslatedCompilerGeneratedE override Type getResultType() { result instanceof BoolType } - override predicate hasTempVariable(TempVariableTag tag, Type type) { + override predicate hasTempVariable(TempVariableTag tag, CSharpType type) { tag = LockWasTakenTemp() and - type = getResultType() + type = getTypeForPRValue(getResultType()) } override IRVariable getInstructionVariable(InstructionTag tag) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedDeclaration.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedDeclaration.qll index 3083a91e706..b13702ac168 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedDeclaration.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/raw/internal/desugar/internal/TranslatedCompilerGeneratedDeclaration.qll @@ -12,6 +12,7 @@ private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedEleme private import semmle.code.csharp.ir.implementation.raw.internal.TranslatedFunction private import semmle.code.csharp.ir.implementation.raw.internal.common.TranslatedDeclarationBase private import TranslatedCompilerGeneratedElement +private import semmle.code.csharp.ir.internal.CSharpType private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language abstract class TranslatedCompilerGeneratedDeclaration extends LocalVariableDeclarationBase, @@ -28,18 +29,15 @@ abstract class TranslatedCompilerGeneratedDeclaration extends LocalVariableDecla child = getInitialization() and result = getInstruction(InitializerStoreTag()) } - override predicate hasInstruction( - Opcode opcode, InstructionTag tag, Type resultType, boolean isLValue - ) { - LocalVariableDeclarationBase.super.hasInstruction(opcode, tag, resultType, isLValue) + override predicate hasInstruction(Opcode opcode, InstructionTag tag, CSharpType resultType) { + LocalVariableDeclarationBase.super.hasInstruction(opcode, tag, resultType) or // we can reuse the initializer store tag // since compiler generated declarations // do not have the `Uninitialized` instruction tag = InitializerStoreTag() and opcode instanceof Opcode::Store and - resultType = getVarType() and - isLValue = false + resultType = getTypeForPRValue(getVarType()) } override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll index 278040f8ab8..badd48552a5 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IR.qll @@ -5,6 +5,7 @@ import IRVariable import Operand private import internal.IRImports as Imports import Imports::EdgeKind +import Imports::IRType import Imports::MemoryAccessKind private newtype TIRPropertyProvider = MkIRPropertyProvider() diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll index 3921472dc8e..de66a3c99dc 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRSanity.qll @@ -1,2 +1,3 @@ private import IR import InstructionSanity +import IRTypeSanity diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll index a7ca4593ac4..1a607f82c29 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/IRVariable.qll @@ -5,6 +5,7 @@ import Imports::TempVariableTag private import Imports::IRUtilities private import Imports::TTempVariableTag private import Imports::TIRVariable +private import Imports::IRType IRUserVariable getIRUserVariable(Language::Function func, Language::Variable var) { result.getVariable() = var and @@ -24,7 +25,17 @@ abstract class IRVariable extends TIRVariable { /** * Gets the type of the variable. */ - abstract Language::Type getType(); + final Language::Type getType() { getLanguageType().hasType(result, false) } + + /** + * Gets the language-neutral type of the variable. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the variable. + */ + abstract Language::LanguageType getLanguageType(); /** * Gets the AST node that declared this variable, or that introduced this @@ -59,7 +70,7 @@ abstract class IRVariable extends TIRVariable { */ class IRUserVariable extends IRVariable, TIRUserVariable { Language::Variable var; - Language::Type type; + Language::LanguageType type; IRUserVariable() { this = TIRUserVariable(var, type, func) } @@ -71,7 +82,7 @@ class IRUserVariable extends IRVariable, TIRUserVariable { result = getVariable().toString() + " " + getVariable().getLocation().toString() } - final override Language::Type getType() { result = type } + final override Language::LanguageType getLanguageType() { result = type } /** * Gets the original user-declared variable. @@ -110,11 +121,11 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) { class IRTempVariable extends IRVariable, IRAutomaticVariable, TIRTempVariable { Language::AST ast; TempVariableTag tag; - Language::Type type; + Language::LanguageType type; IRTempVariable() { this = TIRTempVariable(func, ast, tag, type) } - final override Language::Type getType() { result = type } + final override Language::LanguageType getLanguageType() { result = type } final override Language::AST getAST() { result = ast } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll index 6d5e697150e..049983c9126 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Instruction.qll @@ -5,6 +5,7 @@ import IRVariable import Operand private import internal.InstructionImports as Imports import Imports::EdgeKind +import Imports::IRType import Imports::MemoryAccessKind import Imports::Opcode private import Imports::OperandTag @@ -49,7 +50,8 @@ module InstructionSanity { ( opcode instanceof ReadSideEffectOpcode or opcode instanceof Opcode::InlineAsm or - opcode instanceof Opcode::CallSideEffect + opcode instanceof Opcode::CallSideEffect or + opcode instanceof Opcode::AliasedUse ) and tag instanceof SideEffectOperandTag ) @@ -113,10 +115,12 @@ module InstructionSanity { } query predicate missingOperandType(Operand operand, string message) { - exists(Language::Function func | + exists(Language::Function func, Instruction use | not exists(operand.getType()) and - func = operand.getUse().getEnclosingFunction() and - message = "Operand missing type in function '" + Language::getIdentityString(func) + "'." + use = operand.getUse() and + func = use.getEnclosingFunction() and + message = "Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() + + "' missing type in function '" + Language::getIdentityString(func) + "'." ) } @@ -260,6 +264,7 @@ module InstructionSanity { ) { exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex | not useOperand.getUse() instanceof UnmodeledUseInstruction and + not defInstr instanceof UnmodeledDefinitionInstruction and pointOfEvaluation(useOperand, useBlock, useIndex) and defInstr = useOperand.getAnyDef() and ( @@ -321,7 +326,7 @@ class Instruction extends Construction::TInstruction { } private string getResultPrefix() { - if getResultType() instanceof Language::VoidType + if getResultIRType() instanceof IRVoidType then result = "v" else if hasMemoryResult() @@ -353,23 +358,6 @@ class Instruction extends Construction::TInstruction { ) } - bindingset[type] - private string getValueCategoryString(string type) { - if isGLValue() then result = "glval<" + type + ">" else result = type - } - - string getResultTypeString() { - exists(string valcat | - valcat = getValueCategoryString(getResultType().toString()) and - if - getResultType() instanceof Language::UnknownType and - not isGLValue() and - exists(getResultSize()) - then result = valcat + "[" + getResultSize().toString() + "]" - else result = valcat - ) - } - /** * Gets a human-readable string that uniquely identifies this instruction * within the function. This string is used to refer to this instruction when @@ -389,7 +377,9 @@ class Instruction extends Construction::TInstruction { * * Example: `r1_1(int*)` */ - final string getResultString() { result = getResultId() + "(" + getResultTypeString() + ")" } + final string getResultString() { + result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")" + } /** * Gets a string describing the operands of this instruction, suitable for @@ -457,6 +447,16 @@ class Instruction extends Construction::TInstruction { result = Construction::getInstructionUnconvertedResultExpression(this) } + final Language::LanguageType getResultLanguageType() { + result = Construction::getInstructionResultType(this) + } + + /** + * Gets the type of the result produced by this instruction. If the instruction does not produce + * a result, its result type will be `IRVoidType`. + */ + final IRType getResultIRType() { result = getResultLanguageType().getIRType() } + /** * Gets the type of the result produced by this instruction. If the * instruction does not produce a result, its result type will be `VoidType`. @@ -464,7 +464,16 @@ class Instruction extends Construction::TInstruction { * If `isGLValue()` holds, then the result type of this instruction should be * thought of as "pointer to `getResultType()`". */ - final Language::Type getResultType() { Construction::instructionHasType(this, result, _) } + final Language::Type getResultType() { + exists(Language::LanguageType resultType | + resultType = getResultLanguageType() and + ( + resultType.hasUnspecifiedType(result, _) + or + not resultType.hasUnspecifiedType(_, _) and result instanceof Language::UnknownType + ) + ) + } /** * Holds if the result produced by this instruction is a glvalue. If this @@ -484,7 +493,7 @@ class Instruction extends Construction::TInstruction { * result of the `Load` instruction is a prvalue of type `int`, representing * the integer value loaded from variable `x`. */ - final predicate isGLValue() { Construction::instructionHasType(this, _, true) } + final predicate isGLValue() { Construction::getInstructionResultType(this).hasType(_, true) } /** * Gets the size of the result produced by this instruction, in bytes. If the @@ -493,16 +502,7 @@ class Instruction extends Construction::TInstruction { * If `this.isGLValue()` holds for this instruction, the value of * `getResultSize()` will always be the size of a pointer. */ - final int getResultSize() { - if isGLValue() - then - // a glvalue is always pointer-sized. - result = Language::getPointerSize() - else - if getResultType() instanceof Language::UnknownType - then result = Construction::getInstructionResultSize(this) - else result = Language::getTypeSize(getResultType()) - } + final int getResultSize() { result = Construction::getInstructionResultType(this).getByteSize() } /** * Gets the opcode that specifies the operation performed by this instruction. @@ -1384,7 +1384,7 @@ class CatchInstruction extends Instruction { * An instruction that catches an exception of a specific type. */ class CatchByTypeInstruction extends CatchInstruction { - Language::Type exceptionType; + Language::LanguageType exceptionType; CatchByTypeInstruction() { getOpcode() instanceof Opcode::CatchByType and @@ -1396,7 +1396,7 @@ class CatchByTypeInstruction extends CatchInstruction { /** * Gets the type of exception to be caught. */ - final Language::Type getExceptionType() { result = exceptionType } + final Language::LanguageType getExceptionType() { result = exceptionType } } /** @@ -1423,6 +1423,13 @@ class AliasedDefinitionInstruction extends Instruction { final override MemoryAccessKind getResultMemoryAccess() { result instanceof EscapedMemoryAccess } } +/** + * An instruction that consumes all escaped memory on exit from the function. + */ +class AliasedUseInstruction extends Instruction { + AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse } +} + class UnmodeledUseInstruction extends Instruction { UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll index a23fb081afe..07dd107bf12 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/Operand.qll @@ -1,9 +1,10 @@ private import internal.IRInternal -import Instruction -import IRBlock +private import Instruction +private import IRBlock private import internal.OperandImports as Imports -import Imports::MemoryAccessKind -import Imports::Overlap +private import Imports::MemoryAccessKind +private import Imports::IRType +private import Imports::Overlap private import Imports::OperandTag cached @@ -143,22 +144,40 @@ class Operand extends TOperand { * the definition type, such as in the case of a partial read or a read from a pointer that * has been cast to a different type. */ - Language::Type getType() { result = getAnyDef().getResultType() } + Language::LanguageType getLanguageType() { result = getAnyDef().getResultLanguageType() } + + /** + * Gets the language-neutral type of the value consumed by this operand. This is usually the same + * as the result type of the definition instruction consumed by this operand. For register + * operands, this is always the case. For some memory operands, the operand type may be different + * from the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final IRType getIRType() { result = getLanguageType().getIRType() } + + /** + * Gets the type of the value consumed by this operand. This is usually the same as the + * result type of the definition instruction consumed by this operand. For register operands, + * this is always the case. For some memory operands, the operand type may be different from + * the definition type, such as in the case of a partial read or a read from a pointer that + * has been cast to a different type. + */ + final Language::Type getType() { getLanguageType().hasType(result, _) } /** * Holds if the value consumed by this operand is a glvalue. If this * holds, the value of the operand represents the address of a location, * and the type of the location is given by `getType()`. If this does * not hold, the value of the operand represents a value whose type is - * given by `getResultType()`. + * given by `getType()`. */ - predicate isGLValue() { getAnyDef().isGLValue() } + final predicate isGLValue() { getLanguageType().hasType(_, true) } /** * Gets the size of the value consumed by this operand, in bytes. If the operand does not have * a known constant size, this predicate does not hold. */ - int getSize() { result = Language::getTypeSize(getType()) } + final int getSize() { result = getLanguageType().getByteSize() } } /** @@ -170,11 +189,6 @@ class MemoryOperand extends Operand { this = TPhiOperand(_, _, _, _) } - override predicate isGLValue() { - // A `MemoryOperand` can never be a glvalue - none() - } - /** * Gets the kind of memory access performed by the operand. */ @@ -239,7 +253,7 @@ class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOpe class TypedOperand extends NonPhiMemoryOperand { override TypedOperandTag tag; - final override Language::Type getType() { + final override Language::LanguageType getLanguageType() { result = Construction::getInstructionOperandType(useInstr, tag) } } @@ -381,13 +395,10 @@ class PositionalArgumentOperand extends ArgumentOperand { class SideEffectOperand extends TypedOperand { override SideEffectOperandTag tag; - final override int getSize() { - if getType() instanceof Language::UnknownType - then result = Construction::getInstructionOperandSize(useInstr, tag) - else result = Language::getTypeSize(getType()) - } - override MemoryAccessKind getMemoryAccess() { + useInstr instanceof AliasedUseInstruction and + result instanceof NonLocalMayMemoryAccess + or useInstr instanceof CallSideEffectInstruction and result instanceof EscapedMayMemoryAccess or diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll index f887d39d10b..79e5a2b6713 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll @@ -1,5 +1,5 @@ private import internal.ValueNumberingInternal -private import csharp +private import internal.ValueNumberingImports private import IR /** @@ -23,31 +23,32 @@ newtype TValueNumber = initializeParameterValueNumber(_, irFunc, var) } or TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or - TConstantValueNumber(IRFunction irFunc, Type type, string value) { + TConstantValueNumber(IRFunction irFunc, IRType type, string value) { constantValueNumber(_, irFunc, type, value) } or - TStringConstantValueNumber(IRFunction irFunc, Type type, string value) { + TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) { stringConstantValueNumber(_, irFunc, type, value) } or - TFieldAddressValueNumber(IRFunction irFunc, Field field, ValueNumber objectAddress) { + TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) { fieldAddressValueNumber(_, irFunc, field, objectAddress) } or TBinaryValueNumber( - IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand + IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand ) { binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand) } or TPointerArithmeticValueNumber( - IRFunction irFunc, Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, + IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand ) { pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand) } or - TUnaryValueNumber(IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand) { + TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) { unaryValueNumber(_, irFunc, opcode, type, operand) } or TInheritanceConversionValueNumber( - IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand + IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass, + ValueNumber operand ) { inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand) } or @@ -59,7 +60,7 @@ newtype TValueNumber = class ValueNumber extends TValueNumber { final string toString() { result = getExampleInstruction().getResultId() } - final Location getLocation() { result = getExampleInstruction().getLocation() } + final Language::Location getLocation() { result = getExampleInstruction().getLocation() } /** * Gets the instructions that have been assigned this value number. This will always produce at @@ -150,23 +151,23 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF } private predicate constantValueNumber( - ConstantInstruction instr, IRFunction irFunc, Type type, string value + ConstantInstruction instr, IRFunction irFunc, IRType type, string value ) { instr.getEnclosingIRFunction() = irFunc and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getValue() = value } private predicate stringConstantValueNumber( - StringConstantInstruction instr, IRFunction irFunc, Type type, string value + StringConstantInstruction instr, IRFunction irFunc, IRType type, string value ) { instr.getEnclosingIRFunction() = irFunc and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getValue().getValue() = value } private predicate fieldAddressValueNumber( - FieldAddressInstruction instr, IRFunction irFunc, Field field, ValueNumber objectAddress + FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress ) { instr.getEnclosingIRFunction() = irFunc and instr.getField() = field and @@ -174,43 +175,43 @@ private predicate fieldAddressValueNumber( } private predicate binaryValueNumber( - BinaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber leftOperand, + BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand ) { instr.getEnclosingIRFunction() = irFunc and not instr instanceof PointerArithmeticInstruction and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and valueNumber(instr.getLeft()) = leftOperand and valueNumber(instr.getRight()) = rightOperand } private predicate pointerArithmeticValueNumber( - PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, Type type, int elementSize, - ValueNumber leftOperand, ValueNumber rightOperand + PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, + int elementSize, ValueNumber leftOperand, ValueNumber rightOperand ) { instr.getEnclosingIRFunction() = irFunc and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and instr.getElementSize() = elementSize and valueNumber(instr.getLeft()) = leftOperand and valueNumber(instr.getRight()) = rightOperand } private predicate unaryValueNumber( - UnaryInstruction instr, IRFunction irFunc, Opcode opcode, Type type, ValueNumber operand + UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand ) { instr.getEnclosingIRFunction() = irFunc and not instr instanceof InheritanceConversionInstruction and not instr instanceof CopyInstruction and instr.getOpcode() = opcode and - instr.getResultType() = type and + instr.getResultIRType() = type and valueNumber(instr.getUnary()) = operand } private predicate inheritanceConversionValueNumber( - InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, Class baseClass, - Class derivedClass, ValueNumber operand + InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode, + Language::Class baseClass, Language::Class derivedClass, ValueNumber operand ) { instr.getEnclosingIRFunction() = irFunc and instr.getOpcode() = opcode and @@ -225,7 +226,7 @@ private predicate inheritanceConversionValueNumber( */ private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) { instr.getEnclosingIRFunction() = irFunc and - not instr.getResultType() instanceof VoidType and + not instr.getResultIRType() instanceof IRVoidType and not numberableInstruction(instr) } @@ -269,38 +270,41 @@ private ValueNumber nonUniqueValueNumber(Instruction instr) { initializeThisValueNumber(instr, irFunc) and result = TInitializeThisValueNumber(irFunc) or - exists(Type type, string value | + exists(IRType type, string value | constantValueNumber(instr, irFunc, type, value) and result = TConstantValueNumber(irFunc, type, value) ) or - exists(Type type, string value | + exists(IRType type, string value | stringConstantValueNumber(instr, irFunc, type, value) and result = TStringConstantValueNumber(irFunc, type, value) ) or - exists(Field field, ValueNumber objectAddress | + exists(Language::Field field, ValueNumber objectAddress | fieldAddressValueNumber(instr, irFunc, field, objectAddress) and result = TFieldAddressValueNumber(irFunc, field, objectAddress) ) or - exists(Opcode opcode, Type type, ValueNumber leftOperand, ValueNumber rightOperand | + exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand | binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand) ) or - exists(Opcode opcode, Type type, ValueNumber operand | + exists(Opcode opcode, IRType type, ValueNumber operand | unaryValueNumber(instr, irFunc, opcode, type, operand) and result = TUnaryValueNumber(irFunc, opcode, type, operand) ) or - exists(Opcode opcode, Class baseClass, Class derivedClass, ValueNumber operand | + exists( + Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand + | inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand) ) or exists( - Opcode opcode, Type type, int elementSize, ValueNumber leftOperand, ValueNumber rightOperand + Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand, + ValueNumber rightOperand | pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand, rightOperand) and diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll new file mode 100644 index 00000000000..555cb581d37 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll @@ -0,0 +1 @@ +import semmle.code.csharp.ir.internal.Overlap diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll index 188c68483ad..9e4a85dd8ed 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll @@ -1 +1,2 @@ import semmle.code.csharp.ir.implementation.unaliased_ssa.IR as IR +import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll index b5abc1049f3..3b716c201ac 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRImports.qll @@ -1,2 +1,3 @@ import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind +import semmle.code.csharp.ir.implementation.IRType as IRType import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll index 20c299416ef..9ab8de41259 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll @@ -1,3 +1,4 @@ +import semmle.code.csharp.ir.implementation.IRType as IRType import semmle.code.csharp.ir.implementation.TempVariableTag as TempVariableTag import semmle.code.csharp.ir.internal.IRUtilities as IRUtilities import semmle.code.csharp.ir.internal.TempVariableTag as TTempVariableTag diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll index da9b8e6e877..8628f5be373 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/InstructionImports.qll @@ -1,4 +1,5 @@ import semmle.code.csharp.ir.implementation.EdgeKind as EdgeKind +import semmle.code.csharp.ir.implementation.IRType as IRType import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind import semmle.code.csharp.ir.implementation.Opcode as Opcode import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll index 13667df7276..997185fb9f7 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/OperandImports.qll @@ -1,3 +1,4 @@ import semmle.code.csharp.ir.implementation.MemoryAccessKind as MemoryAccessKind +import semmle.code.csharp.ir.implementation.IRType as IRType import semmle.code.csharp.ir.internal.Overlap as Overlap import semmle.code.csharp.ir.implementation.internal.OperandTag as OperandTag diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll index a5c09c6401b..23121523a6b 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll @@ -4,34 +4,52 @@ private import Alias private import SSAConstruction private import DebugSSA +bindingset[offset] +private string getKeySuffixForOffset(int offset) { + if offset % 2 = 0 then result = "" else result = "_Chi" +} + +bindingset[offset] +private int getIndexForOffset(int offset) { result = offset / 2 } + /** * Property provide that dumps the memory access of each result. Useful for debugging SSA * construction. */ class PropertyProvider extends IRPropertyProvider { override string getInstructionProperty(Instruction instruction, string key) { - exists(MemoryLocation location | - location = getResultMemoryLocation(instruction) and - ( - key = "ResultMemoryLocation" and result = location.toString() - or - key = "ResultVirtualVariable" and result = location.getVirtualVariable().toString() + key = "ResultMemoryLocation" and + result = strictconcat(MemoryLocation loc | + loc = getResultMemoryLocation(instruction) + | + loc.toString(), "," ) - ) or - exists(MemoryLocation location | - location = getOperandMemoryLocation(instruction.getAnOperand()) and - ( - key = "OperandMemoryAccess" and result = location.toString() - or - key = "OperandVirtualVariable" and result = location.getVirtualVariable().toString() + key = "ResultVirtualVariable" and + result = strictconcat(MemoryLocation loc | + loc = getResultMemoryLocation(instruction) + | + loc.getVirtualVariable().toString(), "," ) - ) or - exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex | - hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and - defBlock.getInstruction(defIndex) = instruction and - key = "DefinitionRank[" + useLocation.toString() + "]" and + key = "OperandMemoryLocation" and + result = strictconcat(MemoryLocation loc | + loc = getOperandMemoryLocation(instruction.getAnOperand()) + | + loc.toString(), "," + ) + or + key = "OperandVirtualVariable" and + result = strictconcat(MemoryLocation loc | + loc = getOperandMemoryLocation(instruction.getAnOperand()) + | + loc.getVirtualVariable().toString(), "," + ) + or + exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset | + hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and + defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and + key = "DefinitionRank" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + "]" and result = defRank.toString() ) or @@ -41,10 +59,11 @@ class PropertyProvider extends IRPropertyProvider { result = useRank.toString() ) or - exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defIndex | - hasDefinitionAtRank(useLocation, _, defBlock, defRank, defIndex) and - defBlock.getInstruction(defIndex) = instruction and - key = "DefinitionReachesUse[" + useLocation.toString() + "]" and + exists(MemoryLocation useLocation, IRBlock defBlock, int defRank, int defOffset | + hasDefinitionAtRank(useLocation, _, defBlock, defRank, defOffset) and + defBlock.getInstruction(getIndexForOffset(defOffset)) = instruction and + key = "DefinitionReachesUse" + getKeySuffixForOffset(defOffset) + "[" + useLocation.toString() + + "]" and result = strictconcat(IRBlock useBlock, int useRank, int useIndex | exists(Instruction useInstruction | hasUseAtRank(useLocation, useBlock, useRank, useInstruction) and diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 159b6328ab0..7152dec5c4a 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -1,9 +1,6 @@ import SSAConstructionInternal -private import semmle.code.csharp.ir.implementation.Opcode -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.Overlap +private import SSAConstructionImports private import NewIR -private import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language private class OldBlock = Reachability::ReachableBlock; @@ -51,13 +48,13 @@ private module Cached { cached predicate hasTempVariable( - Language::Function func, Language::AST ast, TempVariableTag tag, Language::Type type + Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type ) { exists(OldIR::IRTempVariable var | var.getEnclosingFunction() = func and var.getAST() = ast and var.getTag() = tag and - var.getType() = type + var.getLanguageType() = type ) } @@ -137,24 +134,12 @@ private module Cached { } cached - Language::Type getInstructionOperandType(Instruction instr, TypedOperandTag tag) { + Language::LanguageType getInstructionOperandType(Instruction instr, TypedOperandTag tag) { exists(OldInstruction oldInstruction, OldIR::TypedOperand oldOperand | oldInstruction = getOldInstruction(instr) and oldOperand = oldInstruction.getAnOperand() and tag = oldOperand.getOperandTag() and - result = oldOperand.getType() - ) - } - - cached - int getInstructionOperandSize(Instruction instr, SideEffectOperandTag tag) { - exists(OldInstruction oldInstruction, OldIR::SideEffectOperand oldOperand | - oldInstruction = getOldInstruction(instr) and - oldOperand = oldInstruction.getAnOperand() and - tag = oldOperand.getOperandTag() and - // Only return a result for operands that need an explicit result size. - oldOperand.getType() instanceof Language::UnknownType and - result = oldOperand.getSize() + result = oldOperand.getLanguageType() ) } @@ -273,29 +258,25 @@ private module Cached { } cached - predicate instructionHasType(Instruction instruction, Language::Type type, boolean isGLValue) { + Language::LanguageType getInstructionResultType(Instruction instruction) { exists(OldInstruction oldInstruction | instruction = WrappedInstruction(oldInstruction) and - type = oldInstruction.getResultType() and - if oldInstruction.isGLValue() then isGLValue = true else isGLValue = false + result = oldInstruction.getResultLanguageType() ) or exists(OldInstruction oldInstruction, Alias::VirtualVariable vvar | instruction = Chi(oldInstruction) and hasChiNode(vvar, oldInstruction) and - type = vvar.getType() and - isGLValue = false + result = vvar.getType() ) or exists(Alias::MemoryLocation location | instruction = Phi(_, location) and - type = location.getType() and - isGLValue = false + result = location.getType() ) or instruction = Unreached(_) and - type instanceof Language::VoidType and - isGLValue = false + result = Language::getVoidType() } cached @@ -373,7 +354,7 @@ private module Cached { } cached - Language::Type getInstructionExceptionType(Instruction instruction) { + Language::LanguageType getInstructionExceptionType(Instruction instruction) { result = getOldInstruction(instruction).(OldIR::CatchByTypeInstruction).getExceptionType() } @@ -382,13 +363,6 @@ private module Cached { result = getOldInstruction(instruction).(OldIR::PointerArithmeticInstruction).getElementSize() } - cached - int getInstructionResultSize(Instruction instruction) { - // Only return a result for instructions that needed an explicit result size. - instruction.getResultType() instanceof Language::UnknownType and - result = getOldInstruction(instruction).getResultSize() - } - cached predicate getInstructionInheritance( Instruction instruction, Language::Class baseClass, Language::Class derivedClass @@ -844,7 +818,7 @@ module DefUse { } /** - * Expose some of the internal predicates to PrintSSA.qll. We do this by publicly importing those modules in the + * Expose some of the internal predicates to PrintSSA.qll. We do this by publically importing those modules in the * `DebugSSA` module, which is then imported by PrintSSA. */ module DebugSSA { diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll new file mode 100644 index 00000000000..6ee403226bc --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionImports.qll @@ -0,0 +1,3 @@ +import semmle.code.csharp.ir.implementation.Opcode +import semmle.code.csharp.ir.implementation.internal.OperandTag +import semmle.code.csharp.ir.internal.Overlap diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll index a4ced8d6d7c..44ff1f110c1 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SSAConstructionInternal.qll @@ -2,4 +2,5 @@ import semmle.code.csharp.ir.implementation.raw.IR as OldIR import semmle.code.csharp.ir.implementation.raw.internal.reachability.ReachableBlock as Reachability import semmle.code.csharp.ir.implementation.raw.internal.reachability.Dominance as Dominance import semmle.code.csharp.ir.implementation.unaliased_ssa.IR as NewIR +import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language import SimpleSSA as Alias diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll index 42f582f7df2..5925631b67c 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll @@ -1,24 +1,20 @@ import AliasAnalysis -private import csharp -private import semmle.code.csharp.ir.implementation.raw.IR -private import semmle.code.csharp.ir.internal.IntegerConstant as Ints -private import semmle.code.csharp.ir.implementation.internal.OperandTag -private import semmle.code.csharp.ir.internal.Overlap +private import SimpleSSAImports private class IntValue = Ints::IntValue; private predicate hasResultMemoryAccess( - Instruction instr, IRVariable var, Type type, IntValue bitOffset + Instruction instr, IRVariable var, Language::LanguageType type, IntValue bitOffset ) { resultPointsTo(instr.getResultAddressOperand().getAnyDef(), var, bitOffset) and - type = instr.getResultType() + type = instr.getResultLanguageType() } private predicate hasOperandMemoryAccess( - MemoryOperand operand, IRVariable var, Type type, IntValue bitOffset + MemoryOperand operand, IRVariable var, Language::LanguageType type, IntValue bitOffset ) { resultPointsTo(operand.getAddressOperand().getAnyDef(), var, bitOffset) and - type = operand.getType() + type = operand.getLanguageType() } /** @@ -30,17 +26,17 @@ private predicate isVariableModeled(IRVariable var) { not variableAddressEscapes(var) and // There's no need to check for the right size. An `IRVariable` never has an `UnknownType`, so the test for // `type = var.getType()` is sufficient. - forall(Instruction instr, Type type, IntValue bitOffset | + forall(Instruction instr, Language::LanguageType type, IntValue bitOffset | hasResultMemoryAccess(instr, var, type, bitOffset) | bitOffset = 0 and - type = var.getType() + type.getIRType() = var.getIRType() ) and - forall(MemoryOperand operand, Type type, IntValue bitOffset | + forall(MemoryOperand operand, Language::LanguageType type, IntValue bitOffset | hasOperandMemoryAccess(operand, var, type, bitOffset) | bitOffset = 0 and - type = var.getType() + type.getIRType() = var.getIRType() ) } @@ -59,7 +55,7 @@ class MemoryLocation extends TMemoryLocation { final VirtualVariable getVirtualVariable() { result = this } - final Type getType() { result = var.getType() } + final Language::LanguageType getType() { result = var.getLanguageType() } final string getUniqueId() { result = var.getUniqueId() } } diff --git a/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll new file mode 100644 index 00000000000..b6f92fda444 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/internal/SimpleSSAImports.qll @@ -0,0 +1,5 @@ +import semmle.code.csharp.ir.implementation.raw.IR +import semmle.code.csharp.ir.internal.IntegerConstant as Ints +import semmle.code.csharp.ir.implementation.internal.OperandTag +import semmle.code.csharp.ir.internal.IRCSharpLanguage as Language +import semmle.code.csharp.ir.internal.Overlap diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/CSharpType.qll b/csharp/ql/src/semmle/code/csharp/ir/internal/CSharpType.qll new file mode 100644 index 00000000000..ac400b210a5 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/ir/internal/CSharpType.qll @@ -0,0 +1,366 @@ +private import csharp +private import semmle.code.csharp.ir.implementation.IRType +private import IRCSharpLanguage as Language + +int getTypeSize(Type type) { + // REVIEW: Is this complete? + result = type.(SimpleType).getSize() + or + result = getTypeSize(type.(Enum).getUnderlyingType()) + or + // TODO: Generate a reasonable size + type instanceof Struct and result = 16 + or + type instanceof RefType and result = getPointerSize() + or + type instanceof PointerType and result = getPointerSize() + or + result = getTypeSize(type.(TupleType).getUnderlyingType()) + or + // TODO: Add room for extra field + result = getTypeSize(type.(NullableType).getUnderlyingType()) + or + type instanceof VoidType and result = 0 +} + +int getPointerSize() { result = 8 } + +/** + * Holds if an `IRErrorType` should exist. + */ +predicate hasErrorType() { exists(UnknownType t) } + +/** + * Holds if an `IRBooleanType` with the specified `byteSize` should exist. + */ +predicate hasBooleanType(int byteSize) { byteSize = getTypeSize(any(BoolType type)) } + +private predicate isSignedIntegerType(ValueType type) { + type instanceof SignedIntegralType or + type.(Enum).getUnderlyingType() instanceof SignedIntegralType +} + +private predicate isUnsignedIntegerType(ValueType type) { + type instanceof UnsignedIntegralType or + type instanceof CharType or + type.(Enum).getUnderlyingType() instanceof UnsignedIntegralType +} + +/** + * Holds if an `IRSignedIntegerType` with the specified `byteSize` should exist. + */ +predicate hasSignedIntegerType(int byteSize) { + byteSize = getTypeSize(any(ValueType type | isSignedIntegerType(type))) +} + +/** + * Holds if an `IRUnsignedIntegerType` with the specified `byteSize` should exist. + */ +predicate hasUnsignedIntegerType(int byteSize) { + byteSize = getTypeSize(any(ValueType type | isUnsignedIntegerType(type))) +} + +/** + * Holds if an `IRFloatingPointType` with the specified `byteSize` should exist. + */ +predicate hasFloatingPointType(int byteSize) { byteSize = any(FloatingPointType type).getSize() } + +private predicate isPointerIshType(Type type) { + type instanceof PointerType or + type instanceof RefType +} + +/** + * Holds if an `IRAddressType` with the specified `byteSize` should exist. + */ +predicate hasAddressType(int byteSize) { + // This covers all pointers, all references, and because it also looks at `NullType`, it + // should always return a result that makes sense for arbitrary glvalues as well. + byteSize = getTypeSize(any(Type type | isPointerIshType(type))) +} + +/** + * Holds if an `IRFunctionAddressType` with the specified `byteSize` should exist. + */ +predicate hasFunctionAddressType(int byteSize) { byteSize = getTypeSize(any(NullType type)) } + +private int getBaseClassSize(ValueOrRefType type) { + if exists(type.getBaseClass()) then result = getContentSize(type.getBaseClass()) else result = 0 +} + +private int getContentSize(ValueOrRefType type) { + result = getBaseClassSize(type) + + sum(Field field | not field.isStatic() | getTypeSize(field.getType())) +} + +private predicate isOpaqueType(ValueOrRefType type) { + type instanceof Struct or + type instanceof NullableType or + type instanceof DecimalType +} + +/** + * Holds if an `IROpaqueType` with the specified `tag` and `byteSize` should exist. + */ +predicate hasOpaqueType(Type tag, int byteSize) { + isOpaqueType(tag) and byteSize = getTypeSize(tag) +} + +private Type getRepresentationType(Type type) { + result = type.(Enum).getUnderlyingType() + or + result = type.(TupleType).getUnderlyingType() + or + not type instanceof Enum and not type instanceof TupleType and result = type +} + +/** + * Gets the `IRType` that represents a prvalue of the specified `Type`. + */ +private IRType getIRTypeForPRValue(Type type) { + exists(Type repType | repType = getRepresentationType(type) | + exists(IROpaqueType opaqueType | opaqueType = result | + opaqueType.getByteSize() = getTypeSize(repType) and + opaqueType.getTag() = repType + ) + or + result.(IRBooleanType).getByteSize() = repType.(BoolType).getSize() + or + isSignedIntegerType(repType) and + result.(IRSignedIntegerType).getByteSize() = getTypeSize(repType) + or + isUnsignedIntegerType(repType) and + result.(IRUnsignedIntegerType).getByteSize() = getTypeSize(repType) + or + result.(IRFloatingPointType).getByteSize() = repType.(FloatingPointType).getSize() + or + isPointerIshType(repType) and result.(IRAddressType).getByteSize() = getTypeSize(repType) + or + repType instanceof VoidType and result instanceof IRVoidType + or + repType instanceof UnknownType and result instanceof IRErrorType + ) +} + +string getOpaqueTagIdentityString(Type tag) { result = tag.getQualifiedName() } + +private newtype TCSharpType = + TPRValueType(Type type) { exists(getIRTypeForPRValue(type)) } or + TGLValueAddressType(Type type) { any() } or + TFunctionAddressType() or + TUnknownType() + +class CSharpType extends TCSharpType { + abstract string toString(); + + /** Gets a string used in IR dumps */ + string getDumpString() { result = toString() } + + /** Gets the size of the type in bytes, if known. */ + final int getByteSize() { result = getIRType().getByteSize() } + + /** + * Gets the `IRType` that represents this `CSharpType`. Many different `CSharpType`s can map to a + * single `IRType`. + */ + abstract IRType getIRType(); + + /** + * Holds if the `CSharpType` represents a prvalue of type `Type` (if `isGLValue` is `false`), or + * if it represents a glvalue of type `Type` (if `isGLValue` is `true`). + */ + abstract predicate hasType(Type type, boolean isGLValue); + + final predicate hasUnspecifiedType(Type type, boolean isGLValue) { hasType(type, isGLValue) } +} + +/** + * A `CSharpType` that wraps an existing `Type` (either as a prvalue or a glvalue). + */ +private class CSharpWrappedType extends CSharpType { + Type cstype; + + CSharpWrappedType() { + this = TPRValueType(cstype) or + this = TGLValueAddressType(cstype) + } + + abstract override string toString(); + + abstract override IRType getIRType(); + + abstract override predicate hasType(Type type, boolean isGLValue); +} + +/** + * A `CSharpType` that represents a prvalue of an existing `Type`. + */ +private class CSharpPRValueType extends CSharpWrappedType, TPRValueType { + final override string toString() { result = cstype.toString() } + + final override IRType getIRType() { result = getIRTypeForPRValue(cstype) } + + final override predicate hasType(Type type, boolean isGLValue) { + type = cstype and + isGLValue = false + } +} + +/** + * A `CSharpType` that represents a glvalue of an existing `Type`. + */ +private class CSharpGLValueAddressType extends CSharpWrappedType, TGLValueAddressType { + final override string toString() { result = "glval<" + cstype.toString() + ">" } + + final override IRAddressType getIRType() { result.getByteSize() = getPointerSize() } + + final override predicate hasType(Type type, boolean isGLValue) { + type = cstype and + isGLValue = true + } +} + +/** + * A `CSharpType` that represents a function address. + */ +private class CSharpFunctionAddressType extends CSharpType, TFunctionAddressType { + final override string toString() { result = "" } + + final override IRFunctionAddressType getIRType() { result.getByteSize() = getPointerSize() } + + final override predicate hasType(Type type, boolean isGLValue) { + type instanceof VoidType and isGLValue = true + } +} + +/** + * A `CSharpType` that represents an unknown type. + */ +private class CSharpUnknownType extends CSharpType, TUnknownType { + final override string toString() { result = "" } + + final override IRUnknownType getIRType() { any() } + + final override predicate hasType(Type type, boolean isGLValue) { + type instanceof VoidType and isGLValue = false + } +} + +/** + * Gets the single instance of `CSharpUnknownType`. + */ +CSharpUnknownType getUnknownType() { any() } + +/** + * Gets the `CSharpType` that represents a prvalue of type `void`. + */ +CSharpPRValueType getVoidType() { exists(VoidType voidType | result.hasType(voidType, false)) } + +/** + * Gets the `CSharpType` that represents a prvalue of type `type`. + */ +CSharpPRValueType getTypeForPRValue(Type type) { result.hasType(type, false) } + +/** + * Gets the `CSharpType` that represents a glvalue of type `type`. + */ +CSharpGLValueAddressType getTypeForGLValue(Type type) { result.hasType(type, true) } + +/** + * Gets the `CSharpType` that represents a prvalue of type `int`. + */ +CSharpPRValueType getIntType() { result.hasType(any(IntType t), false) } + +/** + * Gets the `CSharpType` that represents a prvalue of type `bool`. + */ +CSharpPRValueType getBoolType() { result.hasType(any(BoolType t), false) } + +/** + * Gets the `CSharpType` that represents a prvalue of `NullType`. + */ +CSharpPRValueType getNullType() { result.hasType(any(NullType t), false) } + +/** + * Gets the `CSharpType` that represents a function address. + */ +CSharpFunctionAddressType getFunctionAddressType() { any() } + +/** + * Gets the `CSharpType` that is the canonical type for an `IRBooleanType` with the specified + * `byteSize`. + */ +CSharpPRValueType getCanonicalBooleanType(int byteSize) { + exists(BoolType type | result = TPRValueType(type) and byteSize = type.getSize()) +} + +/** + * Gets the `CSharpType` that is the canonical type for an `IRSignedIntegerType` with the specified + * `byteSize`. + */ +CSharpPRValueType getCanonicalSignedIntegerType(int byteSize) { + result = TPRValueType(any(SignedIntegralType t | t.getSize() = byteSize)) +} + +/** + * Gets the `CSharpType` that is the canonical type for an `IRUnsignedIntegerType` with the specified + * `byteSize`. + */ +CSharpPRValueType getCanonicalUnsignedIntegerType(int byteSize) { + result = TPRValueType(any(UnsignedIntegralType t | t.getSize() = byteSize)) +} + +/** + * Gets the `CSharpType` that is the canonical type for an `IRFloatingPointType` with the specified + * `byteSize`. + */ +CSharpPRValueType getCanonicalFloatingPointType(int byteSize) { + result = TPRValueType(any(FloatingPointType type | type.getSize() = byteSize)) +} + +/** + * Gets the `CSharpType` that is the canonical type for an `IRAddressType` with the specified + * `byteSize`. + */ +CSharpPRValueType getCanonicalAddressType(int byteSize) { + // We just use `NullType`, since it should be unique. + result = TPRValueType(any(NullType type | getTypeSize(type) = byteSize)) +} + +/** + * Gets the `CSharpType` that is the canonical type for an `IRFunctionAddressType` with the specified + * `byteSize`. + */ +CSharpFunctionAddressType getCanonicalFunctionAddressType(int byteSize) { + result.getByteSize() = byteSize +} + +/** + * Gets the `CSharpType` that is the canonical type for `IRErrorType`. + */ +CSharpPRValueType getCanonicalErrorType() { result = TPRValueType(any(UnknownType type)) } + +/** + * Gets the `CSharpType` that is the canonical type for `IRUnknownType`. + */ +CSharpUnknownType getCanonicalUnknownType() { any() } + +/** + * Gets the `CSharpType` that is the canonical type for `IRVoidType`. + */ +CSharpPRValueType getCanonicalVoidType() { result = TPRValueType(any(VoidType type)) } + +/** + * Gets the `CSharpType` that is the canonical type for an `IROpaqueType` with the specified `tag` and + * `byteSize`. + */ +CSharpPRValueType getCanonicalOpaqueType(Type tag, int byteSize) { + isOpaqueType(tag) and + result = TPRValueType(tag) and + getTypeSize(tag) = byteSize +} + +module LanguageTypeSanity { + // Nothing interesting here for C# yet, but the module still has to exist because it is imported + // by `IRTypeSanity`. +} diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IRCSharpLanguage.qll b/csharp/ql/src/semmle/code/csharp/ir/internal/IRCSharpLanguage.qll index c359879c87f..70f3e38ade7 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/internal/IRCSharpLanguage.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/internal/IRCSharpLanguage.qll @@ -1,5 +1,10 @@ private import csharp as CSharp private import IRUtilities +import CSharpType + +class LanguageType = CSharpType; + +class OpaqueTypeTag = CSharp::ValueOrRefType; class Function = CSharp::Callable; @@ -81,32 +86,6 @@ predicate hasPositionalArgIndex(int argIndex) { predicate hasAsmOperandIndex(int operandIndex) { none() } -int getTypeSize(Type type) { - // REVIEW: Is this complete? - result = type.(CSharp::SimpleType).getSize() - or - result = getTypeSize(type.(CSharp::Enum).getUnderlyingType()) - or - // TODO: Generate a reasonable size - type instanceof CSharp::Struct and result = 16 - or - type instanceof CSharp::RefType and result = getPointerSize() - or - type instanceof CSharp::PointerType and result = getPointerSize() - or - result = getTypeSize(type.(CSharp::TupleType).getUnderlyingType()) - or - // TODO: Add room for extra field - result = getTypeSize(type.(CSharp::NullableType).getUnderlyingType()) - or - type instanceof CSharp::VoidType and result = 0 -} - -int getPointerSize() { - // TODO: Deal with sizes in general - result = 8 -} - predicate isVariableAutomatic(Variable var) { var instanceof CSharp::LocalScopeVariable } string getStringLiteralText(StringLiteral s) { diff --git a/csharp/ql/src/semmle/code/csharp/ir/internal/IRUtilities.qll b/csharp/ql/src/semmle/code/csharp/ir/internal/IRUtilities.qll index c14e361aa01..1aeace91377 100644 --- a/csharp/ql/src/semmle/code/csharp/ir/internal/IRUtilities.qll +++ b/csharp/ql/src/semmle/code/csharp/ir/internal/IRUtilities.qll @@ -1,25 +1,13 @@ private import csharp /** - * Get the actual type of the specified variable, as opposed to the declared type. - * This returns the type of the variable after any pointer decay is applied, and - * after any unsized array type has its size inferred from the initializer. + * Get the actual type of the specified variable, as opposed to the declared + * type. */ Type getVariableType(Variable v) { - exists(Type declaredType | - declaredType = v.getType() and - if v instanceof Parameter - then result = declaredType - else - if declaredType instanceof ArrayType - then - // TODO: Arrays have a declared dimension in C#, so this should not be needed - // and not declaredType.(ArrayType).hasArraySize() - result = v.getInitializer().getType() - or - not exists(v.getInitializer()) and result = declaredType - else result = declaredType - ) + // C# doesn't seem to have any cases where the variable's actual type differs + // from its declared type. + result = v.getType() } predicate hasCaseEdge(CaseStmt caseStmt, string minValue, string maxValue) { diff --git a/csharp/ql/src/semmle/code/csharp/security/cryptography/EncryptionKeyDataFlow.qll b/csharp/ql/src/semmle/code/csharp/security/cryptography/EncryptionKeyDataFlow.qll new file mode 100644 index 00000000000..7bde645b8f8 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/security/cryptography/EncryptionKeyDataFlow.qll @@ -0,0 +1,83 @@ +/** + * This module has classes and data flow configuration for working with symmetric encryption keys data flow. + */ + +import csharp + +module EncryptionKeyDataFlow { + private import semmle.code.csharp.frameworks.system.security.cryptography.SymmetricAlgorithm + + /** Array of type Byte */ + class ByteArray extends ArrayType { + ByteArray() { getElementType() instanceof ByteType } + } + + /** Abstract class for all sources of keys */ + abstract class KeySource extends DataFlow::Node { } + + /** + * A symmetric encryption sink is abstract base class for all ways to set a key for symmetric encryption. + */ + abstract class SymmetricEncryptionKeySink extends DataFlow::Node { + /** override to create a meaningful description of the sink */ + abstract string getDescription(); + } + + /** + * A sanitizer for symmetric encryption key. If present, for example, key is properly constructed or retrieved from secret storage. + */ + abstract class KeySanitizer extends DataFlow::ExprNode { } + + /** + * Symmetric Algorithm, 'Key' property assigned a value + */ + class SymmetricEncryptionKeyPropertySink extends SymmetricEncryptionKeySink { + SymmetricEncryptionKeyPropertySink() { + exists(SymmetricAlgorithm ag | asExpr() = ag.getKeyProperty().getAnAssignedValue()) + } + + override string getDescription() { result = "Key property assignment" } + } + + /** + * Symmetric Algorithm, CreateEncryptor method, rgbKey parameter + */ + class SymmetricEncryptionCreateEncryptorSink extends SymmetricEncryptionKeySink { + SymmetricEncryptionCreateEncryptorSink() { + exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricEncryptor() | + asExpr() = mc.getArgumentForName("rgbKey") + ) + } + + override string getDescription() { result = "Encryptor(rgbKey, IV)" } + } + + /** + * Symmetric Algorithm, CreateDecryptor method, rgbKey parameter + */ + class SymmetricEncryptionCreateDecryptorSink extends SymmetricEncryptionKeySink { + SymmetricEncryptionCreateDecryptorSink() { + exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricDecryptor() | + asExpr() = mc.getArgumentForName("rgbKey") + ) + } + + override string getDescription() { result = "Decryptor(rgbKey, IV)" } + } + + /** + * Symmetric Key Data Flow configuration. + */ + class SymmetricKeyTaintTrackingConfiguration extends TaintTracking::Configuration { + SymmetricKeyTaintTrackingConfiguration() { this = "SymmetricKeyTaintTracking" } + + /** Holds if the node is a key source. */ + override predicate isSource(DataFlow::Node src) { src instanceof KeySource } + + /** Holds if the node is a symmetric encryption key sink. */ + override predicate isSink(DataFlow::Node sink) { sink instanceof SymmetricEncryptionKeySink } + + /** Holds if the node is a key sanitizer. */ + override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof KeySanitizer } + } +} diff --git a/csharp/ql/src/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll b/csharp/ql/src/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll new file mode 100644 index 00000000000..b1ecb404477 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll @@ -0,0 +1,112 @@ +/** + * Provides a taint-tracking configuration for reasoning about hard-coded + * symmetric encryption keys. + */ + +import csharp + +module HardcodedSymmetricEncryptionKey { + private import semmle.code.csharp.frameworks.system.security.cryptography.SymmetricAlgorithm + + /** A data flow source for hard-coded symmetric encryption keys. */ + abstract class Source extends DataFlow::Node { } + + /** A data flow sink for hard-coded symmetric encryption keys. */ + abstract class Sink extends DataFlow::ExprNode { + /** Gets a description of this sink. */ + abstract string getDescription(); + } + + /** A sanitizer for hard-coded symmetric encryption keys. */ + abstract class Sanitizer extends DataFlow::ExprNode { } + + private class ByteArrayType extends ArrayType { + ByteArrayType() { getElementType() instanceof ByteType } + } + + private class ByteArrayLiteralSource extends Source { + ByteArrayLiteralSource() { + this.asExpr() = any(ArrayCreation ac | + ac.getArrayType() instanceof ByteArrayType and + ac.hasInitializer() + ) + } + } + + private class StringLiteralSource extends Source { + StringLiteralSource() { this.asExpr() instanceof StringLiteral } + } + + private class SymmetricEncryptionKeyPropertySink extends Sink { + SymmetricEncryptionKeyPropertySink() { + this.asExpr() = any(SymmetricAlgorithm sa).getKeyProperty().getAnAssignedValue() + } + + override string getDescription() { result = "'Key' property assignment" } + } + + private class SymmetricEncryptionCreateEncryptorSink extends Sink { + SymmetricEncryptionCreateEncryptorSink() { + exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricEncryptor() | + asExpr() = mc.getArgumentForName("rgbKey") + ) + } + + override string getDescription() { result = "Encryptor(rgbKey, IV)" } + } + + private class SymmetricEncryptionCreateDecryptorSink extends Sink { + SymmetricEncryptionCreateDecryptorSink() { + exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricDecryptor() | + asExpr() = mc.getArgumentForName("rgbKey") + ) + } + + override string getDescription() { result = "Decryptor(rgbKey, IV)" } + } + + private class CreateSymmetricKeySink extends Sink { + CreateSymmetricKeySink() { + exists(MethodCall mc, Method m | + mc.getTarget() = m and + m + .hasQualifiedName("Windows.Security.Cryptography.Core.SymmetricKeyAlgorithmProvider", + "CreateSymmetricKey") and + this.asExpr() = mc.getArgumentForName("keyMaterial") + ) + } + + override string getDescription() { result = "CreateSymmetricKey(IBuffer keyMaterial)" } + } + + private class CryptographicBuffer extends Class { + CryptographicBuffer() { + this.hasQualifiedName("Windows.Security.Cryptography", "CryptographicBuffer") + } + } + + /** + * A taint-tracking configuration for uncontrolled data in path expression vulnerabilities. + */ + class TaintTrackingConfiguration extends TaintTracking::Configuration { + TaintTrackingConfiguration() { this = "HardcodedSymmetricEncryptionKey" } + + override predicate isSource(DataFlow::Node source) { source instanceof Source } + + override predicate isSink(DataFlow::Node sink) { sink instanceof Sink } + + override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer } + + /** + * Since `CryptographicBuffer` uses native code inside, taint tracking doesn't pass through it. + * Need to create an additional custom step. + */ + override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) { + exists(MethodCall mc, CryptographicBuffer c | + pred.asExpr() = mc.getAnArgument() and + mc.getTarget() = c.getAMethod() and + succ.asExpr() = mc + ) + } + } +} diff --git a/csharp/ql/src/semmle/code/csharp/serialization/Deserializers.qll b/csharp/ql/src/semmle/code/csharp/serialization/Deserializers.qll new file mode 100644 index 00000000000..685a48b7079 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/serialization/Deserializers.qll @@ -0,0 +1,65 @@ +/** + * Provides a library of known unsafe deserializers. + * See https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-Json-Attacks.pdf. + */ + +import csharp + +/** An unsafe deserializer. */ +abstract class UnsafeDeserializer extends Callable { } + +class SystemDeserializer extends UnsafeDeserializer { + SystemDeserializer() { + this + .hasQualifiedName("System.Runtime.Serialization.Formatters.Binary.BinaryFormatter", + "Deserialize") + or + this + .hasQualifiedName("System.Runtime.Serialization.Formatters.Binary.BinaryFormatter", + "UnsafeDeserialize") + or + this + .hasQualifiedName("System.Runtime.Serialization.Formatters.Binary.BinaryFormatter", + "UnsafeDeserializeMethodResponse") + or + this + .hasQualifiedName("System.Runtime.Deserialization.Formatters.Soap.SoapFormatter", + "Deserialize") + or + this.hasQualifiedName("System.Web.UI.ObjectStateFormatter", "Deserialize") + or + this.hasQualifiedName("System.Runtime.Serialization.NetDataContractSerializer", "Deserialize") + or + this.hasQualifiedName("System.Runtime.Serialization.NetDataContractSerializer", "ReadObject") + or + this.hasQualifiedName("System.Web.UI.LosFormatter", "Deserialize") + or + this.hasQualifiedName("System.Workflow.ComponentModel.Activity", "Load") + or + this.hasQualifiedName("System.Resources.ResourceReader", "ResourceReader") + or + this.hasQualifiedName("System.Messaging", "BinaryMessageFormatter") + or + this.hasQualifiedName("System.Windows.Markup.XamlReader", "Parse") + or + this.hasQualifiedName("System.Windows.Markup.XamlReader", "Load") + or + this.hasQualifiedName("System.Windows.Markup.XamlReader", "LoadAsync") + } +} + +class MicrosoftDeserializer extends UnsafeDeserializer { + MicrosoftDeserializer() { + this.hasQualifiedName("Microsoft.Web.Design.Remote.ProxyObject", "DecodeValue") + } +} + +class WrapperDeserializer extends UnsafeDeserializer { + WrapperDeserializer() { + exists(Call call | + call.getEnclosingCallable() = this and + call.getAnArgument() instanceof ParameterAccess and + call.getTarget() instanceof UnsafeDeserializer + ) + } +} diff --git a/csharp/ql/src/semmle/code/csharp/serialization/Serialization.qll b/csharp/ql/src/semmle/code/csharp/serialization/Serialization.qll new file mode 100644 index 00000000000..704d10065f0 --- /dev/null +++ b/csharp/ql/src/semmle/code/csharp/serialization/Serialization.qll @@ -0,0 +1,105 @@ +import csharp + +class SerializationConstructor extends Constructor { + SerializationConstructor() { + this.getNumberOfParameters() = 2 and + this.getParameter(0).getType().hasName("SerializationInfo") and + this.getParameter(1).getType().hasName("StreamingContext") + } +} + +/** + * A serializable type, using any of the built-in .NET serialization mechanisms. + */ +abstract class SerializableType extends ValueOrRefType { + /** + * A method or constructor that should always be considered as potentially + * called on a deserialized object. The precise details depend on the + * deserialization mechanism. + */ + abstract Callable getADeserializationCallback(); + + /** + * A field whose value is restored during a deserialization, rendering it + * potentially untrusted. + */ + abstract Field getASerializedField(); + + /** + * Get a callback that is automatically executed (without user code + * interaction) when an object instance is deserialized. This includes + * deserialization callbacks as well as cleanup/dispose calls. + */ + Callable getAnAutomaticCallback() { + result = this.getADeserializationCallback() or + result.(Destructor).getDeclaringType() = this or + result = any(Method m | + m.getDeclaringType() = this and + m.hasName("Dispose") and + ( + m.getNumberOfParameters() = 0 + or + m.getNumberOfParameters() = 1 and m.getParameter(0).getType() instanceof BoolType + ) + ) + } +} + +/** + * To be serializable by the `BinaryFormatter`, a class must have the `Serializable` + * attribute. + */ +class BinarySerializableType extends SerializableType { + BinarySerializableType() { this.getAnAttribute().getType().hasName("SerializableAttribute") } + + /** + * In addition to the defaults, a `BinarySerializer` will call any method annotated + * with an `OnDeserialized` or `OnDeserializing` attribute, as well as an + * `OnDeserialization` method. + */ + override Callable getADeserializationCallback() { + result.(SerializationConstructor).getDeclaringType() = this + or + result = this.getAMethod() and + ( + result.(Attributable).getAnAttribute().getType().hasName("OnDeserializedAttribute") or + result.(Attributable).getAnAttribute().getType().hasName("OnDeserializingAttribute") or + result.hasName("OnDeserialization") + ) + } + + override Field getASerializedField() { + result.getDeclaringType() = this and + not result.getAnAttribute().getType().hasName("NonSerializedAttribute") and + not result.isStatic() + } +} + +/** + * If a class annotated with the `Serializable` attribute also implements `ISerializable`, + * then it is serialized and deserialized in a special way. + */ +class CustomBinarySerializableType extends BinarySerializableType { + CustomBinarySerializableType() { this.getABaseType*().hasName("ISerializable") } + + /** + * For custom deserialization, the `BinarySerializer` will call the serialization constructor. + */ + override Callable getADeserializationCallback() { + result = super.getADeserializationCallback() or + result.(SerializationConstructor).getDeclaringType() = this + } +} + +class DangerousCallable extends Callable { + DangerousCallable() { + //files + this.(Method).getQualifiedName().matches("System.IO.File.Write%") or + this.(Method).getQualifiedName().matches("System.IO.File.%Copy%") or + this.(Method).getQualifiedName().matches("System.IO.File.%Move%") or + this.(Method).getQualifiedName().matches("System.IO.File.%Append%") or + this.(Method).getQualifiedName().matches("System.IO.%.%Delete%") or + //assembly + this.(Method).getQualifiedName().matches("System.Reflection.Assembly.%Load%") + } +} diff --git a/csharp/ql/test/library-tests/cil/dataflow/DataFlow.expected b/csharp/ql/test/library-tests/cil/dataflow/DataFlow.expected index 65e04f8d48c..d82ff134b35 100644 --- a/csharp/ql/test/library-tests/cil/dataflow/DataFlow.expected +++ b/csharp/ql/test/library-tests/cil/dataflow/DataFlow.expected @@ -12,6 +12,8 @@ | dataflow.cs:46:35:46:39 | "t1b" | dataflow.cs:46:18:46:40 | call to method Taint1 | | dataflow.cs:49:35:49:38 | "t6" | dataflow.cs:49:18:49:45 | call to method TaintIndirect | | dataflow.cs:49:41:49:44 | "t6" | dataflow.cs:49:18:49:45 | call to method TaintIndirect | +| dataflow.cs:102:30:102:33 | null | dataflow.cs:74:21:74:52 | ... ?? ... | | dataflow.cs:102:30:102:33 | null | dataflow.cs:89:24:89:51 | ... ? ... : ... | | dataflow.cs:102:30:102:33 | null | dataflow.cs:108:20:108:33 | call to method IndirectNull | +| dataflow.cs:109:23:109:26 | null | dataflow.cs:74:21:74:52 | ... ?? ... | | dataflow.cs:109:23:109:26 | null | dataflow.cs:89:24:89:51 | ... ? ... : ... | diff --git a/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected b/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected index 63462bbad86..78c0204ed20 100644 --- a/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/local/DataFlow.expected @@ -8,7 +8,9 @@ | LocalDataFlow.cs:412:15:412:20 | access to local variable sink70 | | LocalDataFlow.cs:420:19:420:24 | access to local variable sink71 | | LocalDataFlow.cs:430:23:430:28 | access to local variable sink72 | -| LocalDataFlow.cs:466:15:466:21 | access to parameter tainted | +| LocalDataFlow.cs:445:15:445:20 | access to local variable sink73 | +| LocalDataFlow.cs:446:15:446:20 | access to local variable sink74 | +| LocalDataFlow.cs:472:15:472:21 | access to parameter tainted | | SSA.cs:9:15:9:22 | access to local variable ssaSink0 | | SSA.cs:25:15:25:22 | access to local variable ssaSink1 | | SSA.cs:43:15:43:22 | access to local variable ssaSink2 | diff --git a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected index d38e1f02215..1b93aded791 100644 --- a/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected +++ b/csharp/ql/test/library-tests/dataflow/local/DataFlowStep.expected @@ -545,7 +545,10 @@ | LocalDataFlow.cs:408:15:408:22 | access to local variable nonSink0 | LocalDataFlow.cs:415:31:415:38 | access to local variable nonSink0 | | LocalDataFlow.cs:411:13:411:34 | SSA def(sink70) | LocalDataFlow.cs:412:15:412:20 | access to local variable sink70 | | LocalDataFlow.cs:411:22:411:34 | ... = ... | LocalDataFlow.cs:411:13:411:34 | SSA def(sink70) | +| LocalDataFlow.cs:411:22:411:34 | SSA def(sink0) | LocalDataFlow.cs:443:34:443:38 | access to local variable sink0 | +| LocalDataFlow.cs:411:22:411:34 | SSA def(sink0) | LocalDataFlow.cs:444:22:444:26 | access to local variable sink0 | | LocalDataFlow.cs:411:30:411:34 | access to local variable sink0 | LocalDataFlow.cs:411:22:411:34 | ... = ... | +| LocalDataFlow.cs:411:30:411:34 | access to local variable sink0 | LocalDataFlow.cs:411:22:411:34 | SSA def(sink0) | | LocalDataFlow.cs:412:15:412:20 | [post] access to local variable sink70 | LocalDataFlow.cs:419:13:419:18 | access to local variable sink70 | | LocalDataFlow.cs:412:15:412:20 | access to local variable sink70 | LocalDataFlow.cs:419:13:419:18 | access to local variable sink70 | | LocalDataFlow.cs:415:9:415:38 | SSA def(nonSink0) | LocalDataFlow.cs:416:15:416:22 | access to local variable nonSink0 | @@ -562,12 +565,23 @@ | LocalDataFlow.cs:427:17:427:22 | access to local variable sink70 | LocalDataFlow.cs:429:18:429:30 | SSA def(sink72) | | LocalDataFlow.cs:429:18:429:30 | SSA def(sink72) | LocalDataFlow.cs:430:23:430:28 | access to local variable sink72 | | LocalDataFlow.cs:435:17:435:24 | access to local variable nonSink0 | LocalDataFlow.cs:437:18:437:33 | SSA def(nonSink17) | +| LocalDataFlow.cs:435:17:435:24 | access to local variable nonSink0 | LocalDataFlow.cs:443:22:443:29 | access to local variable nonSink0 | | LocalDataFlow.cs:437:18:437:33 | SSA def(nonSink17) | LocalDataFlow.cs:438:23:438:31 | access to local variable nonSink17 | -| LocalDataFlow.cs:458:28:458:30 | this | LocalDataFlow.cs:458:41:458:45 | this access | -| LocalDataFlow.cs:458:50:458:52 | this | LocalDataFlow.cs:458:56:458:60 | this access | -| LocalDataFlow.cs:458:50:458:52 | value | LocalDataFlow.cs:458:64:458:68 | access to parameter value | -| LocalDataFlow.cs:464:41:464:47 | tainted | LocalDataFlow.cs:466:15:466:21 | access to parameter tainted | -| LocalDataFlow.cs:469:44:469:53 | nonTainted | LocalDataFlow.cs:471:15:471:24 | access to parameter nonTainted | +| LocalDataFlow.cs:443:13:443:38 | SSA def(sink73) | LocalDataFlow.cs:445:15:445:20 | access to local variable sink73 | +| LocalDataFlow.cs:443:22:443:29 | access to local variable nonSink0 | LocalDataFlow.cs:443:22:443:38 | ... ?? ... | +| LocalDataFlow.cs:443:22:443:29 | access to local variable nonSink0 | LocalDataFlow.cs:444:31:444:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:443:22:443:38 | ... ?? ... | LocalDataFlow.cs:443:13:443:38 | SSA def(sink73) | +| LocalDataFlow.cs:443:34:443:38 | access to local variable sink0 | LocalDataFlow.cs:443:22:443:38 | ... ?? ... | +| LocalDataFlow.cs:443:34:443:38 | access to local variable sink0 | LocalDataFlow.cs:444:22:444:26 | access to local variable sink0 | +| LocalDataFlow.cs:444:13:444:38 | SSA def(sink74) | LocalDataFlow.cs:446:15:446:20 | access to local variable sink74 | +| LocalDataFlow.cs:444:22:444:26 | access to local variable sink0 | LocalDataFlow.cs:444:22:444:38 | ... ?? ... | +| LocalDataFlow.cs:444:22:444:38 | ... ?? ... | LocalDataFlow.cs:444:13:444:38 | SSA def(sink74) | +| LocalDataFlow.cs:444:31:444:38 | access to local variable nonSink0 | LocalDataFlow.cs:444:22:444:38 | ... ?? ... | +| LocalDataFlow.cs:464:28:464:30 | this | LocalDataFlow.cs:464:41:464:45 | this access | +| LocalDataFlow.cs:464:50:464:52 | this | LocalDataFlow.cs:464:56:464:60 | this access | +| LocalDataFlow.cs:464:50:464:52 | value | LocalDataFlow.cs:464:64:464:68 | access to parameter value | +| LocalDataFlow.cs:470:41:470:47 | tainted | LocalDataFlow.cs:472:15:472:21 | access to parameter tainted | +| LocalDataFlow.cs:475:44:475:53 | nonTainted | LocalDataFlow.cs:477:15:477:24 | access to parameter nonTainted | | SSA.cs:5:17:5:17 | SSA entry def(this.S) | SSA.cs:67:9:67:14 | access to field S | | SSA.cs:5:17:5:17 | this | SSA.cs:67:9:67:12 | this access | | SSA.cs:5:26:5:32 | tainted | SSA.cs:8:24:8:30 | access to parameter tainted | diff --git a/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs b/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs index 154b4d5fcb9..65de9fc65fe 100644 --- a/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs +++ b/csharp/ql/test/library-tests/dataflow/local/LocalDataFlow.cs @@ -438,6 +438,12 @@ public class LocalDataFlow Check(nonSink17); break; } + + // Null-coalescing expressions + var sink73 = nonSink0 ?? sink0; + var sink74 = sink0 ?? nonSink0; + Check(sink73); + Check(sink74); } static void Check(T x) { } diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected b/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected index ba3d91130f2..756d79333c1 100644 --- a/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected +++ b/csharp/ql/test/library-tests/dataflow/local/TaintTracking.expected @@ -62,7 +62,9 @@ | LocalDataFlow.cs:412:15:412:20 | access to local variable sink70 | | LocalDataFlow.cs:420:19:420:24 | access to local variable sink71 | | LocalDataFlow.cs:430:23:430:28 | access to local variable sink72 | -| LocalDataFlow.cs:466:15:466:21 | access to parameter tainted | +| LocalDataFlow.cs:445:15:445:20 | access to local variable sink73 | +| LocalDataFlow.cs:446:15:446:20 | access to local variable sink74 | +| LocalDataFlow.cs:472:15:472:21 | access to parameter tainted | | SSA.cs:9:15:9:22 | access to local variable ssaSink0 | | SSA.cs:25:15:25:22 | access to local variable ssaSink1 | | SSA.cs:43:15:43:22 | access to local variable ssaSink2 | diff --git a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected index f9511969108..f320c87cb8b 100644 --- a/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected +++ b/csharp/ql/test/library-tests/dataflow/local/TaintTrackingStep.expected @@ -690,7 +690,10 @@ | LocalDataFlow.cs:408:15:408:22 | access to local variable nonSink0 | LocalDataFlow.cs:415:31:415:38 | access to local variable nonSink0 | | LocalDataFlow.cs:411:13:411:34 | SSA def(sink70) | LocalDataFlow.cs:412:15:412:20 | access to local variable sink70 | | LocalDataFlow.cs:411:22:411:34 | ... = ... | LocalDataFlow.cs:411:13:411:34 | SSA def(sink70) | +| LocalDataFlow.cs:411:22:411:34 | SSA def(sink0) | LocalDataFlow.cs:443:34:443:38 | access to local variable sink0 | +| LocalDataFlow.cs:411:22:411:34 | SSA def(sink0) | LocalDataFlow.cs:444:22:444:26 | access to local variable sink0 | | LocalDataFlow.cs:411:30:411:34 | access to local variable sink0 | LocalDataFlow.cs:411:22:411:34 | ... = ... | +| LocalDataFlow.cs:411:30:411:34 | access to local variable sink0 | LocalDataFlow.cs:411:22:411:34 | SSA def(sink0) | | LocalDataFlow.cs:412:15:412:20 | [post] access to local variable sink70 | LocalDataFlow.cs:419:13:419:18 | access to local variable sink70 | | LocalDataFlow.cs:412:15:412:20 | access to local variable sink70 | LocalDataFlow.cs:419:13:419:18 | access to local variable sink70 | | LocalDataFlow.cs:415:9:415:38 | SSA def(nonSink0) | LocalDataFlow.cs:416:15:416:22 | access to local variable nonSink0 | @@ -707,15 +710,26 @@ | LocalDataFlow.cs:427:17:427:22 | access to local variable sink70 | LocalDataFlow.cs:429:18:429:30 | SSA def(sink72) | | LocalDataFlow.cs:429:18:429:30 | SSA def(sink72) | LocalDataFlow.cs:430:23:430:28 | access to local variable sink72 | | LocalDataFlow.cs:435:17:435:24 | access to local variable nonSink0 | LocalDataFlow.cs:437:18:437:33 | SSA def(nonSink17) | +| LocalDataFlow.cs:435:17:435:24 | access to local variable nonSink0 | LocalDataFlow.cs:443:22:443:29 | access to local variable nonSink0 | | LocalDataFlow.cs:437:18:437:33 | SSA def(nonSink17) | LocalDataFlow.cs:438:23:438:31 | access to local variable nonSink17 | -| LocalDataFlow.cs:458:28:458:30 | this | LocalDataFlow.cs:458:41:458:45 | this access | -| LocalDataFlow.cs:458:50:458:52 | this | LocalDataFlow.cs:458:56:458:60 | this access | -| LocalDataFlow.cs:458:50:458:52 | value | LocalDataFlow.cs:458:50:458:52 | value | -| LocalDataFlow.cs:458:50:458:52 | value | LocalDataFlow.cs:458:64:458:68 | access to parameter value | -| LocalDataFlow.cs:464:41:464:47 | tainted | LocalDataFlow.cs:464:41:464:47 | tainted | -| LocalDataFlow.cs:464:41:464:47 | tainted | LocalDataFlow.cs:466:15:466:21 | access to parameter tainted | -| LocalDataFlow.cs:469:44:469:53 | nonTainted | LocalDataFlow.cs:469:44:469:53 | nonTainted | -| LocalDataFlow.cs:469:44:469:53 | nonTainted | LocalDataFlow.cs:471:15:471:24 | access to parameter nonTainted | +| LocalDataFlow.cs:443:13:443:38 | SSA def(sink73) | LocalDataFlow.cs:445:15:445:20 | access to local variable sink73 | +| LocalDataFlow.cs:443:22:443:29 | access to local variable nonSink0 | LocalDataFlow.cs:443:22:443:38 | ... ?? ... | +| LocalDataFlow.cs:443:22:443:29 | access to local variable nonSink0 | LocalDataFlow.cs:444:31:444:38 | access to local variable nonSink0 | +| LocalDataFlow.cs:443:22:443:38 | ... ?? ... | LocalDataFlow.cs:443:13:443:38 | SSA def(sink73) | +| LocalDataFlow.cs:443:34:443:38 | access to local variable sink0 | LocalDataFlow.cs:443:22:443:38 | ... ?? ... | +| LocalDataFlow.cs:443:34:443:38 | access to local variable sink0 | LocalDataFlow.cs:444:22:444:26 | access to local variable sink0 | +| LocalDataFlow.cs:444:13:444:38 | SSA def(sink74) | LocalDataFlow.cs:446:15:446:20 | access to local variable sink74 | +| LocalDataFlow.cs:444:22:444:26 | access to local variable sink0 | LocalDataFlow.cs:444:22:444:38 | ... ?? ... | +| LocalDataFlow.cs:444:22:444:38 | ... ?? ... | LocalDataFlow.cs:444:13:444:38 | SSA def(sink74) | +| LocalDataFlow.cs:444:31:444:38 | access to local variable nonSink0 | LocalDataFlow.cs:444:22:444:38 | ... ?? ... | +| LocalDataFlow.cs:464:28:464:30 | this | LocalDataFlow.cs:464:41:464:45 | this access | +| LocalDataFlow.cs:464:50:464:52 | this | LocalDataFlow.cs:464:56:464:60 | this access | +| LocalDataFlow.cs:464:50:464:52 | value | LocalDataFlow.cs:464:50:464:52 | value | +| LocalDataFlow.cs:464:50:464:52 | value | LocalDataFlow.cs:464:64:464:68 | access to parameter value | +| LocalDataFlow.cs:470:41:470:47 | tainted | LocalDataFlow.cs:470:41:470:47 | tainted | +| LocalDataFlow.cs:470:41:470:47 | tainted | LocalDataFlow.cs:472:15:472:21 | access to parameter tainted | +| LocalDataFlow.cs:475:44:475:53 | nonTainted | LocalDataFlow.cs:475:44:475:53 | nonTainted | +| LocalDataFlow.cs:475:44:475:53 | nonTainted | LocalDataFlow.cs:477:15:477:24 | access to parameter nonTainted | | SSA.cs:5:17:5:17 | SSA entry def(this.S) | SSA.cs:67:9:67:14 | access to field S | | SSA.cs:5:17:5:17 | this | SSA.cs:67:9:67:12 | this access | | SSA.cs:5:26:5:32 | tainted | SSA.cs:5:26:5:32 | tainted | diff --git a/csharp/ql/test/library-tests/ir/ir/raw_ir.expected b/csharp/ql/test/library-tests/ir/ir/raw_ir.expected index acba7e50086..5e3701a4c5a 100644 --- a/csharp/ql/test/library-tests/ir/ir/raw_ir.expected +++ b/csharp/ql/test/library-tests/ir/ir/raw_ir.expected @@ -2,8 +2,8 @@ array.cs: # 2| System.Void ArrayTest.one_dim_init_acc() # 2| Block 0 # 2| v0_0(Void) = EnterFunction : -# 2| mu0_1(null) = AliasedDefinition : -# 2| mu0_2(null) = UnmodeledDefinition : +# 2| mu0_1() = AliasedDefinition : +# 2| mu0_2() = UnmodeledDefinition : # 2| r0_3(glval) = InitializeThis : # 4| r0_4(glval) = VariableAddress[one_dim] : # 4| mu0_5(Int32[]) = Uninitialized[one_dim] : &:r0_4 @@ -53,13 +53,14 @@ array.cs: # 10| mu0_49(Int32) = Store : &:r0_48, r0_43 # 2| v0_50(Void) = ReturnVoid : # 2| v0_51(Void) = UnmodeledUse : mu* -# 2| v0_52(Void) = ExitFunction : +# 2| v0_52(Void) = AliasedUse : ~mu0_2 +# 2| v0_53(Void) = ExitFunction : # 13| System.Void ArrayTest.twod_and_init_acc() # 13| Block 0 # 13| v0_0(Void) = EnterFunction : -# 13| mu0_1(null) = AliasedDefinition : -# 13| mu0_2(null) = UnmodeledDefinition : +# 13| mu0_1() = AliasedDefinition : +# 13| mu0_2() = UnmodeledDefinition : # 13| r0_3(glval) = InitializeThis : # 15| r0_4(glval) = VariableAddress[a] : # 15| mu0_5(Int32[,]) = Uninitialized[a] : &:r0_4 @@ -144,14 +145,15 @@ array.cs: # 20| mu0_84(Int32) = Store : &:r0_83, r0_76 # 13| v0_85(Void) = ReturnVoid : # 13| v0_86(Void) = UnmodeledUse : mu* -# 13| v0_87(Void) = ExitFunction : +# 13| v0_87(Void) = AliasedUse : ~mu0_2 +# 13| v0_88(Void) = ExitFunction : assignop.cs: # 4| System.Void AssignOp.Main() # 4| Block 0 # 4| v0_0(Void) = EnterFunction : -# 4| mu0_1(null) = AliasedDefinition : -# 4| mu0_2(null) = UnmodeledDefinition : +# 4| mu0_1() = AliasedDefinition : +# 4| mu0_2() = UnmodeledDefinition : # 5| r0_3(glval) = VariableAddress[a] : # 5| r0_4(Int32) = Constant[1] : # 5| mu0_5(Int32) = Store : &:r0_3, r0_4 @@ -215,19 +217,20 @@ assignop.cs: # 17| mu0_63(Int32) = Store : &:r0_60, r0_62 # 4| v0_64(Void) = ReturnVoid : # 4| v0_65(Void) = UnmodeledUse : mu* -# 4| v0_66(Void) = ExitFunction : +# 4| v0_66(Void) = AliasedUse : ~mu0_2 +# 4| v0_67(Void) = ExitFunction : casts.cs: # 11| System.Void Casts.Main() # 11| Block 0 # 11| v0_0(Void) = EnterFunction : -# 11| mu0_1(null) = AliasedDefinition : -# 11| mu0_2(null) = UnmodeledDefinition : +# 11| mu0_1() = AliasedDefinition : +# 11| mu0_2() = UnmodeledDefinition : # 13| r0_3(glval) = VariableAddress[Aobj] : # 13| r0_4(Casts_A) = NewObj : -# 13| r0_5(glval) = FunctionAddress[Casts_A] : +# 13| r0_5() = FunctionAddress[Casts_A] : # 13| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 13| mu0_7(null) = ^CallSideEffect : ~mu0_2 +# 13| mu0_7() = ^CallSideEffect : ~mu0_2 # 13| mu0_8(Casts_A) = Store : &:r0_3, r0_4 # 14| r0_9(glval) = VariableAddress[bobjCE] : # 14| r0_10(glval) = VariableAddress[Aobj] : @@ -241,25 +244,26 @@ casts.cs: # 15| mu0_18(Casts_B) = Store : &:r0_14, r0_17 # 11| v0_19(Void) = ReturnVoid : # 11| v0_20(Void) = UnmodeledUse : mu* -# 11| v0_21(Void) = ExitFunction : +# 11| v0_21(Void) = AliasedUse : ~mu0_2 +# 11| v0_22(Void) = ExitFunction : collections.cs: # 11| System.Void Collections.Main() # 11| Block 0 # 11| v0_0(Void) = EnterFunction : -# 11| mu0_1(null) = AliasedDefinition : -# 11| mu0_2(null) = UnmodeledDefinition : +# 11| mu0_1() = AliasedDefinition : +# 11| mu0_2() = UnmodeledDefinition : # 13| r0_3(glval>) = VariableAddress[dict] : # 13| r0_4(Dictionary) = NewObj : -# 13| r0_5(glval) = FunctionAddress[Dictionary] : +# 13| r0_5() = FunctionAddress[Dictionary] : # 13| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 13| mu0_7(null) = ^CallSideEffect : ~mu0_2 -# 15| r0_8(glval) = FunctionAddress[Add] : +# 13| mu0_7() = ^CallSideEffect : ~mu0_2 +# 15| r0_8() = FunctionAddress[Add] : # 15| r0_9(Int32) = Constant[0] : # 15| r0_10(MyClass) = NewObj : -# 15| r0_11(glval) = FunctionAddress[MyClass] : +# 15| r0_11() = FunctionAddress[MyClass] : # 15| v0_12(Void) = Call : func:r0_11, this:r0_10 -# 15| mu0_13(null) = ^CallSideEffect : ~mu0_2 +# 15| mu0_13() = ^CallSideEffect : ~mu0_2 # 15| r0_14(String) = StringConstant["Hello"] : # 15| r0_15(glval) = FieldAddress[a] : r0_10 # 15| mu0_16(String) = Store : &:r0_15, r0_14 @@ -267,13 +271,13 @@ collections.cs: # 15| r0_18(glval) = FieldAddress[b] : r0_10 # 15| mu0_19(String) = Store : &:r0_18, r0_17 # 15| v0_20(Void) = Call : func:r0_8, this:r0_4, 0:r0_9, 1:r0_10 -# 15| mu0_21(null) = ^CallSideEffect : ~mu0_2 -# 16| r0_22(glval) = FunctionAddress[Add] : +# 15| mu0_21() = ^CallSideEffect : ~mu0_2 +# 16| r0_22() = FunctionAddress[Add] : # 16| r0_23(Int32) = Constant[1] : # 16| r0_24(MyClass) = NewObj : -# 16| r0_25(glval) = FunctionAddress[MyClass] : +# 16| r0_25() = FunctionAddress[MyClass] : # 16| v0_26(Void) = Call : func:r0_25, this:r0_24 -# 16| mu0_27(null) = ^CallSideEffect : ~mu0_2 +# 16| mu0_27() = ^CallSideEffect : ~mu0_2 # 16| r0_28(String) = StringConstant["Foo"] : # 16| r0_29(glval) = FieldAddress[a] : r0_24 # 16| mu0_30(String) = Store : &:r0_29, r0_28 @@ -281,29 +285,31 @@ collections.cs: # 16| r0_32(glval) = FieldAddress[b] : r0_24 # 16| mu0_33(String) = Store : &:r0_32, r0_31 # 16| v0_34(Void) = Call : func:r0_22, this:r0_4, 0:r0_23, 1:r0_24 -# 16| mu0_35(null) = ^CallSideEffect : ~mu0_2 +# 16| mu0_35() = ^CallSideEffect : ~mu0_2 # 13| mu0_36(Dictionary) = Store : &:r0_3, r0_4 # 11| v0_37(Void) = ReturnVoid : # 11| v0_38(Void) = UnmodeledUse : mu* -# 11| v0_39(Void) = ExitFunction : +# 11| v0_39(Void) = AliasedUse : ~mu0_2 +# 11| v0_40(Void) = ExitFunction : constructor_init.cs: # 5| System.Void BaseClass..ctor() # 5| Block 0 # 5| v0_0(Void) = EnterFunction : -# 5| mu0_1(null) = AliasedDefinition : -# 5| mu0_2(null) = UnmodeledDefinition : +# 5| mu0_1() = AliasedDefinition : +# 5| mu0_2() = UnmodeledDefinition : # 5| r0_3(glval) = InitializeThis : # 6| v0_4(Void) = NoOp : # 5| v0_5(Void) = ReturnVoid : # 5| v0_6(Void) = UnmodeledUse : mu* -# 5| v0_7(Void) = ExitFunction : +# 5| v0_7(Void) = AliasedUse : ~mu0_2 +# 5| v0_8(Void) = ExitFunction : # 9| System.Void BaseClass..ctor(System.Int32) # 9| Block 0 # 9| v0_0(Void) = EnterFunction : -# 9| mu0_1(null) = AliasedDefinition : -# 9| mu0_2(null) = UnmodeledDefinition : +# 9| mu0_1() = AliasedDefinition : +# 9| mu0_2() = UnmodeledDefinition : # 9| r0_3(glval) = InitializeThis : # 9| r0_4(glval) = VariableAddress[i] : # 9| mu0_5(Int32) = InitializeParameter[i] : &:r0_4 @@ -314,98 +320,103 @@ constructor_init.cs: # 11| mu0_10(Int32) = Store : &:r0_9, r0_7 # 9| v0_11(Void) = ReturnVoid : # 9| v0_12(Void) = UnmodeledUse : mu* -# 9| v0_13(Void) = ExitFunction : +# 9| v0_13(Void) = AliasedUse : ~mu0_2 +# 9| v0_14(Void) = ExitFunction : # 17| System.Void DerivedClass..ctor() # 17| Block 0 # 17| v0_0(Void) = EnterFunction : -# 17| mu0_1(null) = AliasedDefinition : -# 17| mu0_2(null) = UnmodeledDefinition : +# 17| mu0_1() = AliasedDefinition : +# 17| mu0_2() = UnmodeledDefinition : # 17| r0_3(glval) = InitializeThis : # 17| r0_4(glval) = Convert[DerivedClass : BaseClass] : r0_3 -# 17| r0_5(glval) = FunctionAddress[BaseClass] : +# 17| r0_5() = FunctionAddress[BaseClass] : # 17| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 17| mu0_7(null) = ^CallSideEffect : ~mu0_2 +# 17| mu0_7() = ^CallSideEffect : ~mu0_2 # 18| v0_8(Void) = NoOp : # 17| v0_9(Void) = ReturnVoid : # 17| v0_10(Void) = UnmodeledUse : mu* -# 17| v0_11(Void) = ExitFunction : +# 17| v0_11(Void) = AliasedUse : ~mu0_2 +# 17| v0_12(Void) = ExitFunction : # 21| System.Void DerivedClass..ctor(System.Int32) # 21| Block 0 # 21| v0_0(Void) = EnterFunction : -# 21| mu0_1(null) = AliasedDefinition : -# 21| mu0_2(null) = UnmodeledDefinition : +# 21| mu0_1() = AliasedDefinition : +# 21| mu0_2() = UnmodeledDefinition : # 21| r0_3(glval) = InitializeThis : # 21| r0_4(glval) = VariableAddress[i] : # 21| mu0_5(Int32) = InitializeParameter[i] : &:r0_4 # 21| r0_6(glval) = Convert[DerivedClass : BaseClass] : r0_3 -# 21| r0_7(glval) = FunctionAddress[BaseClass] : +# 21| r0_7() = FunctionAddress[BaseClass] : # 21| r0_8(glval) = VariableAddress[i] : # 21| r0_9(Int32) = Load : &:r0_8, ~mu0_2 # 21| v0_10(Void) = Call : func:r0_7, this:r0_6, 0:r0_9 -# 21| mu0_11(null) = ^CallSideEffect : ~mu0_2 +# 21| mu0_11() = ^CallSideEffect : ~mu0_2 # 22| v0_12(Void) = NoOp : # 21| v0_13(Void) = ReturnVoid : # 21| v0_14(Void) = UnmodeledUse : mu* -# 21| v0_15(Void) = ExitFunction : +# 21| v0_15(Void) = AliasedUse : ~mu0_2 +# 21| v0_16(Void) = ExitFunction : # 25| System.Void DerivedClass..ctor(System.Int32,System.Int32) # 25| Block 0 # 25| v0_0(Void) = EnterFunction : -# 25| mu0_1(null) = AliasedDefinition : -# 25| mu0_2(null) = UnmodeledDefinition : +# 25| mu0_1() = AliasedDefinition : +# 25| mu0_2() = UnmodeledDefinition : # 25| r0_3(glval) = InitializeThis : # 25| r0_4(glval) = VariableAddress[i] : # 25| mu0_5(Int32) = InitializeParameter[i] : &:r0_4 # 25| r0_6(glval) = VariableAddress[j] : # 25| mu0_7(Int32) = InitializeParameter[j] : &:r0_6 -# 25| r0_8(glval) = FunctionAddress[DerivedClass] : +# 25| r0_8() = FunctionAddress[DerivedClass] : # 25| r0_9(glval) = VariableAddress[i] : # 25| r0_10(Int32) = Load : &:r0_9, ~mu0_2 # 25| v0_11(Void) = Call : func:r0_8, this:r0_3, 0:r0_10 -# 25| mu0_12(null) = ^CallSideEffect : ~mu0_2 +# 25| mu0_12() = ^CallSideEffect : ~mu0_2 # 26| v0_13(Void) = NoOp : # 25| v0_14(Void) = ReturnVoid : # 25| v0_15(Void) = UnmodeledUse : mu* -# 25| v0_16(Void) = ExitFunction : +# 25| v0_16(Void) = AliasedUse : ~mu0_2 +# 25| v0_17(Void) = ExitFunction : # 29| System.Void DerivedClass.Main() # 29| Block 0 # 29| v0_0(Void) = EnterFunction : -# 29| mu0_1(null) = AliasedDefinition : -# 29| mu0_2(null) = UnmodeledDefinition : +# 29| mu0_1() = AliasedDefinition : +# 29| mu0_2() = UnmodeledDefinition : # 31| r0_3(glval) = VariableAddress[obj1] : # 31| r0_4(DerivedClass) = NewObj : -# 31| r0_5(glval) = FunctionAddress[DerivedClass] : +# 31| r0_5() = FunctionAddress[DerivedClass] : # 31| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 31| mu0_7(null) = ^CallSideEffect : ~mu0_2 +# 31| mu0_7() = ^CallSideEffect : ~mu0_2 # 31| mu0_8(DerivedClass) = Store : &:r0_3, r0_4 # 32| r0_9(glval) = VariableAddress[obj2] : # 32| r0_10(DerivedClass) = NewObj : -# 32| r0_11(glval) = FunctionAddress[DerivedClass] : +# 32| r0_11() = FunctionAddress[DerivedClass] : # 32| r0_12(Int32) = Constant[1] : # 32| v0_13(Void) = Call : func:r0_11, this:r0_10, 0:r0_12 -# 32| mu0_14(null) = ^CallSideEffect : ~mu0_2 +# 32| mu0_14() = ^CallSideEffect : ~mu0_2 # 32| mu0_15(DerivedClass) = Store : &:r0_9, r0_10 # 33| r0_16(glval) = VariableAddress[obj3] : # 33| r0_17(DerivedClass) = NewObj : -# 33| r0_18(glval) = FunctionAddress[DerivedClass] : +# 33| r0_18() = FunctionAddress[DerivedClass] : # 33| r0_19(Int32) = Constant[1] : # 33| r0_20(Int32) = Constant[2] : # 33| v0_21(Void) = Call : func:r0_18, this:r0_17, 0:r0_19, 1:r0_20 -# 33| mu0_22(null) = ^CallSideEffect : ~mu0_2 +# 33| mu0_22() = ^CallSideEffect : ~mu0_2 # 33| mu0_23(DerivedClass) = Store : &:r0_16, r0_17 # 29| v0_24(Void) = ReturnVoid : # 29| v0_25(Void) = UnmodeledUse : mu* -# 29| v0_26(Void) = ExitFunction : +# 29| v0_26(Void) = AliasedUse : ~mu0_2 +# 29| v0_27(Void) = ExitFunction : crement.cs: # 3| System.Void CrementOpsTest.Main() # 3| Block 0 # 3| v0_0(Void) = EnterFunction : -# 3| mu0_1(null) = AliasedDefinition : -# 3| mu0_2(null) = UnmodeledDefinition : +# 3| mu0_1() = AliasedDefinition : +# 3| mu0_2() = UnmodeledDefinition : # 5| r0_3(glval) = VariableAddress[x] : # 5| r0_4(Int32) = Constant[10] : # 5| mu0_5(Int32) = Store : &:r0_3, r0_4 @@ -439,14 +450,15 @@ crement.cs: # 9| mu0_33(Int32) = Store : &:r0_32, r0_28 # 3| v0_34(Void) = ReturnVoid : # 3| v0_35(Void) = UnmodeledUse : mu* -# 3| v0_36(Void) = ExitFunction : +# 3| v0_36(Void) = AliasedUse : ~mu0_2 +# 3| v0_37(Void) = ExitFunction : delegates.cs: # 6| System.Int32 Delegates.returns(System.Int32) # 6| Block 0 # 6| v0_0(Void) = EnterFunction : -# 6| mu0_1(null) = AliasedDefinition : -# 6| mu0_2(null) = UnmodeledDefinition : +# 6| mu0_1() = AliasedDefinition : +# 6| mu0_2() = UnmodeledDefinition : # 6| r0_3(glval) = VariableAddress[ret] : # 6| mu0_4(Int32) = InitializeParameter[ret] : &:r0_3 # 8| r0_5(glval) = VariableAddress[#return] : @@ -456,88 +468,93 @@ delegates.cs: # 6| r0_9(glval) = VariableAddress[#return] : # 6| v0_10(Void) = ReturnValue : &:r0_9, ~mu0_2 # 6| v0_11(Void) = UnmodeledUse : mu* -# 6| v0_12(Void) = ExitFunction : +# 6| v0_12(Void) = AliasedUse : ~mu0_2 +# 6| v0_13(Void) = ExitFunction : # 11| System.Void Delegates.Main() # 11| Block 0 -# 11| v0_0(Void) = EnterFunction : -# 11| mu0_1(null) = AliasedDefinition : -# 11| mu0_2(null) = UnmodeledDefinition : -# 12| r0_3(glval) = VariableAddress[del1] : -# 12| r0_4(Del) = NewObj : -# 12| r0_5(glval) = FunctionAddress[Del] : -# 12| r0_6(glval) = FunctionAddress[returns] : -# 12| v0_7(Void) = Call : func:r0_5, this:r0_4, 0:r0_6 -# 12| mu0_8(null) = ^CallSideEffect : ~mu0_2 -# 12| mu0_9(Del) = Store : &:r0_3, r0_4 -# 13| r0_10(glval) = VariableAddress[del1] : -# 13| r0_11(Del) = Load : &:r0_10, ~mu0_2 -# 13| r0_12(glval) = FunctionAddress[Invoke] : -# 13| r0_13(Int32) = Constant[5] : -# 13| v0_14(Void) = Call : func:r0_12, this:r0_11, 0:r0_13 -# 13| mu0_15(null) = ^CallSideEffect : ~mu0_2 -# 11| v0_16(Void) = ReturnVoid : -# 11| v0_17(Void) = UnmodeledUse : mu* -# 11| v0_18(Void) = ExitFunction : +# 11| v0_0(Void) = EnterFunction : +# 11| mu0_1() = AliasedDefinition : +# 11| mu0_2() = UnmodeledDefinition : +# 12| r0_3(glval) = VariableAddress[del1] : +# 12| r0_4(Del) = NewObj : +# 12| r0_5() = FunctionAddress[Del] : +# 12| r0_6(glval) = FunctionAddress[returns] : +# 12| v0_7(Void) = Call : func:r0_5, this:r0_4, 0:r0_6 +# 12| mu0_8() = ^CallSideEffect : ~mu0_2 +# 12| mu0_9(Del) = Store : &:r0_3, r0_4 +# 13| r0_10(glval) = VariableAddress[del1] : +# 13| r0_11(Del) = Load : &:r0_10, ~mu0_2 +# 13| r0_12() = FunctionAddress[Invoke] : +# 13| r0_13(Int32) = Constant[5] : +# 13| v0_14(Void) = Call : func:r0_12, this:r0_11, 0:r0_13 +# 13| mu0_15() = ^CallSideEffect : ~mu0_2 +# 11| v0_16(Void) = ReturnVoid : +# 11| v0_17(Void) = UnmodeledUse : mu* +# 11| v0_18(Void) = AliasedUse : ~mu0_2 +# 11| v0_19(Void) = ExitFunction : events.cs: # 8| System.Void Events..ctor() # 8| Block 0 # 8| v0_0(Void) = EnterFunction : -# 8| mu0_1(null) = AliasedDefinition : -# 8| mu0_2(null) = UnmodeledDefinition : +# 8| mu0_1() = AliasedDefinition : +# 8| mu0_2() = UnmodeledDefinition : # 8| r0_3(glval) = InitializeThis : # 10| r0_4(MyDel) = NewObj : -# 10| r0_5(glval) = FunctionAddress[MyDel] : +# 10| r0_5() = FunctionAddress[MyDel] : # 10| r0_6(glval) = FunctionAddress[Fun] : # 10| v0_7(Void) = Call : func:r0_5, this:r0_4, 0:r0_6 -# 10| mu0_8(null) = ^CallSideEffect : ~mu0_2 +# 10| mu0_8() = ^CallSideEffect : ~mu0_2 # 10| r0_9(Events) = CopyValue : r0_3 # 10| r0_10(glval) = FieldAddress[Inst] : r0_9 # 10| mu0_11(MyDel) = Store : &:r0_10, r0_4 # 8| v0_12(Void) = ReturnVoid : # 8| v0_13(Void) = UnmodeledUse : mu* -# 8| v0_14(Void) = ExitFunction : +# 8| v0_14(Void) = AliasedUse : ~mu0_2 +# 8| v0_15(Void) = ExitFunction : # 13| System.Void Events.AddEvent() # 13| Block 0 # 13| v0_0(Void) = EnterFunction : -# 13| mu0_1(null) = AliasedDefinition : -# 13| mu0_2(null) = UnmodeledDefinition : +# 13| mu0_1() = AliasedDefinition : +# 13| mu0_2() = UnmodeledDefinition : # 13| r0_3(glval) = InitializeThis : # 15| r0_4(Events) = CopyValue : r0_3 -# 15| r0_5(glval) = FunctionAddress[add_MyEvent] : +# 15| r0_5() = FunctionAddress[add_MyEvent] : # 15| r0_6(Events) = CopyValue : r0_3 # 15| r0_7(glval) = FieldAddress[Inst] : r0_6 # 15| r0_8(MyDel) = Load : &:r0_7, ~mu0_2 # 15| v0_9(Void) = Call : func:r0_5, this:r0_4, 0:r0_8 -# 15| mu0_10(null) = ^CallSideEffect : ~mu0_2 +# 15| mu0_10() = ^CallSideEffect : ~mu0_2 # 13| v0_11(Void) = ReturnVoid : # 13| v0_12(Void) = UnmodeledUse : mu* -# 13| v0_13(Void) = ExitFunction : +# 13| v0_13(Void) = AliasedUse : ~mu0_2 +# 13| v0_14(Void) = ExitFunction : # 18| System.Void Events.RemoveEvent() # 18| Block 0 # 18| v0_0(Void) = EnterFunction : -# 18| mu0_1(null) = AliasedDefinition : -# 18| mu0_2(null) = UnmodeledDefinition : +# 18| mu0_1() = AliasedDefinition : +# 18| mu0_2() = UnmodeledDefinition : # 18| r0_3(glval) = InitializeThis : # 20| r0_4(Events) = CopyValue : r0_3 -# 20| r0_5(glval) = FunctionAddress[remove_MyEvent] : +# 20| r0_5() = FunctionAddress[remove_MyEvent] : # 20| r0_6(Events) = CopyValue : r0_3 # 20| r0_7(glval) = FieldAddress[Inst] : r0_6 # 20| r0_8(MyDel) = Load : &:r0_7, ~mu0_2 # 20| v0_9(Void) = Call : func:r0_5, this:r0_4, 0:r0_8 -# 20| mu0_10(null) = ^CallSideEffect : ~mu0_2 +# 20| mu0_10() = ^CallSideEffect : ~mu0_2 # 18| v0_11(Void) = ReturnVoid : # 18| v0_12(Void) = UnmodeledUse : mu* -# 18| v0_13(Void) = ExitFunction : +# 18| v0_13(Void) = AliasedUse : ~mu0_2 +# 18| v0_14(Void) = ExitFunction : # 23| System.String Events.Fun(System.String) # 23| Block 0 # 23| v0_0(Void) = EnterFunction : -# 23| mu0_1(null) = AliasedDefinition : -# 23| mu0_2(null) = UnmodeledDefinition : +# 23| mu0_1() = AliasedDefinition : +# 23| mu0_2() = UnmodeledDefinition : # 23| r0_3(glval) = InitializeThis : # 23| r0_4(glval) = VariableAddress[str] : # 23| mu0_5(String) = InitializeParameter[str] : &:r0_4 @@ -548,49 +565,51 @@ events.cs: # 23| r0_10(glval) = VariableAddress[#return] : # 23| v0_11(Void) = ReturnValue : &:r0_10, ~mu0_2 # 23| v0_12(Void) = UnmodeledUse : mu* -# 23| v0_13(Void) = ExitFunction : +# 23| v0_13(Void) = AliasedUse : ~mu0_2 +# 23| v0_14(Void) = ExitFunction : # 28| System.Void Events.Main(System.String[]) # 28| Block 0 # 28| v0_0(Void) = EnterFunction : -# 28| mu0_1(null) = AliasedDefinition : -# 28| mu0_2(null) = UnmodeledDefinition : +# 28| mu0_1() = AliasedDefinition : +# 28| mu0_2() = UnmodeledDefinition : # 28| r0_3(glval) = VariableAddress[args] : # 28| mu0_4(String[]) = InitializeParameter[args] : &:r0_3 # 30| r0_5(glval) = VariableAddress[obj] : # 30| r0_6(Events) = NewObj : -# 30| r0_7(glval) = FunctionAddress[Events] : +# 30| r0_7() = FunctionAddress[Events] : # 30| v0_8(Void) = Call : func:r0_7, this:r0_6 -# 30| mu0_9(null) = ^CallSideEffect : ~mu0_2 +# 30| mu0_9() = ^CallSideEffect : ~mu0_2 # 30| mu0_10(Events) = Store : &:r0_5, r0_6 # 31| r0_11(glval) = VariableAddress[obj] : # 31| r0_12(Events) = Load : &:r0_11, ~mu0_2 -# 31| r0_13(glval) = FunctionAddress[AddEvent] : +# 31| r0_13() = FunctionAddress[AddEvent] : # 31| v0_14(Void) = Call : func:r0_13, this:r0_12 -# 31| mu0_15(null) = ^CallSideEffect : ~mu0_2 +# 31| mu0_15() = ^CallSideEffect : ~mu0_2 # 32| r0_16(glval) = VariableAddress[result] : # 32| r0_17(glval) = VariableAddress[obj] : # 32| r0_18(Events) = Load : &:r0_17, ~mu0_2 -# 32| r0_19(glval) = FunctionAddress[Invoke] : +# 32| r0_19() = FunctionAddress[Invoke] : # 32| r0_20(String) = StringConstant["string"] : # 32| v0_21(Void) = Call : func:r0_19, this:r0_18, 0:r0_20 -# 32| mu0_22(null) = ^CallSideEffect : ~mu0_2 +# 32| mu0_22() = ^CallSideEffect : ~mu0_2 # 32| mu0_23(String) = Store : &:r0_16, v0_21 # 33| r0_24(glval) = VariableAddress[obj] : # 33| r0_25(Events) = Load : &:r0_24, ~mu0_2 -# 33| r0_26(glval) = FunctionAddress[RemoveEvent] : +# 33| r0_26() = FunctionAddress[RemoveEvent] : # 33| v0_27(Void) = Call : func:r0_26, this:r0_25 -# 33| mu0_28(null) = ^CallSideEffect : ~mu0_2 +# 33| mu0_28() = ^CallSideEffect : ~mu0_2 # 28| v0_29(Void) = ReturnVoid : # 28| v0_30(Void) = UnmodeledUse : mu* -# 28| v0_31(Void) = ExitFunction : +# 28| v0_31(Void) = AliasedUse : ~mu0_2 +# 28| v0_32(Void) = ExitFunction : foreach.cs: # 4| System.Void ForEach.Main() # 4| Block 0 # 4| v0_0(Void) = EnterFunction : -# 4| mu0_1(null) = AliasedDefinition : -# 4| mu0_2(null) = UnmodeledDefinition : +# 4| mu0_1() = AliasedDefinition : +# 4| mu0_2() = UnmodeledDefinition : # 5| r0_3(glval) = VariableAddress[a_array] : # 5| mu0_4(Int32[]) = Uninitialized[a_array] : &:r0_3 # 5| r0_5(Int32) = Constant[0] : @@ -624,18 +643,18 @@ foreach.cs: # 7| r0_33(glval) = VariableAddress[#temp7:9] : # 7| r0_34(glval) = VariableAddress[a_array] : # 7| r0_35(Int32[]) = Load : &:r0_34, ~mu0_2 -# 7| r0_36(glval) = FunctionAddress[GetEnumerator] : +# 7| r0_36() = FunctionAddress[GetEnumerator] : # 7| r0_37(IEnumerator) = Call : func:r0_36, this:r0_35 -# 7| mu0_38(null) = ^CallSideEffect : ~mu0_2 +# 7| mu0_38() = ^CallSideEffect : ~mu0_2 # 7| mu0_39(IEnumerator) = Store : &:r0_33, r0_37 #-----| Goto -> Block 1 # 7| Block 1 # 7| r1_0(glval) = VariableAddress[#temp7:9] : # 7| r1_1(Boolean) = Load : &:r1_0, ~mu0_2 -# 7| r1_2(glval) = FunctionAddress[MoveNext] : +# 7| r1_2() = FunctionAddress[MoveNext] : # 7| r1_3(Boolean) = Call : func:r1_2, this:r1_1 -# 7| mu1_4(null) = ^CallSideEffect : ~mu0_2 +# 7| mu1_4() = ^CallSideEffect : ~mu0_2 # 7| v1_5(Void) = ConditionalBranch : r1_3 #-----| False -> Block 3 #-----| True -> Block 2 @@ -644,9 +663,9 @@ foreach.cs: # 7| r2_0(glval) = VariableAddress[items] : # 7| r2_1(glval) = VariableAddress[#temp7:9] : # 7| r2_2(Boolean) = Load : &:r2_1, ~mu0_2 -# 7| r2_3(glval) = FunctionAddress[get_Current] : +# 7| r2_3() = FunctionAddress[get_Current] : # 7| r2_4(Int32) = Call : func:r2_3, this:r2_2 -# 7| mu2_5(null) = ^CallSideEffect : ~mu0_2 +# 7| mu2_5() = ^CallSideEffect : ~mu0_2 # 7| mu2_6(Int32) = Store : &:r2_0, r2_4 # 9| r2_7(glval) = VariableAddress[x] : # 9| r2_8(glval) = VariableAddress[items] : @@ -657,19 +676,20 @@ foreach.cs: # 7| Block 3 # 7| r3_0(glval) = VariableAddress[#temp7:9] : # 7| r3_1(Boolean) = Load : &:r3_0, ~mu0_2 -# 7| r3_2(glval) = FunctionAddress[Dispose] : +# 7| r3_2() = FunctionAddress[Dispose] : # 7| v3_3(Void) = Call : func:r3_2, this:r3_1 -# 7| mu3_4(null) = ^CallSideEffect : ~mu0_2 +# 7| mu3_4() = ^CallSideEffect : ~mu0_2 # 4| v3_5(Void) = ReturnVoid : # 4| v3_6(Void) = UnmodeledUse : mu* -# 4| v3_7(Void) = ExitFunction : +# 4| v3_7(Void) = AliasedUse : ~mu0_2 +# 4| v3_8(Void) = ExitFunction : func_with_param_call.cs: # 5| System.Int32 test_call_with_param.f(System.Int32,System.Int32) # 5| Block 0 # 5| v0_0(Void) = EnterFunction : -# 5| mu0_1(null) = AliasedDefinition : -# 5| mu0_2(null) = UnmodeledDefinition : +# 5| mu0_1() = AliasedDefinition : +# 5| mu0_2() = UnmodeledDefinition : # 5| r0_3(glval) = VariableAddress[x] : # 5| mu0_4(Int32) = InitializeParameter[x] : &:r0_3 # 5| r0_5(glval) = VariableAddress[y] : @@ -684,31 +704,33 @@ func_with_param_call.cs: # 5| r0_14(glval) = VariableAddress[#return] : # 5| v0_15(Void) = ReturnValue : &:r0_14, ~mu0_2 # 5| v0_16(Void) = UnmodeledUse : mu* -# 5| v0_17(Void) = ExitFunction : +# 5| v0_17(Void) = AliasedUse : ~mu0_2 +# 5| v0_18(Void) = ExitFunction : # 10| System.Int32 test_call_with_param.g() # 10| Block 0 # 10| v0_0(Void) = EnterFunction : -# 10| mu0_1(null) = AliasedDefinition : -# 10| mu0_2(null) = UnmodeledDefinition : +# 10| mu0_1() = AliasedDefinition : +# 10| mu0_2() = UnmodeledDefinition : # 12| r0_3(glval) = VariableAddress[#return] : -# 12| r0_4(glval) = FunctionAddress[f] : +# 12| r0_4() = FunctionAddress[f] : # 12| r0_5(Int32) = Constant[2] : # 12| r0_6(Int32) = Constant[3] : # 12| r0_7(Int32) = Call : func:r0_4, 0:r0_5, 1:r0_6 -# 12| mu0_8(null) = ^CallSideEffect : ~mu0_2 +# 12| mu0_8() = ^CallSideEffect : ~mu0_2 # 12| mu0_9(Int32) = Store : &:r0_3, r0_7 # 10| r0_10(glval) = VariableAddress[#return] : # 10| v0_11(Void) = ReturnValue : &:r0_10, ~mu0_2 # 10| v0_12(Void) = UnmodeledUse : mu* -# 10| v0_13(Void) = ExitFunction : +# 10| v0_13(Void) = AliasedUse : ~mu0_2 +# 10| v0_14(Void) = ExitFunction : indexers.cs: # 8| System.String Indexers.MyClass.get_Item(System.Int32) # 8| Block 0 # 8| v0_0(Void) = EnterFunction : -# 8| mu0_1(null) = AliasedDefinition : -# 8| mu0_2(null) = UnmodeledDefinition : +# 8| mu0_1() = AliasedDefinition : +# 8| mu0_2() = UnmodeledDefinition : # 8| r0_3(glval) = InitializeThis : # 6| r0_4(glval) = VariableAddress[index] : # 6| mu0_5(Int32) = InitializeParameter[index] : &:r0_4 @@ -724,13 +746,14 @@ indexers.cs: # 8| r0_15(glval) = VariableAddress[#return] : # 8| v0_16(Void) = ReturnValue : &:r0_15, ~mu0_2 # 8| v0_17(Void) = UnmodeledUse : mu* -# 8| v0_18(Void) = ExitFunction : +# 8| v0_18(Void) = AliasedUse : ~mu0_2 +# 8| v0_19(Void) = ExitFunction : # 12| System.Void Indexers.MyClass.set_Item(System.Int32,System.String) # 12| Block 0 # 12| v0_0(Void) = EnterFunction : -# 12| mu0_1(null) = AliasedDefinition : -# 12| mu0_2(null) = UnmodeledDefinition : +# 12| mu0_1() = AliasedDefinition : +# 12| mu0_2() = UnmodeledDefinition : # 12| r0_3(glval) = InitializeThis : # 6| r0_4(glval) = VariableAddress[index] : # 6| mu0_5(Int32) = InitializeParameter[index] : &:r0_4 @@ -747,55 +770,57 @@ indexers.cs: # 14| mu0_16(String) = Store : &:r0_15, r0_9 # 12| v0_17(Void) = ReturnVoid : # 12| v0_18(Void) = UnmodeledUse : mu* -# 12| v0_19(Void) = ExitFunction : +# 12| v0_19(Void) = AliasedUse : ~mu0_2 +# 12| v0_20(Void) = ExitFunction : # 19| System.Void Indexers.Main() # 19| Block 0 # 19| v0_0(Void) = EnterFunction : -# 19| mu0_1(null) = AliasedDefinition : -# 19| mu0_2(null) = UnmodeledDefinition : +# 19| mu0_1() = AliasedDefinition : +# 19| mu0_2() = UnmodeledDefinition : # 21| r0_3(glval) = VariableAddress[inst] : # 21| r0_4(MyClass) = NewObj : -# 21| r0_5(glval) = FunctionAddress[MyClass] : +# 21| r0_5() = FunctionAddress[MyClass] : # 21| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 21| mu0_7(null) = ^CallSideEffect : ~mu0_2 +# 21| mu0_7() = ^CallSideEffect : ~mu0_2 # 21| mu0_8(MyClass) = Store : &:r0_3, r0_4 # 22| r0_9(glval) = VariableAddress[inst] : # 22| r0_10(MyClass) = Load : &:r0_9, ~mu0_2 -# 22| r0_11(glval) = FunctionAddress[set_Item] : +# 22| r0_11() = FunctionAddress[set_Item] : # 22| r0_12(Int32) = Constant[0] : # 22| r0_13(String) = StringConstant["str1"] : # 22| v0_14(Void) = Call : func:r0_11, this:r0_10, 0:r0_12, 1:r0_13 -# 22| mu0_15(null) = ^CallSideEffect : ~mu0_2 +# 22| mu0_15() = ^CallSideEffect : ~mu0_2 # 23| r0_16(glval) = VariableAddress[inst] : # 23| r0_17(MyClass) = Load : &:r0_16, ~mu0_2 -# 23| r0_18(glval) = FunctionAddress[set_Item] : +# 23| r0_18() = FunctionAddress[set_Item] : # 23| r0_19(Int32) = Constant[1] : # 23| r0_20(String) = StringConstant["str1"] : # 23| v0_21(Void) = Call : func:r0_18, this:r0_17, 0:r0_19, 1:r0_20 -# 23| mu0_22(null) = ^CallSideEffect : ~mu0_2 +# 23| mu0_22() = ^CallSideEffect : ~mu0_2 # 24| r0_23(glval) = VariableAddress[inst] : # 24| r0_24(MyClass) = Load : &:r0_23, ~mu0_2 -# 24| r0_25(glval) = FunctionAddress[set_Item] : +# 24| r0_25() = FunctionAddress[set_Item] : # 24| r0_26(Int32) = Constant[1] : # 24| r0_27(glval) = VariableAddress[inst] : # 24| r0_28(MyClass) = Load : &:r0_27, ~mu0_2 -# 24| r0_29(glval) = FunctionAddress[get_Item] : +# 24| r0_29() = FunctionAddress[get_Item] : # 24| r0_30(Int32) = Constant[0] : # 24| r0_31(String) = Call : func:r0_29, this:r0_28, 0:r0_30 -# 24| mu0_32(null) = ^CallSideEffect : ~mu0_2 +# 24| mu0_32() = ^CallSideEffect : ~mu0_2 # 24| v0_33(Void) = Call : func:r0_25, this:r0_24, 0:r0_26, 1:r0_31 -# 24| mu0_34(null) = ^CallSideEffect : ~mu0_2 +# 24| mu0_34() = ^CallSideEffect : ~mu0_2 # 19| v0_35(Void) = ReturnVoid : # 19| v0_36(Void) = UnmodeledUse : mu* -# 19| v0_37(Void) = ExitFunction : +# 19| v0_37(Void) = AliasedUse : ~mu0_2 +# 19| v0_38(Void) = ExitFunction : inheritance_polymorphism.cs: # 3| System.Int32 A.function() # 3| Block 0 # 3| v0_0(Void) = EnterFunction : -# 3| mu0_1(null) = AliasedDefinition : -# 3| mu0_2(null) = UnmodeledDefinition : +# 3| mu0_1() = AliasedDefinition : +# 3| mu0_2() = UnmodeledDefinition : # 3| r0_3(glval) = InitializeThis : # 5| r0_4(glval) = VariableAddress[#return] : # 5| r0_5(Int32) = Constant[0] : @@ -803,13 +828,14 @@ inheritance_polymorphism.cs: # 3| r0_7(glval) = VariableAddress[#return] : # 3| v0_8(Void) = ReturnValue : &:r0_7, ~mu0_2 # 3| v0_9(Void) = UnmodeledUse : mu* -# 3| v0_10(Void) = ExitFunction : +# 3| v0_10(Void) = AliasedUse : ~mu0_2 +# 3| v0_11(Void) = ExitFunction : # 15| System.Int32 C.function() # 15| Block 0 # 15| v0_0(Void) = EnterFunction : -# 15| mu0_1(null) = AliasedDefinition : -# 15| mu0_2(null) = UnmodeledDefinition : +# 15| mu0_1() = AliasedDefinition : +# 15| mu0_2() = UnmodeledDefinition : # 15| r0_3(glval) = InitializeThis : # 17| r0_4(glval) = VariableAddress[#return] : # 17| r0_5(Int32) = Constant[1] : @@ -817,58 +843,60 @@ inheritance_polymorphism.cs: # 15| r0_7(glval) = VariableAddress[#return] : # 15| v0_8(Void) = ReturnValue : &:r0_7, ~mu0_2 # 15| v0_9(Void) = UnmodeledUse : mu* -# 15| v0_10(Void) = ExitFunction : +# 15| v0_10(Void) = AliasedUse : ~mu0_2 +# 15| v0_11(Void) = ExitFunction : # 23| System.Void Program.Main() # 23| Block 0 -# 23| v0_0(Void) = EnterFunction : -# 23| mu0_1(null) = AliasedDefinition : -# 23| mu0_2(null) = UnmodeledDefinition : -# 25| r0_3(glval) = VariableAddress[objB] : -# 25| r0_4(B) = NewObj : -# 25| r0_5(glval) = FunctionAddress[B] : -# 25| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 25| mu0_7(null) = ^CallSideEffect : ~mu0_2 -# 25| mu0_8(B) = Store : &:r0_3, r0_4 -# 26| r0_9(glval) = VariableAddress[objB] : -# 26| r0_10(B) = Load : &:r0_9, ~mu0_2 -# 26| r0_11(glval) = FunctionAddress[function] : -# 26| r0_12(Int32) = Call : func:r0_11, this:r0_10 -# 26| mu0_13(null) = ^CallSideEffect : ~mu0_2 -# 29| r0_14(glval) = VariableAddress[objA] : -# 29| mu0_15(A) = Uninitialized[objA] : &:r0_14 -# 30| r0_16(glval) = VariableAddress[objB] : -# 30| r0_17(B) = Load : &:r0_16, ~mu0_2 -# 30| r0_18(A) = Convert : r0_17 -# 30| r0_19(glval) = VariableAddress[objA] : -# 30| mu0_20(A) = Store : &:r0_19, r0_18 -# 31| r0_21(glval) = VariableAddress[objA] : -# 31| r0_22(A) = Load : &:r0_21, ~mu0_2 -# 31| r0_23(glval) = FunctionAddress[function] : -# 31| r0_24(Int32) = Call : func:r0_23, this:r0_22 -# 31| mu0_25(null) = ^CallSideEffect : ~mu0_2 -# 33| r0_26(glval) = VariableAddress[objC] : -# 33| r0_27(C) = NewObj : -# 33| r0_28(glval) = FunctionAddress[C] : -# 33| v0_29(Void) = Call : func:r0_28, this:r0_27 -# 33| mu0_30(null) = ^CallSideEffect : ~mu0_2 -# 33| r0_31(A) = Convert : r0_27 -# 33| mu0_32(A) = Store : &:r0_26, r0_27 -# 34| r0_33(glval) = VariableAddress[objC] : -# 34| r0_34(A) = Load : &:r0_33, ~mu0_2 -# 34| r0_35(glval) = FunctionAddress[function] : -# 34| r0_36(Int32) = Call : func:r0_35, this:r0_34 -# 34| mu0_37(null) = ^CallSideEffect : ~mu0_2 -# 23| v0_38(Void) = ReturnVoid : -# 23| v0_39(Void) = UnmodeledUse : mu* -# 23| v0_40(Void) = ExitFunction : +# 23| v0_0(Void) = EnterFunction : +# 23| mu0_1() = AliasedDefinition : +# 23| mu0_2() = UnmodeledDefinition : +# 25| r0_3(glval) = VariableAddress[objB] : +# 25| r0_4(B) = NewObj : +# 25| r0_5() = FunctionAddress[B] : +# 25| v0_6(Void) = Call : func:r0_5, this:r0_4 +# 25| mu0_7() = ^CallSideEffect : ~mu0_2 +# 25| mu0_8(B) = Store : &:r0_3, r0_4 +# 26| r0_9(glval) = VariableAddress[objB] : +# 26| r0_10(B) = Load : &:r0_9, ~mu0_2 +# 26| r0_11() = FunctionAddress[function] : +# 26| r0_12(Int32) = Call : func:r0_11, this:r0_10 +# 26| mu0_13() = ^CallSideEffect : ~mu0_2 +# 29| r0_14(glval) = VariableAddress[objA] : +# 29| mu0_15(A) = Uninitialized[objA] : &:r0_14 +# 30| r0_16(glval) = VariableAddress[objB] : +# 30| r0_17(B) = Load : &:r0_16, ~mu0_2 +# 30| r0_18(A) = Convert : r0_17 +# 30| r0_19(glval) = VariableAddress[objA] : +# 30| mu0_20(A) = Store : &:r0_19, r0_18 +# 31| r0_21(glval) = VariableAddress[objA] : +# 31| r0_22(A) = Load : &:r0_21, ~mu0_2 +# 31| r0_23() = FunctionAddress[function] : +# 31| r0_24(Int32) = Call : func:r0_23, this:r0_22 +# 31| mu0_25() = ^CallSideEffect : ~mu0_2 +# 33| r0_26(glval) = VariableAddress[objC] : +# 33| r0_27(C) = NewObj : +# 33| r0_28() = FunctionAddress[C] : +# 33| v0_29(Void) = Call : func:r0_28, this:r0_27 +# 33| mu0_30() = ^CallSideEffect : ~mu0_2 +# 33| r0_31(A) = Convert : r0_27 +# 33| mu0_32(A) = Store : &:r0_26, r0_27 +# 34| r0_33(glval) = VariableAddress[objC] : +# 34| r0_34(A) = Load : &:r0_33, ~mu0_2 +# 34| r0_35() = FunctionAddress[function] : +# 34| r0_36(Int32) = Call : func:r0_35, this:r0_34 +# 34| mu0_37() = ^CallSideEffect : ~mu0_2 +# 23| v0_38(Void) = ReturnVoid : +# 23| v0_39(Void) = UnmodeledUse : mu* +# 23| v0_40(Void) = AliasedUse : ~mu0_2 +# 23| v0_41(Void) = ExitFunction : inoutref.cs: # 11| System.Void InOutRef.set(MyClass,MyClass) # 11| Block 0 # 11| v0_0(Void) = EnterFunction : -# 11| mu0_1(null) = AliasedDefinition : -# 11| mu0_2(null) = UnmodeledDefinition : +# 11| mu0_1() = AliasedDefinition : +# 11| mu0_2() = UnmodeledDefinition : # 11| r0_3(glval) = VariableAddress[o1] : # 11| mu0_4(MyClass) = InitializeParameter[o1] : &:r0_3 # 11| r0_5(glval) = VariableAddress[o2] : @@ -880,13 +908,14 @@ inoutref.cs: # 13| mu0_11(MyClass) = Store : &:r0_10, r0_8 # 11| v0_12(Void) = ReturnVoid : # 11| v0_13(Void) = UnmodeledUse : mu* -# 11| v0_14(Void) = ExitFunction : +# 11| v0_14(Void) = AliasedUse : ~mu0_2 +# 11| v0_15(Void) = ExitFunction : # 16| System.Void InOutRef.F(System.Int32,MyStruct,MyStruct,MyClass,MyClass) # 16| Block 0 # 16| v0_0(Void) = EnterFunction : -# 16| mu0_1(null) = AliasedDefinition : -# 16| mu0_2(null) = UnmodeledDefinition : +# 16| mu0_1() = AliasedDefinition : +# 16| mu0_2() = UnmodeledDefinition : # 16| r0_3(glval) = VariableAddress[a] : # 16| mu0_4(Int32) = InitializeParameter[a] : &:r0_3 # 16| r0_5(glval) = VariableAddress[b] : @@ -929,47 +958,48 @@ inoutref.cs: # 24| r0_42(glval) = VariableAddress[b] : # 24| r0_43(MyStruct) = Load : &:r0_42, ~mu0_2 # 24| mu0_44(MyStruct) = Store : &:r0_43, r0_41 -# 26| r0_45(glval) = FunctionAddress[set] : +# 26| r0_45() = FunctionAddress[set] : # 26| r0_46(glval) = VariableAddress[c] : # 26| r0_47(MyClass) = Load : &:r0_46, ~mu0_2 # 26| r0_48(glval) = VariableAddress[c1] : # 26| r0_49(MyClass) = Load : &:r0_48, ~mu0_2 # 26| r0_50(MyClass) = Load : &:r0_49, ~mu0_2 # 26| v0_51(Void) = Call : func:r0_45, 0:r0_47, 1:r0_50 -# 26| mu0_52(null) = ^CallSideEffect : ~mu0_2 +# 26| mu0_52() = ^CallSideEffect : ~mu0_2 # 16| v0_53(Void) = ReturnVoid : # 16| v0_54(Void) = UnmodeledUse : mu* -# 16| v0_55(Void) = ExitFunction : +# 16| v0_55(Void) = AliasedUse : ~mu0_2 +# 16| v0_56(Void) = ExitFunction : # 29| System.Void InOutRef.Main() # 29| Block 0 # 29| v0_0(Void) = EnterFunction : -# 29| mu0_1(null) = AliasedDefinition : -# 29| mu0_2(null) = UnmodeledDefinition : +# 29| mu0_1() = AliasedDefinition : +# 29| mu0_2() = UnmodeledDefinition : # 31| r0_3(glval) = VariableAddress[a] : # 31| r0_4(Int32) = Constant[0] : # 31| mu0_5(Int32) = Store : &:r0_3, r0_4 # 32| r0_6(glval) = VariableAddress[b] : # 32| r0_7(MyStruct) = NewObj : -# 32| r0_8(glval) = FunctionAddress[MyStruct] : +# 32| r0_8() = FunctionAddress[MyStruct] : # 32| v0_9(Void) = Call : func:r0_8, this:r0_7 -# 32| mu0_10(null) = ^CallSideEffect : ~mu0_2 +# 32| mu0_10() = ^CallSideEffect : ~mu0_2 # 32| r0_11(MyStruct) = Load : &:r0_7, ~mu0_2 # 32| mu0_12(MyStruct) = Store : &:r0_6, r0_11 # 33| r0_13(glval) = VariableAddress[c] : # 33| r0_14(MyClass) = NewObj : -# 33| r0_15(glval) = FunctionAddress[MyClass] : +# 33| r0_15() = FunctionAddress[MyClass] : # 33| v0_16(Void) = Call : func:r0_15, this:r0_14 -# 33| mu0_17(null) = ^CallSideEffect : ~mu0_2 +# 33| mu0_17() = ^CallSideEffect : ~mu0_2 # 33| mu0_18(MyClass) = Store : &:r0_13, r0_14 -# 34| r0_19(glval) = FunctionAddress[F] : +# 34| r0_19() = FunctionAddress[F] : # 34| r0_20(glval) = VariableAddress[a] : # 34| r0_21(glval) = VariableAddress[b] : # 34| r0_22(glval) = VariableAddress[b] : # 34| r0_23(glval) = VariableAddress[c] : # 34| r0_24(glval) = VariableAddress[c] : # 34| v0_25(Void) = Call : func:r0_19, 0:r0_20, 1:r0_21, 2:r0_22, 3:r0_23, 4:r0_24 -# 34| mu0_26(null) = ^CallSideEffect : ~mu0_2 +# 34| mu0_26() = ^CallSideEffect : ~mu0_2 # 36| r0_27(glval) = VariableAddress[x] : # 36| r0_28(glval) = VariableAddress[b] : # 36| r0_29(glval) = FieldAddress[fld] : r0_28 @@ -977,14 +1007,15 @@ inoutref.cs: # 36| mu0_31(Int32) = Store : &:r0_27, r0_30 # 29| v0_32(Void) = ReturnVoid : # 29| v0_33(Void) = UnmodeledUse : mu* -# 29| v0_34(Void) = ExitFunction : +# 29| v0_34(Void) = AliasedUse : ~mu0_2 +# 29| v0_35(Void) = ExitFunction : isexpr.cs: # 8| System.Void IsExpr.Main() # 8| Block 0 # 8| v0_0(Void) = EnterFunction : -# 8| mu0_1(null) = AliasedDefinition : -# 8| mu0_2(null) = UnmodeledDefinition : +# 8| mu0_1() = AliasedDefinition : +# 8| mu0_2() = UnmodeledDefinition : # 10| r0_3(glval) = VariableAddress[obj] : # 10| r0_4(null) = Constant[null] : # 10| r0_5(Is_A) = Convert : r0_4 @@ -1008,7 +1039,8 @@ isexpr.cs: # 8| Block 1 # 8| v1_0(Void) = ReturnVoid : # 8| v1_1(Void) = UnmodeledUse : mu* -# 8| v1_2(Void) = ExitFunction : +# 8| v1_2(Void) = AliasedUse : ~mu0_2 +# 8| v1_3(Void) = ExitFunction : # 13| Block 2 # 13| v2_0(Void) = ConditionalBranch : r0_18 @@ -1046,8 +1078,8 @@ jumps.cs: # 5| System.Void Jumps.Main() # 5| Block 0 # 5| v0_0(Void) = EnterFunction : -# 5| mu0_1(null) = AliasedDefinition : -# 5| mu0_2(null) = UnmodeledDefinition : +# 5| mu0_1() = AliasedDefinition : +# 5| mu0_2() = UnmodeledDefinition : # 7| r0_3(glval) = VariableAddress[i] : # 7| r0_4(Int32) = Constant[1] : # 7| mu0_5(Int32) = Store : &:r0_3, r0_4 @@ -1089,10 +1121,10 @@ jumps.cs: #-----| Goto -> Block 7 # 13| Block 6 -# 13| r6_0(glval) = FunctionAddress[WriteLine] : -# 13| r6_1(String) = StringConstant["BreakAndContinue"] : -# 13| v6_2(Void) = Call : func:r6_0, 0:r6_1 -# 13| mu6_3(null) = ^CallSideEffect : ~mu0_2 +# 13| r6_0() = FunctionAddress[WriteLine] : +# 13| r6_1(String) = StringConstant["BreakAndContinue"] : +# 13| v6_2(Void) = Call : func:r6_0, 0:r6_1 +# 13| mu6_3() = ^CallSideEffect : ~mu0_2 #-----| Goto -> Block 19 # 16| Block 7 @@ -1207,26 +1239,27 @@ jumps.cs: #-----| Goto -> Block 22 # 37| Block 22 -# 37| v22_0(Void) = NoOp : -# 38| r22_1(glval) = FunctionAddress[WriteLine] : -# 38| r22_2(String) = StringConstant["Done"] : -# 38| v22_3(Void) = Call : func:r22_1, 0:r22_2 -# 38| mu22_4(null) = ^CallSideEffect : ~mu0_2 -# 5| v22_5(Void) = ReturnVoid : -# 5| v22_6(Void) = UnmodeledUse : mu* -# 5| v22_7(Void) = ExitFunction : +# 37| v22_0(Void) = NoOp : +# 38| r22_1() = FunctionAddress[WriteLine] : +# 38| r22_2(String) = StringConstant["Done"] : +# 38| v22_3(Void) = Call : func:r22_1, 0:r22_2 +# 38| mu22_4() = ^CallSideEffect : ~mu0_2 +# 5| v22_5(Void) = ReturnVoid : +# 5| v22_6(Void) = UnmodeledUse : mu* +# 5| v22_7(Void) = AliasedUse : ~mu0_2 +# 5| v22_8(Void) = ExitFunction : lock.cs: # 5| System.Void LockTest.A() # 5| Block 0 # 5| v0_0(Void) = EnterFunction : -# 5| mu0_1(null) = AliasedDefinition : -# 5| mu0_2(null) = UnmodeledDefinition : +# 5| mu0_1() = AliasedDefinition : +# 5| mu0_2() = UnmodeledDefinition : # 7| r0_3(glval) = VariableAddress[object] : # 7| r0_4(Object) = NewObj : -# 7| r0_5(glval) = FunctionAddress[Object] : +# 7| r0_5() = FunctionAddress[Object] : # 7| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 7| mu0_7(null) = ^CallSideEffect : ~mu0_2 +# 7| mu0_7() = ^CallSideEffect : ~mu0_2 # 7| mu0_8(Object) = Store : &:r0_3, r0_4 # 8| r0_9(glval) = VariableAddress[#temp8:9] : # 8| r0_10(glval) = VariableAddress[object] : @@ -1235,20 +1268,20 @@ lock.cs: # 8| r0_13(glval) = VariableAddress[#temp8:9] : # 8| r0_14(Boolean) = Constant[false] : # 8| mu0_15(Boolean) = Store : &:r0_13, r0_14 -# 8| r0_16(glval) = FunctionAddress[Enter] : +# 8| r0_16() = FunctionAddress[Enter] : # 8| r0_17(glval) = VariableAddress[#temp8:9] : # 8| r0_18(Object) = Load : &:r0_17, ~mu0_2 # 8| r0_19(glval) = VariableAddress[#temp8:9] : # 8| v0_20(Void) = Call : func:r0_16, 0:r0_18, 1:r0_19 -# 8| mu0_21(null) = ^CallSideEffect : ~mu0_2 -# 10| r0_22(glval) = FunctionAddress[WriteLine] : +# 8| mu0_21() = ^CallSideEffect : ~mu0_2 +# 10| r0_22() = FunctionAddress[WriteLine] : # 10| r0_23(glval) = VariableAddress[object] : # 10| r0_24(Object) = Load : &:r0_23, ~mu0_2 -# 10| r0_25(glval) = FunctionAddress[ToString] : +# 10| r0_25() = FunctionAddress[ToString] : # 10| r0_26(String) = Call : func:r0_25, this:r0_24 -# 10| mu0_27(null) = ^CallSideEffect : ~mu0_2 +# 10| mu0_27() = ^CallSideEffect : ~mu0_2 # 10| v0_28(Void) = Call : func:r0_22, 0:r0_26 -# 10| mu0_29(null) = ^CallSideEffect : ~mu0_2 +# 10| mu0_29() = ^CallSideEffect : ~mu0_2 # 8| r0_30(glval) = VariableAddress[#temp8:9] : # 8| r0_31(Boolean) = Load : &:r0_30, ~mu0_2 # 8| v0_32(Void) = ConditionalBranch : r0_31 @@ -1258,33 +1291,35 @@ lock.cs: # 5| Block 1 # 5| v1_0(Void) = ReturnVoid : # 5| v1_1(Void) = UnmodeledUse : mu* -# 5| v1_2(Void) = ExitFunction : +# 5| v1_2(Void) = AliasedUse : ~mu0_2 +# 5| v1_3(Void) = ExitFunction : # 8| Block 2 -# 8| r2_0(glval) = FunctionAddress[Exit] : +# 8| r2_0() = FunctionAddress[Exit] : # 8| r2_1(glval) = VariableAddress[#temp8:9] : # 8| r2_2(Object) = Load : &:r2_1, ~mu0_2 # 8| v2_3(Void) = Call : func:r2_0, 0:r2_2 -# 8| mu2_4(null) = ^CallSideEffect : ~mu0_2 +# 8| mu2_4() = ^CallSideEffect : ~mu0_2 #-----| Goto -> Block 1 obj_creation.cs: # 7| System.Void ObjCreation.MyClass..ctor() # 7| Block 0 # 7| v0_0(Void) = EnterFunction : -# 7| mu0_1(null) = AliasedDefinition : -# 7| mu0_2(null) = UnmodeledDefinition : +# 7| mu0_1() = AliasedDefinition : +# 7| mu0_2() = UnmodeledDefinition : # 7| r0_3(glval) = InitializeThis : # 8| v0_4(Void) = NoOp : # 7| v0_5(Void) = ReturnVoid : # 7| v0_6(Void) = UnmodeledUse : mu* -# 7| v0_7(Void) = ExitFunction : +# 7| v0_7(Void) = AliasedUse : ~mu0_2 +# 7| v0_8(Void) = ExitFunction : # 11| System.Void ObjCreation.MyClass..ctor(System.Int32) # 11| Block 0 # 11| v0_0(Void) = EnterFunction : -# 11| mu0_1(null) = AliasedDefinition : -# 11| mu0_2(null) = UnmodeledDefinition : +# 11| mu0_1() = AliasedDefinition : +# 11| mu0_2() = UnmodeledDefinition : # 11| r0_3(glval) = InitializeThis : # 11| r0_4(glval) = VariableAddress[_x] : # 11| mu0_5(Int32) = InitializeParameter[_x] : &:r0_4 @@ -1295,37 +1330,39 @@ obj_creation.cs: # 13| mu0_10(Int32) = Store : &:r0_9, r0_7 # 11| v0_11(Void) = ReturnVoid : # 11| v0_12(Void) = UnmodeledUse : mu* -# 11| v0_13(Void) = ExitFunction : +# 11| v0_13(Void) = AliasedUse : ~mu0_2 +# 11| v0_14(Void) = ExitFunction : # 17| System.Void ObjCreation.SomeFun(ObjCreation.MyClass) # 17| Block 0 # 17| v0_0(Void) = EnterFunction : -# 17| mu0_1(null) = AliasedDefinition : -# 17| mu0_2(null) = UnmodeledDefinition : +# 17| mu0_1() = AliasedDefinition : +# 17| mu0_2() = UnmodeledDefinition : # 17| r0_3(glval) = VariableAddress[x] : # 17| mu0_4(MyClass) = InitializeParameter[x] : &:r0_3 # 18| v0_5(Void) = NoOp : # 17| v0_6(Void) = ReturnVoid : # 17| v0_7(Void) = UnmodeledUse : mu* -# 17| v0_8(Void) = ExitFunction : +# 17| v0_8(Void) = AliasedUse : ~mu0_2 +# 17| v0_9(Void) = ExitFunction : # 21| System.Void ObjCreation.Main() # 21| Block 0 # 21| v0_0(Void) = EnterFunction : -# 21| mu0_1(null) = AliasedDefinition : -# 21| mu0_2(null) = UnmodeledDefinition : +# 21| mu0_1() = AliasedDefinition : +# 21| mu0_2() = UnmodeledDefinition : # 23| r0_3(glval) = VariableAddress[obj] : # 23| r0_4(MyClass) = NewObj : -# 23| r0_5(glval) = FunctionAddress[MyClass] : +# 23| r0_5() = FunctionAddress[MyClass] : # 23| r0_6(Int32) = Constant[100] : # 23| v0_7(Void) = Call : func:r0_5, this:r0_4, 0:r0_6 -# 23| mu0_8(null) = ^CallSideEffect : ~mu0_2 +# 23| mu0_8() = ^CallSideEffect : ~mu0_2 # 23| mu0_9(MyClass) = Store : &:r0_3, r0_4 # 24| r0_10(glval) = VariableAddress[obj_initlist] : # 24| r0_11(MyClass) = NewObj : -# 24| r0_12(glval) = FunctionAddress[MyClass] : +# 24| r0_12() = FunctionAddress[MyClass] : # 24| v0_13(Void) = Call : func:r0_12, this:r0_11 -# 24| mu0_14(null) = ^CallSideEffect : ~mu0_2 +# 24| mu0_14() = ^CallSideEffect : ~mu0_2 # 24| r0_15(Int32) = Constant[101] : # 24| r0_16(glval) = FieldAddress[x] : r0_11 # 24| mu0_17(Int32) = Store : &:r0_16, r0_15 @@ -1336,32 +1373,33 @@ obj_creation.cs: # 25| r0_22(glval) = FieldAddress[x] : r0_21 # 25| r0_23(Int32) = Load : &:r0_22, ~mu0_2 # 25| mu0_24(Int32) = Store : &:r0_19, r0_23 -# 27| r0_25(glval) = FunctionAddress[SomeFun] : +# 27| r0_25() = FunctionAddress[SomeFun] : # 27| r0_26(MyClass) = NewObj : -# 27| r0_27(glval) = FunctionAddress[MyClass] : +# 27| r0_27() = FunctionAddress[MyClass] : # 27| r0_28(Int32) = Constant[100] : # 27| v0_29(Void) = Call : func:r0_27, this:r0_26, 0:r0_28 -# 27| mu0_30(null) = ^CallSideEffect : ~mu0_2 +# 27| mu0_30() = ^CallSideEffect : ~mu0_2 # 27| v0_31(Void) = Call : func:r0_25, 0:r0_26 -# 27| mu0_32(null) = ^CallSideEffect : ~mu0_2 +# 27| mu0_32() = ^CallSideEffect : ~mu0_2 # 21| v0_33(Void) = ReturnVoid : # 21| v0_34(Void) = UnmodeledUse : mu* -# 21| v0_35(Void) = ExitFunction : +# 21| v0_35(Void) = AliasedUse : ~mu0_2 +# 21| v0_36(Void) = ExitFunction : pointers.cs: # 3| System.Void Pointers.addone(System.Int32[]) # 3| Block 0 # 3| v0_0(Void) = EnterFunction : -# 3| mu0_1(null) = AliasedDefinition : -# 3| mu0_2(null) = UnmodeledDefinition : +# 3| mu0_1() = AliasedDefinition : +# 3| mu0_2() = UnmodeledDefinition : # 3| r0_3(glval) = VariableAddress[arr] : # 3| mu0_4(Int32[]) = InitializeParameter[arr] : &:r0_3 # 5| r0_5(glval) = VariableAddress[length] : # 5| r0_6(glval) = VariableAddress[arr] : # 5| r0_7(Int32[]) = Load : &:r0_6, ~mu0_2 -# 5| r0_8(glval) = FunctionAddress[get_Length] : +# 5| r0_8() = FunctionAddress[get_Length] : # 5| r0_9(Int32) = Call : func:r0_8, this:r0_7 -# 5| mu0_10(null) = ^CallSideEffect : ~mu0_2 +# 5| mu0_10() = ^CallSideEffect : ~mu0_2 # 5| mu0_11(Int32) = Store : &:r0_5, r0_9 # 6| r0_12(glval) = VariableAddress[b] : # 6| r0_13(glval) = VariableAddress[arr] : @@ -1380,7 +1418,8 @@ pointers.cs: # 3| Block 1 # 3| v1_0(Void) = ReturnVoid : # 3| v1_1(Void) = UnmodeledUse : mu* -# 3| v1_2(Void) = ExitFunction : +# 3| v1_2(Void) = AliasedUse : ~mu0_2 +# 3| v1_3(Void) = ExitFunction : # 9| Block 2 # 9| r2_0(glval) = VariableAddress[i] : @@ -1412,19 +1451,19 @@ pointers.cs: # 25| System.Void Pointers.Main() # 25| Block 0 # 25| v0_0(Void) = EnterFunction : -# 25| mu0_1(null) = AliasedDefinition : -# 25| mu0_2(null) = UnmodeledDefinition : +# 25| mu0_1() = AliasedDefinition : +# 25| mu0_2() = UnmodeledDefinition : # 26| r0_3(glval) = VariableAddress[o] : # 26| r0_4(MyClass) = NewObj : -# 26| r0_5(glval) = FunctionAddress[MyClass] : +# 26| r0_5() = FunctionAddress[MyClass] : # 26| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 26| mu0_7(null) = ^CallSideEffect : ~mu0_2 +# 26| mu0_7() = ^CallSideEffect : ~mu0_2 # 26| mu0_8(MyClass) = Store : &:r0_3, r0_4 # 27| r0_9(glval) = VariableAddress[s] : # 27| r0_10(MyStruct) = NewObj : -# 27| r0_11(glval) = FunctionAddress[MyStruct] : +# 27| r0_11() = FunctionAddress[MyStruct] : # 27| v0_12(Void) = Call : func:r0_11, this:r0_10 -# 27| mu0_13(null) = ^CallSideEffect : ~mu0_2 +# 27| mu0_13() = ^CallSideEffect : ~mu0_2 # 27| r0_14(MyStruct) = Load : &:r0_10, ~mu0_2 # 27| mu0_15(MyStruct) = Store : &:r0_9, r0_14 # 30| r0_16(glval) = VariableAddress[p] : @@ -1468,38 +1507,40 @@ pointers.cs: # 39| r0_54(glval) = PointerAdd[4] : r0_43, r0_53 # 39| r0_55(Int32) = Constant[3] : # 39| mu0_56(Int32) = Store : &:r0_54, r0_55 -# 40| r0_57(glval) = FunctionAddress[addone] : +# 40| r0_57() = FunctionAddress[addone] : # 40| r0_58(glval) = VariableAddress[arr] : # 40| r0_59(Int32[]) = Load : &:r0_58, ~mu0_2 # 40| v0_60(Void) = Call : func:r0_57, 0:r0_59 -# 40| mu0_61(null) = ^CallSideEffect : ~mu0_2 +# 40| mu0_61() = ^CallSideEffect : ~mu0_2 # 25| v0_62(Void) = ReturnVoid : # 25| v0_63(Void) = UnmodeledUse : mu* -# 25| v0_64(Void) = ExitFunction : +# 25| v0_64(Void) = AliasedUse : ~mu0_2 +# 25| v0_65(Void) = ExitFunction : prop.cs: # 7| System.Int32 PropClass.get_Prop() # 7| Block 0 # 7| v0_0(Void) = EnterFunction : -# 7| mu0_1(null) = AliasedDefinition : -# 7| mu0_2(null) = UnmodeledDefinition : +# 7| mu0_1() = AliasedDefinition : +# 7| mu0_2() = UnmodeledDefinition : # 7| r0_3(glval) = InitializeThis : # 9| r0_4(glval) = VariableAddress[#return] : # 9| r0_5(PropClass) = CopyValue : r0_3 -# 9| r0_6(glval) = FunctionAddress[func] : +# 9| r0_6() = FunctionAddress[func] : # 9| r0_7(Int32) = Call : func:r0_6, this:r0_5 -# 9| mu0_8(null) = ^CallSideEffect : ~mu0_2 +# 9| mu0_8() = ^CallSideEffect : ~mu0_2 # 9| mu0_9(Int32) = Store : &:r0_4, r0_7 # 7| r0_10(glval) = VariableAddress[#return] : # 7| v0_11(Void) = ReturnValue : &:r0_10, ~mu0_2 # 7| v0_12(Void) = UnmodeledUse : mu* -# 7| v0_13(Void) = ExitFunction : +# 7| v0_13(Void) = AliasedUse : ~mu0_2 +# 7| v0_14(Void) = ExitFunction : # 12| System.Void PropClass.set_Prop(System.Int32) # 12| Block 0 # 12| v0_0(Void) = EnterFunction : -# 12| mu0_1(null) = AliasedDefinition : -# 12| mu0_2(null) = UnmodeledDefinition : +# 12| mu0_1() = AliasedDefinition : +# 12| mu0_2() = UnmodeledDefinition : # 12| r0_3(glval) = InitializeThis : # 12| r0_4(glval) = VariableAddress[value] : # 12| mu0_5(Int32) = InitializeParameter[value] : &:r0_4 @@ -1509,13 +1550,14 @@ prop.cs: # 14| mu0_9(Int32) = Store : &:r0_8, r0_7 # 12| v0_10(Void) = ReturnVoid : # 12| v0_11(Void) = UnmodeledUse : mu* -# 12| v0_12(Void) = ExitFunction : +# 12| v0_12(Void) = AliasedUse : ~mu0_2 +# 12| v0_13(Void) = ExitFunction : # 18| System.Int32 PropClass.func() # 18| Block 0 # 18| v0_0(Void) = EnterFunction : -# 18| mu0_1(null) = AliasedDefinition : -# 18| mu0_2(null) = UnmodeledDefinition : +# 18| mu0_1() = AliasedDefinition : +# 18| mu0_2() = UnmodeledDefinition : # 18| r0_3(glval) = InitializeThis : # 20| r0_4(glval) = VariableAddress[#return] : # 20| r0_5(Int32) = Constant[0] : @@ -1523,86 +1565,91 @@ prop.cs: # 18| r0_7(glval) = VariableAddress[#return] : # 18| v0_8(Void) = ReturnValue : &:r0_7, ~mu0_2 # 18| v0_9(Void) = UnmodeledUse : mu* -# 18| v0_10(Void) = ExitFunction : +# 18| v0_10(Void) = AliasedUse : ~mu0_2 +# 18| v0_11(Void) = ExitFunction : # 26| System.Void Prog.Main() # 26| Block 0 # 26| v0_0(Void) = EnterFunction : -# 26| mu0_1(null) = AliasedDefinition : -# 26| mu0_2(null) = UnmodeledDefinition : +# 26| mu0_1() = AliasedDefinition : +# 26| mu0_2() = UnmodeledDefinition : # 28| r0_3(glval) = VariableAddress[obj] : # 28| r0_4(PropClass) = NewObj : -# 28| r0_5(glval) = FunctionAddress[PropClass] : +# 28| r0_5() = FunctionAddress[PropClass] : # 28| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 28| mu0_7(null) = ^CallSideEffect : ~mu0_2 +# 28| mu0_7() = ^CallSideEffect : ~mu0_2 # 28| mu0_8(PropClass) = Store : &:r0_3, r0_4 # 29| r0_9(glval) = VariableAddress[obj] : # 29| r0_10(PropClass) = Load : &:r0_9, ~mu0_2 -# 29| r0_11(glval) = FunctionAddress[set_Prop] : +# 29| r0_11() = FunctionAddress[set_Prop] : # 29| r0_12(Int32) = Constant[5] : # 29| v0_13(Void) = Call : func:r0_11, this:r0_10, 0:r0_12 -# 29| mu0_14(null) = ^CallSideEffect : ~mu0_2 +# 29| mu0_14() = ^CallSideEffect : ~mu0_2 # 30| r0_15(glval) = VariableAddress[x] : # 30| r0_16(glval) = VariableAddress[obj] : # 30| r0_17(PropClass) = Load : &:r0_16, ~mu0_2 -# 30| r0_18(glval) = FunctionAddress[get_Prop] : +# 30| r0_18() = FunctionAddress[get_Prop] : # 30| r0_19(Int32) = Call : func:r0_18, this:r0_17 -# 30| mu0_20(null) = ^CallSideEffect : ~mu0_2 +# 30| mu0_20() = ^CallSideEffect : ~mu0_2 # 30| mu0_21(Int32) = Store : &:r0_15, r0_19 # 26| v0_22(Void) = ReturnVoid : # 26| v0_23(Void) = UnmodeledUse : mu* -# 26| v0_24(Void) = ExitFunction : +# 26| v0_24(Void) = AliasedUse : ~mu0_2 +# 26| v0_25(Void) = ExitFunction : simple_call.cs: # 5| System.Int32 test_simple_call.f() # 5| Block 0 # 5| v0_0(Void) = EnterFunction : -# 5| mu0_1(null) = AliasedDefinition : -# 5| mu0_2(null) = UnmodeledDefinition : +# 5| mu0_1() = AliasedDefinition : +# 5| mu0_2() = UnmodeledDefinition : # 7| r0_3(glval) = VariableAddress[#return] : # 7| r0_4(Int32) = Constant[0] : # 7| mu0_5(Int32) = Store : &:r0_3, r0_4 # 5| r0_6(glval) = VariableAddress[#return] : # 5| v0_7(Void) = ReturnValue : &:r0_6, ~mu0_2 # 5| v0_8(Void) = UnmodeledUse : mu* -# 5| v0_9(Void) = ExitFunction : +# 5| v0_9(Void) = AliasedUse : ~mu0_2 +# 5| v0_10(Void) = ExitFunction : # 10| System.Int32 test_simple_call.g() # 10| Block 0 # 10| v0_0(Void) = EnterFunction : -# 10| mu0_1(null) = AliasedDefinition : -# 10| mu0_2(null) = UnmodeledDefinition : +# 10| mu0_1() = AliasedDefinition : +# 10| mu0_2() = UnmodeledDefinition : # 10| r0_3(glval) = InitializeThis : # 12| r0_4(glval) = VariableAddress[#return] : -# 12| r0_5(glval) = FunctionAddress[f] : +# 12| r0_5() = FunctionAddress[f] : # 12| r0_6(Int32) = Call : func:r0_5 -# 12| mu0_7(null) = ^CallSideEffect : ~mu0_2 +# 12| mu0_7() = ^CallSideEffect : ~mu0_2 # 12| mu0_8(Int32) = Store : &:r0_4, r0_6 # 10| r0_9(glval) = VariableAddress[#return] : # 10| v0_10(Void) = ReturnValue : &:r0_9, ~mu0_2 # 10| v0_11(Void) = UnmodeledUse : mu* -# 10| v0_12(Void) = ExitFunction : +# 10| v0_12(Void) = AliasedUse : ~mu0_2 +# 10| v0_13(Void) = ExitFunction : simple_function.cs: # 5| System.Int32 test_simple_function.f() # 5| Block 0 # 5| v0_0(Void) = EnterFunction : -# 5| mu0_1(null) = AliasedDefinition : -# 5| mu0_2(null) = UnmodeledDefinition : +# 5| mu0_1() = AliasedDefinition : +# 5| mu0_2() = UnmodeledDefinition : # 7| r0_3(glval) = VariableAddress[#return] : # 7| r0_4(Int32) = Constant[0] : # 7| mu0_5(Int32) = Store : &:r0_3, r0_4 # 5| r0_6(glval) = VariableAddress[#return] : # 5| v0_7(Void) = ReturnValue : &:r0_6, ~mu0_2 # 5| v0_8(Void) = UnmodeledUse : mu* -# 5| v0_9(Void) = ExitFunction : +# 5| v0_9(Void) = AliasedUse : ~mu0_2 +# 5| v0_10(Void) = ExitFunction : stmts.cs: # 5| System.Int32 test_stmts.ifStmt(System.Int32) # 5| Block 0 # 5| v0_0(Void) = EnterFunction : -# 5| mu0_1(null) = AliasedDefinition : -# 5| mu0_2(null) = UnmodeledDefinition : +# 5| mu0_1() = AliasedDefinition : +# 5| mu0_2() = UnmodeledDefinition : # 5| r0_3(glval) = VariableAddress[x] : # 5| mu0_4(Int32) = InitializeParameter[x] : &:r0_3 # 7| r0_5(glval) = VariableAddress[x] : @@ -1617,7 +1664,8 @@ stmts.cs: # 5| r1_0(glval) = VariableAddress[#return] : # 5| v1_1(Void) = ReturnValue : &:r1_0, ~mu0_2 # 5| v1_2(Void) = UnmodeledUse : mu* -# 5| v1_3(Void) = ExitFunction : +# 5| v1_3(Void) = AliasedUse : ~mu0_2 +# 5| v1_4(Void) = ExitFunction : # 10| Block 2 # 10| r2_0(glval) = VariableAddress[#return] : @@ -1634,8 +1682,8 @@ stmts.cs: # 13| System.Void test_stmts.whileStmt(System.Int32) # 13| Block 0 # 13| v0_0(Void) = EnterFunction : -# 13| mu0_1(null) = AliasedDefinition : -# 13| mu0_2(null) = UnmodeledDefinition : +# 13| mu0_1() = AliasedDefinition : +# 13| mu0_2() = UnmodeledDefinition : # 13| r0_3(glval) = VariableAddress[x] : # 13| mu0_4(Int32) = InitializeParameter[x] : &:r0_3 # 15| r0_5(glval) = VariableAddress[i] : @@ -1646,7 +1694,8 @@ stmts.cs: # 13| Block 1 # 13| v1_0(Void) = ReturnVoid : # 13| v1_1(Void) = UnmodeledUse : mu* -# 13| v1_2(Void) = ExitFunction : +# 13| v1_2(Void) = AliasedUse : ~mu0_2 +# 13| v1_3(Void) = ExitFunction : # 16| Block 2 # 16| r2_0(glval) = VariableAddress[i] : @@ -1669,13 +1718,13 @@ stmts.cs: # 22| System.Int32 test_stmts.switchStmt() # 22| Block 0 # 22| v0_0(Void) = EnterFunction : -# 22| mu0_1(null) = AliasedDefinition : -# 22| mu0_2(null) = UnmodeledDefinition : +# 22| mu0_1() = AliasedDefinition : +# 22| mu0_2() = UnmodeledDefinition : # 24| r0_3(glval) = VariableAddress[caseSwitch] : # 24| r0_4(Object) = NewObj : -# 24| r0_5(glval) = FunctionAddress[Object] : +# 24| r0_5() = FunctionAddress[Object] : # 24| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 24| mu0_7(null) = ^CallSideEffect : ~mu0_2 +# 24| mu0_7() = ^CallSideEffect : ~mu0_2 # 24| mu0_8(Object) = Store : &:r0_3, r0_4 # 25| r0_9(glval) = VariableAddress[select] : # 25| r0_10(Int32) = Constant[0] : @@ -1693,7 +1742,8 @@ stmts.cs: # 22| r1_0(glval) = VariableAddress[#return] : # 22| v1_1(Void) = ReturnValue : &:r1_0, ~mu0_2 # 22| v1_2(Void) = UnmodeledUse : mu* -# 22| v1_3(Void) = ExitFunction : +# 22| v1_3(Void) = AliasedUse : ~mu0_2 +# 22| v1_4(Void) = ExitFunction : # 29| Block 2 # 29| v2_0(Void) = NoOp : @@ -1735,8 +1785,8 @@ stmts.cs: # 46| System.Void test_stmts.tryCatchFinally() # 46| Block 0 # 46| v0_0(Void) = EnterFunction : -# 46| mu0_1(null) = AliasedDefinition : -# 46| mu0_2(null) = UnmodeledDefinition : +# 46| mu0_1() = AliasedDefinition : +# 46| mu0_2() = UnmodeledDefinition : # 48| r0_3(glval) = VariableAddress[x] : # 48| r0_4(Int32) = Constant[5] : # 48| mu0_5(Int32) = Store : &:r0_3, r0_4 @@ -1750,7 +1800,8 @@ stmts.cs: # 46| Block 1 # 46| v1_0(Void) = UnmodeledUse : mu* -# 46| v1_1(Void) = ExitFunction : +# 46| v1_1(Void) = AliasedUse : ~mu0_2 +# 46| v1_2(Void) = ExitFunction : # 46| Block 2 # 46| v2_0(Void) = Unwind : @@ -1759,9 +1810,9 @@ stmts.cs: # 52| Block 3 # 52| r3_0(glval) = VariableAddress[#throw52:17] : # 52| r3_1(Exception) = NewObj : -# 52| r3_2(glval) = FunctionAddress[Exception] : +# 52| r3_2() = FunctionAddress[Exception] : # 52| v3_3(Void) = Call : func:r3_2, this:r3_1 -# 52| mu3_4(null) = ^CallSideEffect : ~mu0_2 +# 52| mu3_4() = ^CallSideEffect : ~mu0_2 # 52| mu3_5(Exception) = Store : &:r3_0, r3_1 # 52| v3_6(Void) = ThrowValue : &:r3_0, ~mu0_2 #-----| Exception -> Block 6 @@ -1800,8 +1851,8 @@ stmts.cs: # 69| System.Void test_stmts.forStmt() # 69| Block 0 # 69| v0_0(Void) = EnterFunction : -# 69| mu0_1(null) = AliasedDefinition : -# 69| mu0_2(null) = UnmodeledDefinition : +# 69| mu0_1() = AliasedDefinition : +# 69| mu0_2() = UnmodeledDefinition : # 71| r0_3(glval) = VariableAddress[x] : # 71| r0_4(Int32) = Constant[0] : # 71| mu0_5(Int32) = Store : &:r0_3, r0_4 @@ -1816,7 +1867,8 @@ stmts.cs: # 69| Block 1 # 69| v1_0(Void) = ReturnVoid : # 69| v1_1(Void) = UnmodeledUse : mu* -# 69| v1_2(Void) = ExitFunction : +# 69| v1_2(Void) = AliasedUse : ~mu0_2 +# 69| v1_3(Void) = ExitFunction : # 72| Block 2 # 72| r2_0(glval) = VariableAddress[i] : @@ -1883,8 +1935,8 @@ stmts.cs: # 89| System.Void test_stmts.doWhile() # 89| Block 0 # 89| v0_0(Void) = EnterFunction : -# 89| mu0_1(null) = AliasedDefinition : -# 89| mu0_2(null) = UnmodeledDefinition : +# 89| mu0_1() = AliasedDefinition : +# 89| mu0_2() = UnmodeledDefinition : # 91| r0_3(glval) = VariableAddress[x] : # 91| r0_4(Int32) = Constant[0] : # 91| mu0_5(Int32) = Store : &:r0_3, r0_4 @@ -1893,7 +1945,8 @@ stmts.cs: # 89| Block 1 # 89| v1_0(Void) = ReturnVoid : # 89| v1_1(Void) = UnmodeledUse : mu* -# 89| v1_2(Void) = ExitFunction : +# 89| v1_2(Void) = AliasedUse : ~mu0_2 +# 89| v1_3(Void) = ExitFunction : # 94| Block 2 # 94| r2_0(glval) = VariableAddress[x] : @@ -1913,8 +1966,8 @@ stmts.cs: # 99| System.Void test_stmts.checkedUnchecked() # 99| Block 0 # 99| v0_0(Void) = EnterFunction : -# 99| mu0_1(null) = AliasedDefinition : -# 99| mu0_2(null) = UnmodeledDefinition : +# 99| mu0_1() = AliasedDefinition : +# 99| mu0_2() = UnmodeledDefinition : # 101| r0_3(glval) = VariableAddress[num] : # 101| r0_4(Int32) = Constant[2147483647] : # 101| r0_5(Int32) = Load : &:r0_4, ~mu0_2 @@ -1933,90 +1986,95 @@ stmts.cs: # 108| mu0_18(Int32) = Store : &:r0_17, r0_16 # 99| v0_19(Void) = ReturnVoid : # 99| v0_20(Void) = UnmodeledUse : mu* -# 99| v0_21(Void) = ExitFunction : +# 99| v0_21(Void) = AliasedUse : ~mu0_2 +# 99| v0_22(Void) = ExitFunction : using.cs: # 7| System.Void UsingStmt.MyDisposable..ctor() # 7| Block 0 # 7| v0_0(Void) = EnterFunction : -# 7| mu0_1(null) = AliasedDefinition : -# 7| mu0_2(null) = UnmodeledDefinition : +# 7| mu0_1() = AliasedDefinition : +# 7| mu0_2() = UnmodeledDefinition : # 7| r0_3(glval) = InitializeThis : # 7| v0_4(Void) = NoOp : # 7| v0_5(Void) = ReturnVoid : # 7| v0_6(Void) = UnmodeledUse : mu* -# 7| v0_7(Void) = ExitFunction : +# 7| v0_7(Void) = AliasedUse : ~mu0_2 +# 7| v0_8(Void) = ExitFunction : # 8| System.Void UsingStmt.MyDisposable.DoSomething() # 8| Block 0 # 8| v0_0(Void) = EnterFunction : -# 8| mu0_1(null) = AliasedDefinition : -# 8| mu0_2(null) = UnmodeledDefinition : +# 8| mu0_1() = AliasedDefinition : +# 8| mu0_2() = UnmodeledDefinition : # 8| r0_3(glval) = InitializeThis : # 8| v0_4(Void) = NoOp : # 8| v0_5(Void) = ReturnVoid : # 8| v0_6(Void) = UnmodeledUse : mu* -# 8| v0_7(Void) = ExitFunction : +# 8| v0_7(Void) = AliasedUse : ~mu0_2 +# 8| v0_8(Void) = ExitFunction : # 9| System.Void UsingStmt.MyDisposable.Dispose() # 9| Block 0 # 9| v0_0(Void) = EnterFunction : -# 9| mu0_1(null) = AliasedDefinition : -# 9| mu0_2(null) = UnmodeledDefinition : +# 9| mu0_1() = AliasedDefinition : +# 9| mu0_2() = UnmodeledDefinition : # 9| r0_3(glval) = InitializeThis : # 9| v0_4(Void) = NoOp : # 9| v0_5(Void) = ReturnVoid : # 9| v0_6(Void) = UnmodeledUse : mu* -# 9| v0_7(Void) = ExitFunction : +# 9| v0_7(Void) = AliasedUse : ~mu0_2 +# 9| v0_8(Void) = ExitFunction : # 12| System.Void UsingStmt.Main() # 12| Block 0 # 12| v0_0(Void) = EnterFunction : -# 12| mu0_1(null) = AliasedDefinition : -# 12| mu0_2(null) = UnmodeledDefinition : +# 12| mu0_1() = AliasedDefinition : +# 12| mu0_2() = UnmodeledDefinition : # 14| r0_3(glval) = VariableAddress[o1] : # 14| r0_4(MyDisposable) = NewObj : -# 14| r0_5(glval) = FunctionAddress[MyDisposable] : +# 14| r0_5() = FunctionAddress[MyDisposable] : # 14| v0_6(Void) = Call : func:r0_5, this:r0_4 -# 14| mu0_7(null) = ^CallSideEffect : ~mu0_2 +# 14| mu0_7() = ^CallSideEffect : ~mu0_2 # 14| mu0_8(MyDisposable) = Store : &:r0_3, r0_4 # 16| r0_9(glval) = VariableAddress[o1] : # 16| r0_10(MyDisposable) = Load : &:r0_9, ~mu0_2 -# 16| r0_11(glval) = FunctionAddress[DoSomething] : +# 16| r0_11() = FunctionAddress[DoSomething] : # 16| v0_12(Void) = Call : func:r0_11, this:r0_10 -# 16| mu0_13(null) = ^CallSideEffect : ~mu0_2 +# 16| mu0_13() = ^CallSideEffect : ~mu0_2 # 19| r0_14(glval) = VariableAddress[o2] : # 19| r0_15(MyDisposable) = NewObj : -# 19| r0_16(glval) = FunctionAddress[MyDisposable] : +# 19| r0_16() = FunctionAddress[MyDisposable] : # 19| v0_17(Void) = Call : func:r0_16, this:r0_15 -# 19| mu0_18(null) = ^CallSideEffect : ~mu0_2 +# 19| mu0_18() = ^CallSideEffect : ~mu0_2 # 19| mu0_19(MyDisposable) = Store : &:r0_14, r0_15 # 22| r0_20(glval) = VariableAddress[o2] : # 22| r0_21(MyDisposable) = Load : &:r0_20, ~mu0_2 -# 22| r0_22(glval) = FunctionAddress[DoSomething] : +# 22| r0_22() = FunctionAddress[DoSomething] : # 22| v0_23(Void) = Call : func:r0_22, this:r0_21 -# 22| mu0_24(null) = ^CallSideEffect : ~mu0_2 +# 22| mu0_24() = ^CallSideEffect : ~mu0_2 # 25| r0_25(glval) = VariableAddress[o3] : # 25| r0_26(MyDisposable) = NewObj : -# 25| r0_27(glval) = FunctionAddress[MyDisposable] : +# 25| r0_27() = FunctionAddress[MyDisposable] : # 25| v0_28(Void) = Call : func:r0_27, this:r0_26 -# 25| mu0_29(null) = ^CallSideEffect : ~mu0_2 +# 25| mu0_29() = ^CallSideEffect : ~mu0_2 # 25| mu0_30(MyDisposable) = Store : &:r0_25, r0_26 # 26| r0_31(glval) = VariableAddress[o3] : # 26| r0_32(MyDisposable) = Load : &:r0_31, ~mu0_2 -# 26| r0_33(glval) = FunctionAddress[DoSomething] : +# 26| r0_33() = FunctionAddress[DoSomething] : # 26| v0_34(Void) = Call : func:r0_33, this:r0_32 -# 26| mu0_35(null) = ^CallSideEffect : ~mu0_2 +# 26| mu0_35() = ^CallSideEffect : ~mu0_2 # 12| v0_36(Void) = ReturnVoid : # 12| v0_37(Void) = UnmodeledUse : mu* -# 12| v0_38(Void) = ExitFunction : +# 12| v0_38(Void) = AliasedUse : ~mu0_2 +# 12| v0_39(Void) = ExitFunction : variables.cs: # 5| System.Void test_variables.f() # 5| Block 0 # 5| v0_0(Void) = EnterFunction : -# 5| mu0_1(null) = AliasedDefinition : -# 5| mu0_2(null) = UnmodeledDefinition : +# 5| mu0_1() = AliasedDefinition : +# 5| mu0_2() = UnmodeledDefinition : # 7| r0_3(glval) = VariableAddress[x] : # 7| mu0_4(Int32) = Uninitialized[x] : &:r0_3 # 7| r0_5(glval) = VariableAddress[y] : @@ -2035,4 +2093,5 @@ variables.cs: # 10| mu0_18(Int32) = Store : &:r0_15, r0_17 # 5| v0_19(Void) = ReturnVoid : # 5| v0_20(Void) = UnmodeledUse : mu* -# 5| v0_21(Void) = ExitFunction : +# 5| v0_21(Void) = AliasedUse : ~mu0_2 +# 5| v0_22(Void) = ExitFunction : diff --git a/csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.expected b/csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.expected index 012ff53039b..c709a73f5e3 100644 --- a/csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.expected +++ b/csharp/ql/test/library-tests/ir/ir/raw_ir_sanity.expected @@ -14,3 +14,7 @@ containsLoopOfForwardEdges lostReachability backEdgeCountMismatch useNotDominatedByDefinition +missingCanonicalLanguageType +multipleCanonicalLanguageTypes +missingIRType +multipleIRTypes diff --git a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected b/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected index 012ff53039b..c709a73f5e3 100644 --- a/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected +++ b/csharp/ql/test/library-tests/ir/ir/unaliased_ssa_sanity.expected @@ -14,3 +14,7 @@ containsLoopOfForwardEdges lostReachability backEdgeCountMismatch useNotDominatedByDefinition +missingCanonicalLanguageType +multipleCanonicalLanguageTypes +missingIRType +multipleIRTypes diff --git a/csharp/ql/test/query-tests/API Abuse/NoDisposeCallOnLocalIDisposable/NoDisposeCallOnLocalIDisposable.cs b/csharp/ql/test/query-tests/API Abuse/NoDisposeCallOnLocalIDisposable/NoDisposeCallOnLocalIDisposable.cs index 45caec0022d..82a33086144 100644 --- a/csharp/ql/test/query-tests/API Abuse/NoDisposeCallOnLocalIDisposable/NoDisposeCallOnLocalIDisposable.cs +++ b/csharp/ql/test/query-tests/API Abuse/NoDisposeCallOnLocalIDisposable/NoDisposeCallOnLocalIDisposable.cs @@ -83,6 +83,11 @@ class Test // GOOD: Disposed automatically. using var c2 = new Timer(TimerProc); + // GOOD: ownership taken via ?? + StringReader source = null; + using(XmlReader.Create(source ?? new StringReader("xml"), null)) + ; + return null; } diff --git a/csharp/ql/test/query-tests/Security Features/CWE-091/XMLInjection/Test.cs b/csharp/ql/test/query-tests/Security Features/CWE-091/XMLInjection/Test.cs new file mode 100644 index 00000000000..af89b7d7328 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-091/XMLInjection/Test.cs @@ -0,0 +1,37 @@ +// semmle-extractor-options: /r:System.Private.Xml.dll /r:System.Xml.dll /r:System.Xml.ReaderWriter.dll /r:System.Runtime.Extensions.dll /r:System.Collections.Specialized.dll ${testdir}/../../../../resources/stubs/System.Web.cs + +using System; +using System.Security; +using System.Web; +using System.Xml; + +public class XMLInjectionHandler : IHttpHandler { + public void ProcessRequest(HttpContext ctx) { + string employeeName = ctx.Request.QueryString["employeeName"]; + + using (XmlWriter writer = XmlWriter.Create("employees.xml")) + { + writer.WriteStartDocument(); + + // BAD: Insert user input directly into XML + writer.WriteRaw("" + employeeName + ""); + + // GOOD: Escape user input before inserting into string + writer.WriteRaw("" + SecurityElement.Escape(employeeName) + ""); + + // GOOD: Use standard API, which automatically encodes values + writer.WriteStartElement("Employee"); + writer.WriteElementString("Name", employeeName); + writer.WriteEndElement(); + + writer.WriteEndElement(); + writer.WriteEndDocument(); + } + } + + public bool IsReusable { + get { + return true; + } + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-091/XMLInjection/XMLInjection.expected b/csharp/ql/test/query-tests/Security Features/CWE-091/XMLInjection/XMLInjection.expected new file mode 100644 index 00000000000..a4c6c87daa0 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-091/XMLInjection/XMLInjection.expected @@ -0,0 +1 @@ +| Test.cs:17:25:17:80 | ... + ... | $@ flows to here and is inserted as XML. | Test.cs:10:27:10:49 | access to property QueryString | User-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-091/XMLInjection/XMLInjection.qlref b/csharp/ql/test/query-tests/Security Features/CWE-091/XMLInjection/XMLInjection.qlref new file mode 100644 index 00000000000..e3b1776a3fb --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-091/XMLInjection/XMLInjection.qlref @@ -0,0 +1 @@ +Security Features/CWE-091/XMLInjection.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected b/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected new file mode 100644 index 00000000000..9cd34259ba1 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.expected @@ -0,0 +1 @@ +| Test.cs:12:36:12:46 | access to local variable libraryName | $@ flows to here and is used as the path to dynamically load an assembly. | Test.cs:9:26:9:48 | access to property QueryString | User-provided value | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.qlref b/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.qlref new file mode 100644 index 00000000000..b1cd9fb617f --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/AssemblyPathInjection.qlref @@ -0,0 +1 @@ +Security Features/CWE-114/AssemblyPathInjection.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/Test.cs b/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/Test.cs new file mode 100644 index 00000000000..07493f3d773 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-114/AssemblyPathInjection/Test.cs @@ -0,0 +1,23 @@ +// semmle-extractor-options: /r:System.Collections.Specialized.dll ${testdir}/../../../../resources/stubs/System.Web.cs + +using System; +using System.Web; +using System.Reflection; + +public class DLLInjectionHandler : IHttpHandler { + public void ProcessRequest(HttpContext ctx) { + string libraryName = ctx.Request.QueryString["libraryName"]; + + // BAD: Load DLL based on user input + var badDLL = Assembly.LoadFile(libraryName); + + // GOOD: Load DLL using fixed string + var goodDLL = Assembly.LoadFile(@"C:\visual studio 2012\Projects\ConsoleApplication1\ConsoleApplication1\DLL.dll"); + } + + public bool IsReusable { + get { + return true; + } + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs new file mode 100644 index 00000000000..9d58f2df62e --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs @@ -0,0 +1,117 @@ +// semmle-extractor-options: /r:System.IO.FileSystem.dll /r:System.Security.Cryptography.Primitives.dll /r:System.Security.Cryptography.Csp.dll /r:System.Security.Cryptography.Algorithms.dll +using System; +using System.IO; +using System.Text; +using System.Security.Cryptography; +using Windows.Security.Cryptography; +using Windows.Security.Cryptography.Core; + +namespace HardcodedSymmetricEncryptionKey +{ + class Program + { + static void Main(string[] args) + { + var a = new AesCryptoServiceProvider(); + + // BAD: explicit key assignment, hard-coded value + a.Key = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 }; + + var b = new AesCryptoServiceProvider() + { + // BAD: explicit key assignment, hard-coded value + Key = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 } + }; + + var c = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 }; + var d = c; + + var byteArrayFromString = Encoding.UTF8.GetBytes("Hello, world: here is a very bad way to create a key"); + + // BAD: key assignment via variable, from hard-coded value + a.Key = d; + + // GOOD (not really, but better than hard coding) + a.Key = File.ReadAllBytes("secret.key"); + + var cp = CreateProvider(d); + + var iv = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 }; + + // BAD: hard-coded key passed to Encrypt [NOT DETECTED] + var ct = Encrypt("Test string here", c, iv); + + // BAD: hard-coded key converted from string and passed to Encrypt [NOT DETECTED] + var ct1 = Encrypt("Test string here", byteArrayFromString, iv); + + // GOOD (this function hashes password) + var de = DecryptWithPassword(ct, c, iv); + + // BAD [NOT DETECTED] + CreateCryptographicKey(null, byteArrayFromString); + + // GOOD + CreateCryptographicKey(null, File.ReadAllBytes("secret.key")); + } + + public static string DecryptWithPassword(byte[] cipherText, byte[] password, byte[] IV) + { + byte[] rawPlaintext; + var salt = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 }; + using (Rfc2898DeriveBytes k1 = new Rfc2898DeriveBytes(password, salt, 100)) + { + using (Aes aes = new AesManaged()) + { + using (MemoryStream ms = new MemoryStream()) + { + // GOOD: for this test, the flow through Rfc2898DeriveBytes should be considered GOOD. + // GOOD: Use of hard-coded password for Rfc2898DeriveBytes is found by Semmle CWE-798: HardcodedCredentials.ql + using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(k1.GetBytes(16), IV), CryptoStreamMode.Write)) + { + cs.Write(cipherText, 0, cipherText.Length); + } + rawPlaintext = ms.ToArray(); + } + + return Encoding.Unicode.GetString(rawPlaintext); + } + } + } + + static SymmetricAlgorithm CreateProvider(byte[] key) + { + return new AesManaged() + { + // BAD: assignment from parameter + Key = key + }; + } + + public static byte[] Encrypt(string plaintext, byte[] key, byte[] IV) + { + byte[] rawPlaintext = Encoding.Unicode.GetBytes("Test string here"); + byte[] cipherText = null; + using (Aes aes = new AesManaged()) + { + using (MemoryStream ms = new MemoryStream()) + { + // BAD: flow of hardcoded key to CreateEncryptor constructor + using (CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(key, IV), CryptoStreamMode.Write)) + { + cs.Write(rawPlaintext, 0, rawPlaintext.Length); + } + + cipherText = ms.ToArray(); + } + + return cipherText; + } + } + + static CryptographicKey CreateCryptographicKey(SymmetricKeyAlgorithmProvider p, byte[] bytes) + { + var buffer = CryptographicBuffer.CreateFromByteArray(bytes); + return p.CreateSymmetricKey(buffer); + } + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected new file mode 100644 index 00000000000..29a4889da65 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected @@ -0,0 +1,6 @@ +| HardcodedSymmetricEncryptionKey.cs:18:21:18:97 | array creation of type Byte[] | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:18:21:18:97 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:23:23:23:99 | array creation of type Byte[] | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:23:23:23:99 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:32:21:32:21 | access to local variable d | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:26:21:26:97 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:86:23:86:25 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:26:21:26:97 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:99:87:99:89 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:26:21:26:97 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:99:87:99:89 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:29:62:29:115 | "Hello, world: here is a very bad way to create a key" | key | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.qlref b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.qlref new file mode 100644 index 00000000000..a2d565b3295 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.qlref @@ -0,0 +1 @@ +Security Features/CWE-321/HardcodedEncryptionKey.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/Stubs.cs b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/Stubs.cs new file mode 100644 index 00000000000..3263367dbb3 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/Stubs.cs @@ -0,0 +1,22 @@ +namespace Windows.Security.Cryptography +{ + public static class CryptographicBuffer + { + public static Windows.Storage.Streams.IBuffer CreateFromByteArray(byte[] value) => throw null; + } +} + +namespace Windows.Storage.Streams +{ + public interface IBuffer { } +} + +namespace Windows.Security.Cryptography.Core +{ + public sealed class CryptographicKey { } + + public sealed class SymmetricKeyAlgorithmProvider + { + public CryptographicKey CreateSymmetricKey(Windows.Storage.Streams.IBuffer keyMaterial) => throw null; + } +} \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/InsecureSQLConnection.cs b/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/InsecureSQLConnection.cs new file mode 100644 index 00000000000..c57499a14ce --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/InsecureSQLConnection.cs @@ -0,0 +1,59 @@ +using System.Data.SqlClient; + +namespace InsecureSQLConnection +{ + public class Class1 + { + public void StringInConstructor() + { + SqlConnection conn = new SqlConnection("Encrypt=true"); + } + + public void StringInProperty() + { + SqlConnection conn = new SqlConnection(); + conn.ConnectionString = "Encrypt=true"; + + } + + public void StringInBuilder() + { + SqlConnectionStringBuilder conBuilder = new SqlConnectionStringBuilder(); + conBuilder.Encrypt = true; + SqlConnection conn = new SqlConnection(conBuilder.ToString()); + } + + public void StringInBuilderProperty() + { + SqlConnectionStringBuilder conBuilder = new SqlConnectionStringBuilder(); + conBuilder.Encrypt = true; + SqlConnection conn = new SqlConnection(); + conn.ConnectionString = conBuilder.ToString(); + + } + + public void TriggerThis() + { + // BAD, Encrypt not specified [NOT DETECTED] + SqlConnection conn = new SqlConnection("Server=myServerName\\myInstanceName;Database=myDataBase;User Id=myUsername;"); + } + + void Test4() + { + string connectString = + "Server=1.2.3.4;Database=Anything;UID=ab;Pwd=cd"; + // BAD, Encrypt not specified [NOT DETECTED] + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectString); + var conn = new SqlConnection(builder.ConnectionString); + } + + void Test5() + { + string connectString = + "Server=1.2.3.4;Database=Anything;UID=ab;Pwd=cd;Encrypt=false"; + // BAD, Encrypt set to false [NOT DETECTED] + SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectString); + var conn = new SqlConnection(builder.ConnectionString); + } + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/InsecureSQLConnection.expected b/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/InsecureSQLConnection.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/InsecureSQLConnection.qlref b/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/InsecureSQLConnection.qlref new file mode 100644 index 00000000000..e3b1776a3fb --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/InsecureSQLConnection.qlref @@ -0,0 +1 @@ +Security Features/CWE-091/XMLInjection.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/stubs.cs b/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/stubs.cs new file mode 100644 index 00000000000..26461fc0224 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-327/InsecureSQLConnection/stubs.cs @@ -0,0 +1,71 @@ +// This file contains auto-generated code. + +namespace System +{ + namespace Data + { + // Generated from `System.Data.IDbConnection` in `System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089` + public interface IDbConnection : System.IDisposable + { + string ConnectionString { get; set; } + } + + namespace Common + { + // Generated from `System.Data.Common.DbConnectionStringBuilder` in `System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089` + public class DbConnectionStringBuilder : System.Collections.IEnumerable, System.Collections.IDictionary, System.Collections.ICollection + { + System.Collections.IDictionaryEnumerator System.Collections.IDictionary.GetEnumerator() => throw null; + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null; + bool System.Collections.ICollection.IsSynchronized { get => throw null; } + bool System.Collections.IDictionary.Contains(object keyword) => throw null; + object System.Collections.ICollection.SyncRoot { get => throw null; } + public object this[object keyword] { get => throw null; set => throw null; } + public bool IsReadOnly { get => throw null; } + public override string ToString() => throw null; + public string ConnectionString { get => throw null; set => throw null; } + public virtual System.Collections.ICollection Keys { get => throw null; } + public virtual System.Collections.ICollection Values { get => throw null; } + public virtual bool IsFixedSize { get => throw null; } + public virtual int Count { get => throw null; } + public virtual void Clear() => throw null; + void System.Collections.ICollection.CopyTo(System.Array array, int index) => throw null; + void System.Collections.IDictionary.Add(object keyword, object value) => throw null; + void System.Collections.IDictionary.Remove(object keyword) => throw null; + public void Dispose() => throw null; + } + + // Generated from `System.Data.Common.DbConnection` in `System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089` + abstract public class DbConnection : System.IDisposable, System.Data.IDbConnection + { + public abstract string ConnectionString { get; set; } + public void Dispose() => throw null; + } + + } + namespace SqlClient + { + // Generated from `System.Data.SqlClient.SqlConnectionStringBuilder` in `System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089` + public class SqlConnectionStringBuilder : System.Data.Common.DbConnectionStringBuilder + { + public SqlConnectionStringBuilder() => throw null; + public SqlConnectionStringBuilder(string connectionString) => throw null; + public bool Encrypt { get => throw null; set => throw null; } + public override System.Collections.ICollection Keys { get => throw null; } + public override System.Collections.ICollection Values { get => throw null; } + public override bool IsFixedSize { get => throw null; } + public override void Clear() => throw null; + } + + // Generated from `System.Data.SqlClient.SqlConnection` in `System.Data, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089` + public class SqlConnection : System.Data.Common.DbConnection, System.ICloneable + { + object System.ICloneable.Clone() => throw null; + public SqlConnection() => throw null; + public SqlConnection(string connectionString) => throw null; + public override string ConnectionString { get => throw null; set => throw null; } + } + + } + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegate.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegate.cs new file mode 100644 index 00000000000..93dede10b15 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegate.cs @@ -0,0 +1,22 @@ +using System; +using System.Linq.Expressions; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; + +class DeserializedDelegate +{ + delegate int D(); + + public static void M(FileStream fs) + { + var formatter = new BinaryFormatter(); + // BAD + var a = (Func)formatter.Deserialize(fs); + // BAD + var b = (Expression>)formatter.Deserialize(fs); + // BAD + var c = (D)formatter.Deserialize(fs); + // GOOD + var d = (int)formatter.Deserialize(fs); + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegate.expected b/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegate.expected new file mode 100644 index 00000000000..79cc1962888 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegate.expected @@ -0,0 +1,4 @@ +| DeserializedDelegate.cs:14:28:14:52 | call to method Deserialize | Deserialization of delegate type. | +| DeserializedDelegate.cs:16:40:16:64 | call to method Deserialize | Deserialization of delegate type. | +| DeserializedDelegate.cs:18:20:18:44 | call to method Deserialize | Deserialization of delegate type. | +| DeserializedDelegateBad.cs:11:28:11:52 | call to method Deserialize | Deserialization of delegate type. | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegate.qlref b/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegate.qlref new file mode 100644 index 00000000000..913c61338c4 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegate.qlref @@ -0,0 +1 @@ +Security Features/CWE-502/DeserializedDelegate.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegateBad.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegateBad.cs new file mode 100644 index 00000000000..12b2dc76987 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/DeserializedDelegateBad.cs @@ -0,0 +1,14 @@ +using System; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; + +class Bad +{ + public static int InvokeSerialized(FileStream fs) + { + var formatter = new BinaryFormatter(); + // BAD + var f = (Func)formatter.Deserialize(fs); + return f(); + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/ExtractorOptions.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/ExtractorOptions.cs new file mode 100644 index 00000000000..7b252c937e9 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/DeserializedDelegate/ExtractorOptions.cs @@ -0,0 +1 @@ +// semmle-extractor-options: /r:System.Runtime.Serialization.Formatters.dll /r:System.IO.FileSystem.dll /r:System.Linq.Expressions.dll diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/ExtractorOptions.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/ExtractorOptions.cs new file mode 100644 index 00000000000..11f05a14a00 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/ExtractorOptions.cs @@ -0,0 +1 @@ +// semmle-extractor-options: /r:System.Runtime.Extensions.dll /r:System.IO.FileSystem.dll diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/SystemWebStub.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/SystemWebStub.cs new file mode 100644 index 00000000000..48c74335f6f --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/SystemWebStub.cs @@ -0,0 +1,34 @@ +// This file contains auto-generated code. +// original-extractor-options: /r:System.Runtime.Extensions.dll /r:System.IO.FileSystem.dll + +namespace System +{ + namespace Web + { + namespace Script + { + namespace Serialization + { + // Generated from `System.Web.Script.Serialization.JavaScriptSerializer` in `System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35` + public class JavaScriptSerializer + { + public JavaScriptSerializer() => throw null; + public JavaScriptSerializer(System.Web.Script.Serialization.JavaScriptTypeResolver resolver) => throw null; + public object DeserializeObject(string input) => throw null; + } + + // Generated from `System.Web.Script.Serialization.JavaScriptTypeResolver` in `System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35` + abstract public class JavaScriptTypeResolver + { + } + + // Generated from `System.Web.Script.Serialization.SimpleTypeResolver` in `System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35` + public class SimpleTypeResolver : System.Web.Script.Serialization.JavaScriptTypeResolver + { + public SimpleTypeResolver() => throw null; + } + + } + } + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserialization.expected b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserialization.expected new file mode 100644 index 00000000000..6edb1f62902 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserialization.expected @@ -0,0 +1 @@ +| UnsafeDeserializationBad.cs:9:16:9:38 | call to method DeserializeObject | Unsafe deserializer is used. Make sure the value being deserialized comes from a trusted source. | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserialization.qlref b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserialization.qlref new file mode 100644 index 00000000000..78efa2399c0 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserialization.qlref @@ -0,0 +1 @@ +Security Features/CWE-502/UnsafeDeserialization.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserializationBad.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserializationBad.cs new file mode 100644 index 00000000000..385e700a0bf --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserializationBad.cs @@ -0,0 +1,11 @@ +using System.Web.Script.Serialization; + +class Bad +{ + public static object Deserialize(string s) + { + JavaScriptSerializer sr = new JavaScriptSerializer(new SimpleTypeResolver()); + // BAD + return sr.DeserializeObject(s); + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserializationGood.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserializationGood.cs new file mode 100644 index 00000000000..775862cd683 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserialization/UnsafeDeserializationGood.cs @@ -0,0 +1,11 @@ +using System.Web.Script.Serialization; + +class Good +{ + public static object Deserialize(string s) + { + // GOOD + JavaScriptSerializer sr = new JavaScriptSerializer(); + return sr.DeserializeObject(s); + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/ExtractorOptions.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/ExtractorOptions.cs new file mode 100644 index 00000000000..11f05a14a00 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/ExtractorOptions.cs @@ -0,0 +1 @@ +// semmle-extractor-options: /r:System.Runtime.Extensions.dll /r:System.IO.FileSystem.dll diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/SystemWebStub.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/SystemWebStub.cs new file mode 100644 index 00000000000..293c0cdf355 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/SystemWebStub.cs @@ -0,0 +1,45 @@ +// This file contains auto-generated code. +// original-extractor-options: /r:System.Runtime.Extensions.dll /r:System.IO.FileSystem.dll + +namespace System +{ + namespace Web + { + namespace UI + { + namespace WebControls + { + public class TextBox + { + public string Text { get; set; } + } + } + } + + namespace Script + { + namespace Serialization + { + // Generated from `System.Web.Script.Serialization.JavaScriptSerializer` in `System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35` + public class JavaScriptSerializer + { + public JavaScriptSerializer() => throw null; + public JavaScriptSerializer(System.Web.Script.Serialization.JavaScriptTypeResolver resolver) => throw null; + public object DeserializeObject(string input) => throw null; + } + + // Generated from `System.Web.Script.Serialization.JavaScriptTypeResolver` in `System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35` + abstract public class JavaScriptTypeResolver + { + } + + // Generated from `System.Web.Script.Serialization.SimpleTypeResolver` in `System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35` + public class SimpleTypeResolver : System.Web.Script.Serialization.JavaScriptTypeResolver + { + public SimpleTypeResolver() => throw null; + } + + } + } + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInput.expected b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInput.expected new file mode 100644 index 00000000000..fce66c8e1d5 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInput.expected @@ -0,0 +1,7 @@ +edges +| UnsafeDeserializationUntrustedInputBad.cs:10:37:10:43 | access to parameter textBox | UnsafeDeserializationUntrustedInputBad.cs:10:37:10:48 | access to property Text | +nodes +| UnsafeDeserializationUntrustedInputBad.cs:10:37:10:43 | access to parameter textBox | semmle.label | access to parameter textBox | +| UnsafeDeserializationUntrustedInputBad.cs:10:37:10:48 | access to property Text | semmle.label | access to property Text | +#select +| UnsafeDeserializationUntrustedInputBad.cs:10:37:10:48 | access to property Text | UnsafeDeserializationUntrustedInputBad.cs:10:37:10:43 | access to parameter textBox | UnsafeDeserializationUntrustedInputBad.cs:10:37:10:48 | access to property Text | $@ flows to unsafe deserializer. | UnsafeDeserializationUntrustedInputBad.cs:10:37:10:43 | access to parameter textBox | User-provided data | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInput.qlref b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInput.qlref new file mode 100644 index 00000000000..626bcae9b33 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInput.qlref @@ -0,0 +1 @@ +Security Features/CWE-502/UnsafeDeserializationUntrustedInput.ql \ No newline at end of file diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInputBad.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInputBad.cs new file mode 100644 index 00000000000..db2b25097ba --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInputBad.cs @@ -0,0 +1,12 @@ +using System.Web.UI.WebControls; +using System.Web.Script.Serialization; + +class Bad +{ + public static object Deserialize(TextBox textBox) + { + JavaScriptSerializer sr = new JavaScriptSerializer(new SimpleTypeResolver()); + // BAD + return sr.DeserializeObject(textBox.Text); + } +} diff --git a/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInputGood.cs b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInputGood.cs new file mode 100644 index 00000000000..d1e2935b218 --- /dev/null +++ b/csharp/ql/test/query-tests/Security Features/CWE-502/UnsafeDeserializationUntrustedInput/UnsafeDeserializationUntrustedInputGood.cs @@ -0,0 +1,12 @@ +using System.Web.UI.WebControls; +using System.Web.Script.Serialization; + +class Good +{ + public static object Deserialize(TextBox textBox) + { + JavaScriptSerializer sr = new JavaScriptSerializer(); + // GOOD + return sr.DeserializeObject(textBox.Text); + } +} diff --git a/csharp/upgrades/qlpack.yml b/csharp/upgrades/qlpack.yml new file mode 100644 index 00000000000..8ad3718778d --- /dev/null +++ b/csharp/upgrades/qlpack.yml @@ -0,0 +1,2 @@ +name: codeql-csharp-upgrades +upgrades: . diff --git a/docs/language/global-sphinx-files/_templates/layout.html b/docs/language/global-sphinx-files/_templates/layout.html index 6aa09b5fb38..de3b189e1ee 100644 --- a/docs/language/global-sphinx-files/_templates/layout.html +++ b/docs/language/global-sphinx-files/_templates/layout.html @@ -61,7 +61,7 @@