mirror of
https://github.com/github/codeql.git
synced 2026-05-01 11:45:14 +02:00
Merge branch 'master' into python-support-django2
This commit is contained in:
@@ -46,6 +46,12 @@ Follow the steps below to help other users understand what your query does, and
|
||||
Query help files explain the purpose of your query to other users. Write your query help in a `.qhelp` file and save it in the same directory as your new query.
|
||||
For more information on writing query help, see the [Query help style guide](https://github.com/Semmle/ql/blob/master/docs/query-help-style-guide.md).
|
||||
|
||||
7. **Maintain backwards compatibility**
|
||||
|
||||
The standard CodeQL libraries must evolve in a backwards compatible manner. If any backwards incompatible changes need to be made, the existing API must first be marked as deprecated. This is done by adding a `deprecated` annotation along with a QLDoc reference to the replacement API. Only after at least one full release cycle has elapsed may the old API be removed.
|
||||
|
||||
In addition to contributions to our standard queries and libraries, we also welcome contributions of a more experimental nature, which do not need to fulfill all the requirements listed above. See the guidelines for [experimental queries and libraries](docs/experimental.md) for details.
|
||||
|
||||
## Using your personal data
|
||||
|
||||
If you contribute to this project, we will record your name and email
|
||||
|
||||
@@ -27,10 +27,10 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been improved when flow through functions needs to be
|
||||
combined with both taint tracking and flow through fields allowing more flow
|
||||
to be tracked. This affects and improves some security queries, which may
|
||||
report additional results.
|
||||
* The data-flow library has been improved, which affects and improves some security queries. The improvements are:
|
||||
- Track flow through functions that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like functions, that is, functions that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
* Created the `semmle.code.cpp.models.interfaces.Allocation` library to model allocation such as `new` expressions and calls to `malloc`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
|
||||
* Created the `semmle.code.cpp.models.interfaces.Deallocation` library to model deallocation such as `delete` expressions and calls to `free`. This in intended to replace the functionality in `semmle.code.cpp.commons.Alloc` with a more consistent and useful interface.
|
||||
* The new class `StackVariable` should be used in place of `LocalScopeVariable`
|
||||
@@ -46,3 +46,5 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
|
||||
the following improvements:
|
||||
* The library now models data flow through `strdup` and similar functions.
|
||||
* The library now models data flow through formatting functions such as `sprintf`.
|
||||
* The security pack taint tracking library (`semmle.code.cpp.security.TaintTracking`) uses a new intermediate representation. This provides a more precise analysis of pointers to stack variables and flow through parameters, improving the results of many security queries.
|
||||
* The global value numbering library (`semmle.code.cpp.valuenumbering.GlobalValueNumbering`) uses a new intermediate representation to provide a more precise analysis of heap allocated memory and pointers to stack variables.
|
||||
|
||||
@@ -33,10 +33,10 @@ The following changes in version 1.24 affect C# analysis in all applications.
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been improved when flow through methods needs to be
|
||||
combined with both taint tracking and flow through fields allowing more flow
|
||||
to be tracked. This affects and improves most security queries, which may
|
||||
report additional results.
|
||||
* The data-flow library has been improved, which affects and improves most security queries. The improvements are:
|
||||
- Track flow through methods that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like methods, that is, methods that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
* The taint tracking library now tracks flow through (implicit or explicit) conversion operator calls.
|
||||
* [Code contracts](https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts) are now recognized, and are treated like any other assertion methods.
|
||||
* Expression nullability flow state is given by the predicates `Expr.hasNotNullFlowState()` and `Expr.hasMaybeNullFlowState()`.
|
||||
|
||||
@@ -5,6 +5,7 @@ The following changes in version 1.24 affect Java analysis in all applications.
|
||||
## General improvements
|
||||
|
||||
* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
|
||||
* A `Customizations.qll` file has been added to allow customizations of the standard library that apply to all queries.
|
||||
|
||||
## New queries
|
||||
|
||||
@@ -26,10 +27,10 @@ The following changes in version 1.24 affect Java analysis in all applications.
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The data-flow library has been improved when flow through methods needs to be
|
||||
combined with both taint tracking and flow through fields allowing more flow
|
||||
to be tracked. This affects and improves most security queries, which may
|
||||
report additional results.
|
||||
* The data-flow library has been improved, which affects and improves most security queries. The improvements are:
|
||||
- Track flow through methods that combine taint tracking with flow through fields.
|
||||
- Track flow through clone-like methods, that is, methods that read contents of a field from a
|
||||
parameter and stores the value in the field of a returned object.
|
||||
* Identification of test classes has been improved. Previously, one of the
|
||||
match conditions would classify any class with a name containing the string
|
||||
"Test" as a test class, but now this matching has been replaced with one that
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
## General improvements
|
||||
|
||||
* TypeScript 3.8 is now supported.
|
||||
|
||||
* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
|
||||
|
||||
* Imports with the `.js` extension can now be resolved to a TypeScript file,
|
||||
@@ -9,8 +11,16 @@
|
||||
|
||||
* Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
|
||||
|
||||
* Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
|
||||
|
||||
* The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
|
||||
|
||||
* The call graph construction has been improved, leading to more results from the security queries:
|
||||
- Calls can now be resolved to indirectly-defined class members in more cases.
|
||||
- Calls through partial invocations such as `.bind` can now be resolved in more cases.
|
||||
|
||||
* Support for flow summaries has been more clearly marked as being experimental and moved to the new `experimental` folder.
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [Electron](https://electronjs.org/)
|
||||
- [Handlebars](https://www.npmjs.com/package/handlebars)
|
||||
@@ -24,6 +34,7 @@
|
||||
- [http2](https://nodejs.org/api/http2.html)
|
||||
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
|
||||
- [react](https://www.npmjs.com/package/react)
|
||||
- [request](https://www.npmjs.com/package/request)
|
||||
- [send](https://www.npmjs.com/package/send)
|
||||
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
|
||||
- [ws](https://github.com/websockets/ws)
|
||||
@@ -35,7 +46,11 @@
|
||||
| Cross-site scripting through exception (`js/xss-through-exception`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where an exception is written to the DOM. Results are not shown on LGTM by default. |
|
||||
| Regular expression always matches (`js/regex/always-matches`) | correctness, regular-expressions | Highlights regular expression checks that trivially succeed by matching an empty substring. Results are shown on LGTM by default. |
|
||||
| Missing await (`js/missing-await`) | correctness | Highlights expressions that operate directly on a promise object in a nonsensical way, instead of awaiting its result. Results are shown on LGTM by default. |
|
||||
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | security, external/cwe/cwe-400, external/cwe/cwe-471 | Highlights recursive copying operations that are susceptible to prototype pollution. Results are shown on LGTM by default. |
|
||||
| Polynomial regular expression used on uncontrolled data (`js/polynomial-redos`) | security, external/cwe/cwe-730, external/cwe/cwe-400 | Highlights expensive regular expressions that may be used on malicious input. Results are shown on LGTM by default. |
|
||||
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | security, external/cwe/cwe-400, external/cwe/cwe-471 | Highlights recursive assignment operations that are susceptible to prototype pollution. Results are shown on LGTM by default. |
|
||||
| Unsafe jQuery plugin (`js/unsafe-jquery-plugin`) | Highlights potential XSS vulnerabilities in unsafely designed jQuery plugins. Results are shown on LGTM by default. |
|
||||
| Unnecessary use of `cat` process (`js/unnecessary-use-of-cat`) | correctness, security, maintainability | Highlights command executions of `cat` where the fs API should be used instead. Results are shown on LGTM by default. |
|
||||
|
||||
|
||||
## Changes to existing queries
|
||||
|
||||
@@ -48,8 +63,10 @@
|
||||
| Expression has no effect (`js/useless-expression`) | Fewer false positive results | The query now recognizes block-level flow type annotations and ignores the first statement of a try block. |
|
||||
| Use of call stack introspection in strict mode (`js/strict-mode-call-stack-introspection`) | Fewer false positive results | The query no longer flags expression statements. |
|
||||
| Missing CSRF middleware (`js/missing-token-validation`) | Fewer false positive results | The query reports fewer duplicates and only flags handlers that explicitly access cookie data. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional ways dangerous paths can be constructed. |
|
||||
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional ways dangerous paths can be constructed and used. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
|
||||
| Syntax error (`js/syntax-error`) | Lower severity | This results of this query are now displayed with lower severity. |
|
||||
| Use of password hash with insufficient computational effort (`js/insufficient-password-hash`) | Fewer false positive results | This query now recognizes additional cases that do not require secure hashing. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
|
||||
@@ -222,10 +222,12 @@
|
||||
"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 ValueNumberInternal": [
|
||||
"IR ValueNumberInternal": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"cpp/ql/src/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
|
||||
@@ -15,24 +15,33 @@ class ConstantZero extends Expr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `candidate` is an expression such that if it's unsigned then we
|
||||
* want an alert at `ge`.
|
||||
*/
|
||||
private predicate lookForUnsignedAt(GEExpr ge, Expr candidate) {
|
||||
// Base case: `candidate >= 0`
|
||||
ge.getRightOperand() instanceof ConstantZero and
|
||||
candidate = ge.getLeftOperand().getFullyConverted() and
|
||||
// left operand was a signed or unsigned IntegralType before conversions
|
||||
// (not a pointer, checking a pointer >= 0 is an entirely different mistake)
|
||||
// (not an enum, as the fully converted type of an enum is compiler dependent
|
||||
// so checking an enum >= 0 is always reasonable)
|
||||
ge.getLeftOperand().getUnderlyingType() instanceof IntegralType
|
||||
or
|
||||
// Recursive case: `...(largerType)candidate >= 0`
|
||||
exists(Conversion conversion |
|
||||
lookForUnsignedAt(ge, conversion) and
|
||||
candidate = conversion.getExpr() and
|
||||
conversion.getType().getSize() > candidate.getType().getSize()
|
||||
)
|
||||
}
|
||||
|
||||
class UnsignedGEZero extends GEExpr {
|
||||
UnsignedGEZero() {
|
||||
this.getRightOperand() instanceof ConstantZero and
|
||||
// left operand was a signed or unsigned IntegralType before conversions
|
||||
// (not a pointer, checking a pointer >= 0 is an entirely different mistake)
|
||||
// (not an enum, as the fully converted type of an enum is compiler dependent
|
||||
// so checking an enum >= 0 is always reasonable)
|
||||
getLeftOperand().getUnderlyingType() instanceof IntegralType and
|
||||
exists(Expr ue |
|
||||
// ue is some conversion of the left operand
|
||||
ue = getLeftOperand().getConversion*() and
|
||||
// ue is unsigned
|
||||
ue.getUnderlyingType().(IntegralType).isUnsigned() and
|
||||
// ue may be converted to zero or more strictly larger possibly signed types
|
||||
// before it is fully converted
|
||||
forall(Expr following | following = ue.getConversion+() |
|
||||
following.getType().getSize() > ue.getType().getSize()
|
||||
)
|
||||
lookForUnsignedAt(this, ue) and
|
||||
ue.getUnderlyingType().(IntegralType).isUnsigned()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
1
cpp/ql/src/experimental/README.md
Normal file
1
cpp/ql/src/experimental/README.md
Normal file
@@ -0,0 +1 @@
|
||||
This directory contains [experimental](../../../../docs/experimental.md) CodeQL queries and libraries.
|
||||
@@ -19,6 +19,8 @@ import semmle.code.cpp.exprs.Access
|
||||
class Field extends MemberVariable {
|
||||
Field() { fieldoffsets(underlyingElement(this), _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "Field" }
|
||||
|
||||
/**
|
||||
* Gets the offset of this field in bytes from the start of its declaring
|
||||
* type (on the machine where facts were extracted).
|
||||
@@ -84,6 +86,8 @@ class Field extends MemberVariable {
|
||||
class BitField extends Field {
|
||||
BitField() { bitfield(underlyingElement(this), _, _) }
|
||||
|
||||
override string getCanonicalQLClass() { result = "BitField" }
|
||||
|
||||
/**
|
||||
* Gets the size of this bitfield in bits (on the machine where facts
|
||||
* were extracted).
|
||||
|
||||
@@ -28,6 +28,8 @@ private import semmle.code.cpp.internal.ResolveClass
|
||||
* can have multiple declarations.
|
||||
*/
|
||||
class Variable extends Declaration, @variable {
|
||||
override string getCanonicalQLClass() { result = "Variable" }
|
||||
|
||||
/** Gets the initializer of this variable, if any. */
|
||||
Initializer getInitializer() { result.getDeclaration() = this }
|
||||
|
||||
@@ -351,6 +353,8 @@ class StackVariable extends LocalScopeVariable {
|
||||
* A local variable can be declared by a `DeclStmt` or a `ConditionDeclExpr`.
|
||||
*/
|
||||
class LocalVariable extends LocalScopeVariable, @localvariable {
|
||||
override string getCanonicalQLClass() { result = "LocalVariable" }
|
||||
|
||||
override string getName() { localvariables(underlyingElement(this), _, result) }
|
||||
|
||||
override Type getType() { localvariables(underlyingElement(this), unresolveElement(result), _) }
|
||||
@@ -396,6 +400,8 @@ class NamespaceVariable extends GlobalOrNamespaceVariable {
|
||||
NamespaceVariable() {
|
||||
exists(Namespace n | namespacembrs(unresolveElement(n), underlyingElement(this)))
|
||||
}
|
||||
|
||||
override string getCanonicalQLClass() { result = "NamespaceVariable" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -415,6 +421,8 @@ class NamespaceVariable extends GlobalOrNamespaceVariable {
|
||||
*/
|
||||
class GlobalVariable extends GlobalOrNamespaceVariable {
|
||||
GlobalVariable() { not this instanceof NamespaceVariable }
|
||||
|
||||
override string getCanonicalQLClass() { result = "GlobalVariable" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -434,6 +442,8 @@ class GlobalVariable extends GlobalOrNamespaceVariable {
|
||||
class MemberVariable extends Variable, @membervariable {
|
||||
MemberVariable() { this.isMember() }
|
||||
|
||||
override string getCanonicalQLClass() { result = "MemberVariable" }
|
||||
|
||||
/** Holds if this member is private. */
|
||||
predicate isPrivate() { this.hasSpecifier("private") }
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
private import cpp
|
||||
|
||||
Function viableImpl(Call call) { result = viableCallable(call) }
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -132,16 +132,6 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `call` passes an implicit or explicit qualifier, i.e., a
|
||||
* `this` parameter.
|
||||
*/
|
||||
predicate callHasQualifier(Call call) {
|
||||
call.hasQualifier()
|
||||
or
|
||||
call.getTarget() instanceof Destructor
|
||||
}
|
||||
|
||||
private newtype TContent =
|
||||
TFieldContent(Field f) or
|
||||
TCollectionContent() or
|
||||
@@ -301,3 +291,5 @@ class DataFlowCall extends Expr {
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
@@ -442,6 +442,20 @@ class Expr extends StmtParent, @expr {
|
||||
else result = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unique non-`Conversion` expression `e` for which
|
||||
* `this = e.getConversion*()`.
|
||||
*
|
||||
* For example, if called on the expression `(int)(char)x`, this predicate
|
||||
* gets the expression `x`.
|
||||
*/
|
||||
Expr getUnconverted() {
|
||||
not this instanceof Conversion and
|
||||
result = this
|
||||
or
|
||||
result = this.(Conversion).getExpr().getUnconverted()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of this expression, after any implicit conversions and explicit casts, and after resolving typedefs.
|
||||
*
|
||||
|
||||
@@ -267,7 +267,7 @@ private predicate modelTaintToParameter(Function f, int parameterIn, int paramet
|
||||
|
||||
/**
|
||||
* Holds if `chi` is on the chain of chi-instructions for all aliased memory.
|
||||
* Taint shoud not pass through these instructions since they tend to mix up
|
||||
* Taint should not pass through these instructions since they tend to mix up
|
||||
* unrelated objects.
|
||||
*/
|
||||
private predicate isChiForAllAliasedMemory(Instruction instr) {
|
||||
@@ -275,7 +275,7 @@ private predicate isChiForAllAliasedMemory(Instruction instr) {
|
||||
or
|
||||
isChiForAllAliasedMemory(instr.(ChiInstruction).getTotal())
|
||||
or
|
||||
isChiForAllAliasedMemory(instr.(PhiInstruction).getAnInput())
|
||||
isChiForAllAliasedMemory(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
|
||||
}
|
||||
|
||||
private predicate modelTaintToReturnValue(Function f, int parameterIn) {
|
||||
@@ -343,6 +343,7 @@ private Element adjustedSink(DataFlow::Node sink) {
|
||||
result.(AssignOperation).getAnOperand() = sink.asExpr()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate tainted(Expr source, Element tainted) {
|
||||
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
|
||||
cfg.hasFlow(getNodeForSource(source), sink) and
|
||||
@@ -350,6 +351,7 @@ predicate tainted(Expr source, Element tainted) {
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
|
||||
tainted(source, tainted) and
|
||||
globalVar = ""
|
||||
|
||||
@@ -3,8 +3,6 @@ private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
|
||||
Function viableImpl(CallInstruction call) { result = viableCallable(call) }
|
||||
|
||||
/**
|
||||
* Gets a function that might be called by `call`.
|
||||
*/
|
||||
@@ -137,6 +135,12 @@ private module VirtualDispatch {
|
||||
exists(FunctionInstruction fi |
|
||||
this.flowsFrom(DataFlow::instructionNode(fi), _) and
|
||||
result = fi.getFunctionSymbol()
|
||||
) and
|
||||
(
|
||||
this.getNumberOfArguments() <= result.getEffectiveNumberOfParameters() and
|
||||
this.getNumberOfArguments() >= result.getEffectiveNumberOfParameters()
|
||||
or
|
||||
result.isVarargs()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -67,16 +67,6 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
|
||||
*/
|
||||
predicate jumpStep(Node n1, Node n2) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `call` passes an implicit or explicit qualifier, i.e., a
|
||||
* `this` parameter.
|
||||
*/
|
||||
predicate callHasQualifier(Call call) {
|
||||
call.hasQualifier()
|
||||
or
|
||||
call.getTarget() instanceof Destructor
|
||||
}
|
||||
|
||||
private newtype TContent =
|
||||
TFieldContent(Field f) or
|
||||
TCollectionContent() or
|
||||
@@ -210,3 +200,5 @@ class DataFlowCall extends CallInstruction {
|
||||
}
|
||||
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
@@ -131,10 +131,7 @@ class ExprNode extends InstructionNode {
|
||||
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
Expr getExpr() {
|
||||
result.getConversion*() = instr.getConvertedResultExpression() and
|
||||
not result instanceof Conversion
|
||||
}
|
||||
Expr getExpr() { result = instr.getUnconvertedResultExpression() }
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
@@ -303,10 +300,12 @@ ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
|
||||
VariableNode variableNode(Variable v) { result.getVariable() = v }
|
||||
|
||||
/**
|
||||
* DEPRECATED: See UninitializedNode.
|
||||
*
|
||||
* Gets the `Node` corresponding to the value of an uninitialized local
|
||||
* variable `v`.
|
||||
*/
|
||||
UninitializedNode uninitializedNode(LocalVariable v) { result.getLocalVariable() = v }
|
||||
Node uninitializedNode(LocalVariable v) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
|
||||
|
||||
@@ -82,6 +82,7 @@ private newtype TOpcode =
|
||||
TSizedBufferReadSideEffect() or
|
||||
TSizedBufferMustWriteSideEffect() or
|
||||
TSizedBufferMayWriteSideEffect() or
|
||||
TInitializeDynamicAllocation() or
|
||||
TChi() or
|
||||
TInlineAsm() or
|
||||
TUnreached() or
|
||||
@@ -213,23 +214,28 @@ abstract class IndirectReadOpcode extends IndirectMemoryAccessOpcode {
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that accesses a memory buffer of unknown size.
|
||||
* An opcode that accesses a memory buffer.
|
||||
*/
|
||||
abstract class BufferAccessOpcode extends Opcode {
|
||||
final override predicate hasAddressOperand() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that accesses a memory buffer of unknown size.
|
||||
*/
|
||||
abstract class UnsizedBufferAccessOpcode extends BufferAccessOpcode { }
|
||||
|
||||
/**
|
||||
* An opcode that writes to a memory buffer of unknown size.
|
||||
*/
|
||||
abstract class BufferWriteOpcode extends BufferAccessOpcode {
|
||||
abstract class UnsizedBufferWriteOpcode extends UnsizedBufferAccessOpcode {
|
||||
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
/**
|
||||
* An opcode that reads from a memory buffer of unknown size.
|
||||
*/
|
||||
abstract class BufferReadOpcode extends BufferAccessOpcode {
|
||||
abstract class UnsizedBufferReadOpcode extends UnsizedBufferAccessOpcode {
|
||||
final override MemoryAccessKind getReadMemoryAccess() { result instanceof BufferMemoryAccess }
|
||||
}
|
||||
|
||||
@@ -261,9 +267,7 @@ abstract class EntireAllocationReadOpcode extends EntireAllocationAccessOpcode {
|
||||
/**
|
||||
* An opcode that accesses a memory buffer whose size is determined by a `BufferSizeOperand`.
|
||||
*/
|
||||
abstract class SizedBufferAccessOpcode extends Opcode {
|
||||
final override predicate hasAddressOperand() { any() }
|
||||
|
||||
abstract class SizedBufferAccessOpcode extends BufferAccessOpcode {
|
||||
final override predicate hasBufferSizeOperand() { any() }
|
||||
}
|
||||
|
||||
@@ -666,17 +670,18 @@ module Opcode {
|
||||
final override string toString() { result = "IndirectMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
class BufferReadSideEffect extends ReadSideEffectOpcode, BufferReadOpcode, TBufferReadSideEffect {
|
||||
class BufferReadSideEffect extends ReadSideEffectOpcode, UnsizedBufferReadOpcode,
|
||||
TBufferReadSideEffect {
|
||||
final override string toString() { result = "BufferReadSideEffect" }
|
||||
}
|
||||
|
||||
class BufferMustWriteSideEffect extends WriteSideEffectOpcode, BufferWriteOpcode,
|
||||
class BufferMustWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode,
|
||||
TBufferMustWriteSideEffect {
|
||||
final override string toString() { result = "BufferMustWriteSideEffect" }
|
||||
}
|
||||
|
||||
class BufferMayWriteSideEffect extends WriteSideEffectOpcode, BufferWriteOpcode, MayWriteOpcode,
|
||||
TBufferMayWriteSideEffect {
|
||||
class BufferMayWriteSideEffect extends WriteSideEffectOpcode, UnsizedBufferWriteOpcode,
|
||||
MayWriteOpcode, TBufferMayWriteSideEffect {
|
||||
final override string toString() { result = "BufferMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
@@ -695,6 +700,11 @@ module Opcode {
|
||||
final override string toString() { result = "SizedBufferMayWriteSideEffect" }
|
||||
}
|
||||
|
||||
class InitializeDynamicAllocation extends SideEffectOpcode, EntireAllocationWriteOpcode,
|
||||
TInitializeDynamicAllocation {
|
||||
final override string toString() { result = "InitializeDynamicAllocation" }
|
||||
}
|
||||
|
||||
class Chi extends Opcode, TChi {
|
||||
final override string toString() { result = "Chi" }
|
||||
|
||||
|
||||
@@ -1,3 +1,275 @@
|
||||
private import IR
|
||||
import InstructionSanity
|
||||
import IRTypeSanity
|
||||
import InstructionSanity // module is below
|
||||
import IRTypeSanity // module is in IRType.qll
|
||||
|
||||
module InstructionSanity {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import internal.IRInternal
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
not tag instanceof UnmodeledUseOperandTag and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func, Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(Instruction instr) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple (`n`) edges of kind `kind` from `source`,
|
||||
* where `target` is among the targets of those edges.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
source.getSuccessor(kind) = target
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` in `f` is part of a loop even though the AST of `f`
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(Language::Function f, Instruction instr) {
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
|
||||
count(instr.getBlock().getAPredecessor()) < 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getUse() = instr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(IRBlock block) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
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
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
func = switchInstr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
|
||||
/**
|
||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||
* generated to hold the return value of afunction, or the variable generated to hold the result of
|
||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||
* a condition operator (`a ? b : c`).
|
||||
*/
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
|
||||
@@ -10,264 +10,6 @@ import Imports::MemoryAccessKind
|
||||
import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
|
||||
module InstructionSanity {
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
not tag instanceof UnmodeledUseOperandTag and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func, Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(Instruction instr) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple (`n`) edges of kind `kind` from `source`,
|
||||
* where `target` is among the targets of those edges.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
source.getSuccessor(kind) = target
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` in `f` is part of a loop even though the AST of `f`
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(Language::Function f, Instruction instr) {
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
|
||||
count(instr.getBlock().getAPredecessor()) < 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getUse() = instr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(IRBlock block) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
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
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `Instruction` that is contained in `IRFunction`, and has a location with the specified
|
||||
* `File` and line number. Used for assigning register names when printing IR.
|
||||
@@ -1192,6 +934,11 @@ class CallInstruction extends Instruction {
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
result = getPositionalArgumentOperand(index).getDef()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1340,6 +1087,26 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the initial value of newly allocated memory, e.g. the result of a
|
||||
* call to `malloc`.
|
||||
*/
|
||||
class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
|
||||
InitializeDynamicAllocationInstruction() {
|
||||
getOpcode() instanceof Opcode::InitializeDynamicAllocation
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address of the allocation this instruction is initializing.
|
||||
*/
|
||||
final AddressOperand getAllocationAddressOperand() { result = getAnOperand() }
|
||||
|
||||
/**
|
||||
* Gets the operand for the allocation this instruction is initializing.
|
||||
*/
|
||||
final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a GNU or MSVC inline assembly statement.
|
||||
*/
|
||||
|
||||
@@ -27,19 +27,19 @@ class ValueNumber extends TValueNumber {
|
||||
final Language::Location getLocation() {
|
||||
if
|
||||
exists(Instruction i |
|
||||
i = getAnInstruction() and not i.getLocation() instanceof UnknownLocation
|
||||
i = getAnInstruction() and not i.getLocation() instanceof Language::UnknownLocation
|
||||
)
|
||||
then
|
||||
result =
|
||||
min(Language::Location l |
|
||||
l = getAnInstruction().getLocation() and not l instanceof UnknownLocation
|
||||
l = getAnInstruction().getLocation() and not l instanceof Language::UnknownLocation
|
||||
|
|
||||
l
|
||||
order by
|
||||
l.getFile().getAbsolutePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(),
|
||||
l.getEndColumn()
|
||||
)
|
||||
else result instanceof UnknownDefaultLocation
|
||||
else result instanceof Language::UnknownDefaultLocation
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.Location
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
private import ValueNumberingImports
|
||||
private import cpp
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(IRFunction irFunc, Language::AST ast) {
|
||||
@@ -15,7 +14,7 @@ newtype TValueNumber =
|
||||
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
stringConstantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Field field, TValueNumber objectAddress) {
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, TValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
@@ -33,7 +32,8 @@ newtype TValueNumber =
|
||||
unaryValueNumber(_, irFunc, opcode, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, TValueNumber operand
|
||||
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
|
||||
TValueNumber operand
|
||||
) {
|
||||
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
@@ -136,7 +136,7 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
|
||||
instr.getEnclosingIRFunction() = irFunc
|
||||
}
|
||||
|
||||
predicate constantValueNumber(
|
||||
private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
|
||||
@@ -7,6 +7,9 @@ private newtype TAllocation =
|
||||
TVariableAllocation(IRVariable var) or
|
||||
TIndirectParameterAllocation(IRAutomaticUserVariable var) {
|
||||
exists(InitializeIndirectionInstruction instr | instr.getIRVariable() = var)
|
||||
} or
|
||||
TDynamicAllocation(CallInstruction call) {
|
||||
exists(InitializeDynamicAllocationInstruction instr | instr.getPrimaryInstruction() = call)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,3 +98,29 @@ class IndirectParameterAllocation extends Allocation, TIndirectParameterAllocati
|
||||
|
||||
final override predicate alwaysEscapes() { none() }
|
||||
}
|
||||
|
||||
class DynamicAllocation extends Allocation, TDynamicAllocation {
|
||||
CallInstruction call;
|
||||
|
||||
DynamicAllocation() { this = TDynamicAllocation(call) }
|
||||
|
||||
final override string toString() {
|
||||
result = call.toString() + " at " + call.getLocation() // This isn't performant, but it's only used in test/dump code right now.
|
||||
}
|
||||
|
||||
final override CallInstruction getABaseInstruction() { result = call }
|
||||
|
||||
final override IRFunction getEnclosingIRFunction() { result = call.getEnclosingIRFunction() }
|
||||
|
||||
final override Language::Location getLocation() { result = call.getLocation() }
|
||||
|
||||
final override string getUniqueId() { result = call.getUniqueId() }
|
||||
|
||||
final override IRType getIRType() { result instanceof IRUnknownType }
|
||||
|
||||
final override predicate isReadOnly() { none() }
|
||||
|
||||
final override predicate isAlwaysAllocatedOnStack() { none() }
|
||||
|
||||
final override predicate alwaysEscapes() { none() }
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ private predicate hasResultMemoryAccess(
|
||||
type = languageType.getIRType() and
|
||||
isIndirectOrBufferMemoryAccess(instr.getResultMemoryAccess()) and
|
||||
(if instr.hasResultMayMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if type.getByteSize() > 0
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
)
|
||||
@@ -43,7 +43,7 @@ private predicate hasOperandMemoryAccess(
|
||||
type = languageType.getIRType() and
|
||||
isIndirectOrBufferMemoryAccess(operand.getMemoryAccess()) and
|
||||
(if operand.hasMayReadMemoryAccess() then isMayAccess = true else isMayAccess = false) and
|
||||
if type.getByteSize() > 0
|
||||
if exists(type.getByteSize())
|
||||
then endBitOffset = Ints::add(startBitOffset, Ints::mul(type.getByteSize(), 8))
|
||||
else endBitOffset = Ints::unknown()
|
||||
)
|
||||
@@ -68,8 +68,12 @@ private newtype TMemoryLocation =
|
||||
) and
|
||||
languageType = type.getCanonicalLanguageType()
|
||||
} or
|
||||
TEntireAllocationMemoryLocation(IndirectParameterAllocation var, boolean isMayAccess) {
|
||||
isMayAccess = false or isMayAccess = true
|
||||
TEntireAllocationMemoryLocation(Allocation var, boolean isMayAccess) {
|
||||
(
|
||||
var instanceof IndirectParameterAllocation or
|
||||
var instanceof DynamicAllocation
|
||||
) and
|
||||
(isMayAccess = false or isMayAccess = true)
|
||||
} or
|
||||
TUnknownMemoryLocation(IRFunction irFunc, boolean isMayAccess) {
|
||||
isMayAccess = false or isMayAccess = true
|
||||
|
||||
@@ -665,17 +665,18 @@ module DefUse {
|
||||
private predicate definitionReachesRank(
|
||||
Alias::MemoryLocation useLocation, OldBlock block, int defRank, int reachesRank
|
||||
) {
|
||||
// The def always reaches the next use, even if there is also a def on the
|
||||
// use instruction.
|
||||
hasDefinitionAtRank(useLocation, _, block, defRank, _) and
|
||||
reachesRank <= exitRank(useLocation, block) and // Without this, the predicate would be infinite.
|
||||
(
|
||||
// The def always reaches the next use, even if there is also a def on the
|
||||
// use instruction.
|
||||
reachesRank = defRank + 1
|
||||
or
|
||||
// If the def reached the previous rank, it also reaches the current rank,
|
||||
// unless there was another def at the previous rank.
|
||||
definitionReachesRank(useLocation, block, defRank, reachesRank - 1) and
|
||||
not hasDefinitionAtRank(useLocation, _, block, reachesRank - 1, _)
|
||||
reachesRank = defRank + 1
|
||||
or
|
||||
// If the def reached the previous rank, it also reaches the current rank,
|
||||
// unless there was another def at the previous rank.
|
||||
exists(int prevRank |
|
||||
reachesRank = prevRank + 1 and
|
||||
definitionReachesRank(useLocation, block, defRank, prevRank) and
|
||||
not prevRank = exitRank(useLocation, block) and
|
||||
not hasDefinitionAtRank(useLocation, _, block, prevRank, _)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,275 @@
|
||||
private import IR
|
||||
import InstructionSanity
|
||||
import IRTypeSanity
|
||||
import InstructionSanity // module is below
|
||||
import IRTypeSanity // module is in IRType.qll
|
||||
|
||||
module InstructionSanity {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import internal.IRInternal
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
not tag instanceof UnmodeledUseOperandTag and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func, Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(Instruction instr) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple (`n`) edges of kind `kind` from `source`,
|
||||
* where `target` is among the targets of those edges.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
source.getSuccessor(kind) = target
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` in `f` is part of a loop even though the AST of `f`
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(Language::Function f, Instruction instr) {
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
|
||||
count(instr.getBlock().getAPredecessor()) < 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getUse() = instr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(IRBlock block) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
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
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
func = switchInstr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
|
||||
/**
|
||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||
* generated to hold the return value of afunction, or the variable generated to hold the result of
|
||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||
* a condition operator (`a ? b : c`).
|
||||
*/
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
|
||||
@@ -10,264 +10,6 @@ import Imports::MemoryAccessKind
|
||||
import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
|
||||
module InstructionSanity {
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
not tag instanceof UnmodeledUseOperandTag and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func, Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(Instruction instr) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple (`n`) edges of kind `kind` from `source`,
|
||||
* where `target` is among the targets of those edges.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
source.getSuccessor(kind) = target
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` in `f` is part of a loop even though the AST of `f`
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(Language::Function f, Instruction instr) {
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
|
||||
count(instr.getBlock().getAPredecessor()) < 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getUse() = instr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(IRBlock block) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
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
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `Instruction` that is contained in `IRFunction`, and has a location with the specified
|
||||
* `File` and line number. Used for assigning register names when printing IR.
|
||||
@@ -1192,6 +934,11 @@ class CallInstruction extends Instruction {
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
result = getPositionalArgumentOperand(index).getDef()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1340,6 +1087,26 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the initial value of newly allocated memory, e.g. the result of a
|
||||
* call to `malloc`.
|
||||
*/
|
||||
class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
|
||||
InitializeDynamicAllocationInstruction() {
|
||||
getOpcode() instanceof Opcode::InitializeDynamicAllocation
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address of the allocation this instruction is initializing.
|
||||
*/
|
||||
final AddressOperand getAllocationAddressOperand() { result = getAnOperand() }
|
||||
|
||||
/**
|
||||
* Gets the operand for the allocation this instruction is initializing.
|
||||
*/
|
||||
final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a GNU or MSVC inline assembly statement.
|
||||
*/
|
||||
|
||||
@@ -27,19 +27,19 @@ class ValueNumber extends TValueNumber {
|
||||
final Language::Location getLocation() {
|
||||
if
|
||||
exists(Instruction i |
|
||||
i = getAnInstruction() and not i.getLocation() instanceof UnknownLocation
|
||||
i = getAnInstruction() and not i.getLocation() instanceof Language::UnknownLocation
|
||||
)
|
||||
then
|
||||
result =
|
||||
min(Language::Location l |
|
||||
l = getAnInstruction().getLocation() and not l instanceof UnknownLocation
|
||||
l = getAnInstruction().getLocation() and not l instanceof Language::UnknownLocation
|
||||
|
|
||||
l
|
||||
order by
|
||||
l.getFile().getAbsolutePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(),
|
||||
l.getEndColumn()
|
||||
)
|
||||
else result instanceof UnknownDefaultLocation
|
||||
else result instanceof Language::UnknownDefaultLocation
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.Location
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
private import ValueNumberingImports
|
||||
private import cpp
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(IRFunction irFunc, Language::AST ast) {
|
||||
@@ -15,7 +14,7 @@ newtype TValueNumber =
|
||||
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
stringConstantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Field field, TValueNumber objectAddress) {
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, TValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
@@ -33,7 +32,8 @@ newtype TValueNumber =
|
||||
unaryValueNumber(_, irFunc, opcode, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, TValueNumber operand
|
||||
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
|
||||
TValueNumber operand
|
||||
) {
|
||||
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
@@ -136,7 +136,7 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
|
||||
instr.getEnclosingIRFunction() = irFunc
|
||||
}
|
||||
|
||||
predicate constantValueNumber(
|
||||
private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
|
||||
@@ -68,14 +68,7 @@ private module Cached {
|
||||
|
||||
cached
|
||||
Expr getInstructionUnconvertedResultExpression(Instruction instruction) {
|
||||
exists(Expr converted |
|
||||
result = converted.(Conversion).getExpr+()
|
||||
or
|
||||
result = converted
|
||||
|
|
||||
not result instanceof Conversion and
|
||||
converted = getInstructionConvertedResultExpression(instruction)
|
||||
)
|
||||
result = getInstructionConvertedResultExpression(instruction).getUnconverted()
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -102,6 +95,19 @@ private module Cached {
|
||||
result = getMemoryOperandDefinition(instr, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a non-phi instruction that defines an operand of `instr` but only if
|
||||
* both `instr` and the result have neighbor on the other side of the edge
|
||||
* between them. This is a necessary condition for being in a cycle, and it
|
||||
* removes about two thirds of the tuples that would otherwise be in this
|
||||
* predicate.
|
||||
*/
|
||||
private Instruction getNonPhiOperandDefOfIntermediate(Instruction instr) {
|
||||
result = getNonPhiOperandDef(instr) and
|
||||
exists(getNonPhiOperandDef(result)) and
|
||||
instr = getNonPhiOperandDef(_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a cycle in the operand graph that doesn't go
|
||||
* through a phi instruction and therefore should be impossible.
|
||||
@@ -115,7 +121,7 @@ private module Cached {
|
||||
cached
|
||||
predicate isInCycle(Instruction instr) {
|
||||
instr instanceof Instruction and
|
||||
getNonPhiOperandDef+(instr) = instr
|
||||
getNonPhiOperandDefOfIntermediate+(instr) = instr
|
||||
}
|
||||
|
||||
cached
|
||||
|
||||
@@ -341,16 +341,32 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { none() }
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
||||
expr.getTarget() instanceof AllocationFunction and
|
||||
opcode instanceof Opcode::InitializeDynamicAllocation and
|
||||
tag = OnlyInstructionTag() and
|
||||
type = getUnknownType()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() { result = getChild(0).getFirstInstruction() }
|
||||
override Instruction getFirstInstruction() {
|
||||
if expr.getTarget() instanceof AllocationFunction
|
||||
then result = getInstruction(OnlyInstructionTag())
|
||||
else result = getChild(0).getFirstInstruction()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = OnlyInstructionTag() and
|
||||
kind = gotoEdge() and
|
||||
expr.getTarget() instanceof AllocationFunction and
|
||||
if exists(getChild(0))
|
||||
then result = getChild(0).getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) { none() }
|
||||
|
||||
override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
none()
|
||||
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag = addressOperand() and
|
||||
result = getPrimaryInstructionForSideEffect(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
@@ -487,7 +503,7 @@ class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEff
|
||||
}
|
||||
|
||||
override CppType getInstructionOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
if hasSpecificReadSideEffect(any(Opcode::BufferReadSideEffect op))
|
||||
if hasSpecificReadSideEffect(any(BufferAccessOpcode op))
|
||||
then
|
||||
result = getUnknownType() and
|
||||
tag instanceof OnlyInstructionTag and
|
||||
|
||||
@@ -411,7 +411,9 @@ newtype TTranslatedElement =
|
||||
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
|
||||
// The side effects of a `Call`
|
||||
TTranslatedSideEffects(Call expr) {
|
||||
exists(TTranslatedArgumentSideEffect(expr, _, _, _)) or expr instanceof ConstructorCall
|
||||
exists(TTranslatedArgumentSideEffect(expr, _, _, _)) or
|
||||
expr instanceof ConstructorCall or
|
||||
expr.getTarget() instanceof AllocationFunction
|
||||
} or // A precise side effect of an argument to a `Call`
|
||||
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
|
||||
(
|
||||
|
||||
@@ -655,6 +655,11 @@ class TranslatedSwitchStmt extends TranslatedStmt {
|
||||
kind = getCaseEdge(switchCase) and
|
||||
result = getTranslatedStmt(switchCase).getFirstInstruction()
|
||||
)
|
||||
or
|
||||
not stmt.hasDefaultCase() and
|
||||
tag = SwitchBranchTag() and
|
||||
kind instanceof DefaultEdge and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
|
||||
@@ -1,3 +1,275 @@
|
||||
private import IR
|
||||
import InstructionSanity
|
||||
import IRTypeSanity
|
||||
import InstructionSanity // module is below
|
||||
import IRTypeSanity // module is in IRType.qll
|
||||
|
||||
module InstructionSanity {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import internal.IRInternal
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
not tag instanceof UnmodeledUseOperandTag and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func, Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(Instruction instr) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple (`n`) edges of kind `kind` from `source`,
|
||||
* where `target` is among the targets of those edges.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
source.getSuccessor(kind) = target
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` in `f` is part of a loop even though the AST of `f`
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(Language::Function f, Instruction instr) {
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
|
||||
count(instr.getBlock().getAPredecessor()) < 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getUse() = instr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(IRBlock block) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
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
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
query predicate switchInstructionWithoutDefaultEdge(
|
||||
SwitchInstruction switchInstr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(switchInstr.getDefaultSuccessor()) and
|
||||
message =
|
||||
"SwitchInstruction " + switchInstr.toString() + " without a DefaultEdge in function '$@'." and
|
||||
func = switchInstr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ IRTempVariable getIRTempVariable(Language::AST ast, TempVariableTag tag) {
|
||||
|
||||
/**
|
||||
* A temporary variable introduced by IR construction. The most common examples are the variable
|
||||
* generated to hold the return value of afunction, or the variable generated to hold the result of
|
||||
* generated to hold the return value of a function, or the variable generated to hold the result of
|
||||
* a condition operator (`a ? b : c`).
|
||||
*/
|
||||
class IRTempVariable extends IRGeneratedVariable, IRAutomaticVariable, TIRTempVariable {
|
||||
|
||||
@@ -10,264 +10,6 @@ import Imports::MemoryAccessKind
|
||||
import Imports::Opcode
|
||||
private import Imports::OperandTag
|
||||
|
||||
module InstructionSanity {
|
||||
/**
|
||||
* Holds if instruction `instr` is missing an expected operand with tag `tag`.
|
||||
*/
|
||||
query predicate missingOperand(Instruction instr, string message, IRFunction func, string funcText) {
|
||||
exists(OperandTag tag |
|
||||
instr.getOpcode().hasOperand(tag) and
|
||||
not exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
message =
|
||||
"Instruction '" + instr.getOpcode().toString() +
|
||||
"' is missing an expected operand with tag '" + tag.toString() + "' in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has an unexpected operand with tag `tag`.
|
||||
*/
|
||||
query predicate unexpectedOperand(Instruction instr, OperandTag tag) {
|
||||
exists(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
not instr.getOpcode().hasOperand(tag) and
|
||||
not (instr instanceof CallInstruction and tag instanceof ArgumentOperandTag) and
|
||||
not (
|
||||
instr instanceof BuiltInOperationInstruction and tag instanceof PositionalArgumentOperandTag
|
||||
) and
|
||||
not (instr instanceof InlineAsmInstruction and tag instanceof AsmOperandTag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` has multiple operands with tag `tag`.
|
||||
*/
|
||||
query predicate duplicateOperand(
|
||||
Instruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(OperandTag tag, int operandCount |
|
||||
operandCount =
|
||||
strictcount(NonPhiOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getOperandTag() = tag
|
||||
) and
|
||||
operandCount > 1 and
|
||||
not tag instanceof UnmodeledUseOperandTag and
|
||||
message =
|
||||
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
|
||||
" in function '$@'." and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `Phi` instruction `instr` is missing an operand corresponding to
|
||||
* the predecessor block `pred`.
|
||||
*/
|
||||
query predicate missingPhiOperand(PhiInstruction instr, IRBlock pred) {
|
||||
pred = instr.getBlock().getAPredecessor() and
|
||||
not exists(PhiInputOperand operand |
|
||||
operand = instr.getAnOperand() and
|
||||
operand.getPredecessorBlock() = pred
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingOperandType(Operand operand, string message) {
|
||||
exists(Language::Function func, Instruction use |
|
||||
not exists(operand.getType()) and
|
||||
use = operand.getUse() and
|
||||
func = use.getEnclosingFunction() and
|
||||
message =
|
||||
"Operand '" + operand.toString() + "' of instruction '" + use.getOpcode().toString() +
|
||||
"' missing type in function '" + Language::getIdentityString(func) + "'."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate duplicateChiOperand(
|
||||
ChiInstruction chi, string message, IRFunction func, string funcText
|
||||
) {
|
||||
chi.getTotal() = chi.getPartial() and
|
||||
message =
|
||||
"Chi instruction for " + chi.getPartial().toString() +
|
||||
" has duplicate operands in function $@" and
|
||||
func = chi.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
query predicate sideEffectWithoutPrimary(
|
||||
SideEffectInstruction instr, string message, IRFunction func, string funcText
|
||||
) {
|
||||
not exists(instr.getPrimaryInstruction()) and
|
||||
message = "Side effect instruction missing primary instruction in function $@" and
|
||||
func = instr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an instruction, other than `ExitFunction`, has no successors.
|
||||
*/
|
||||
query predicate instructionWithoutSuccessor(Instruction instr) {
|
||||
not exists(instr.getASuccessor()) and
|
||||
not instr instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not instr instanceof PhiInstruction and
|
||||
not instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there are multiple (`n`) edges of kind `kind` from `source`,
|
||||
* where `target` is among the targets of those edges.
|
||||
*/
|
||||
query predicate ambiguousSuccessors(Instruction source, EdgeKind kind, int n, Instruction target) {
|
||||
n = strictcount(Instruction t | source.getSuccessor(kind) = t) and
|
||||
n > 1 and
|
||||
source.getSuccessor(kind) = target
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` in `f` is part of a loop even though the AST of `f`
|
||||
* contains no element that can cause loops.
|
||||
*/
|
||||
query predicate unexplainedLoop(Language::Function f, Instruction instr) {
|
||||
exists(IRBlock block |
|
||||
instr.getBlock() = block and
|
||||
block.getEnclosingFunction() = f and
|
||||
block.getASuccessor+() = block
|
||||
) and
|
||||
not Language::hasPotentialLoop(f)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `Phi` instruction is present in a block with fewer than two
|
||||
* predecessors.
|
||||
*/
|
||||
query predicate unnecessaryPhiInstruction(PhiInstruction instr) {
|
||||
count(instr.getBlock().getAPredecessor()) < 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if operand `operand` consumes a value that was defined in
|
||||
* a different function.
|
||||
*/
|
||||
query predicate operandAcrossFunctions(Operand operand, Instruction instr, Instruction defInstr) {
|
||||
operand.getUse() = instr and
|
||||
operand.getAnyDef() = defInstr and
|
||||
instr.getEnclosingIRFunction() != defInstr.getEnclosingIRFunction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if instruction `instr` is not in exactly one block.
|
||||
*/
|
||||
query predicate instructionWithoutUniqueBlock(Instruction instr, int blockCount) {
|
||||
blockCount = count(instr.getBlock()) and
|
||||
blockCount != 1
|
||||
}
|
||||
|
||||
private predicate forwardEdge(IRBlock b1, IRBlock b2) {
|
||||
b1.getASuccessor() = b2 and
|
||||
not b1.getBackEdgeSuccessor(_) = b2
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a loop in which no edge is a back edge.
|
||||
*
|
||||
* This check ensures we don't have too _few_ back edges.
|
||||
*/
|
||||
query predicate containsLoopOfForwardEdges(IRFunction f) {
|
||||
exists(IRBlock block |
|
||||
forwardEdge+(block, block) and
|
||||
block.getEnclosingIRFunction() = f
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `block` is reachable from its function entry point but would not
|
||||
* be reachable by traversing only forward edges. This check is skipped for
|
||||
* functions containing `goto` statements as the property does not generally
|
||||
* hold there.
|
||||
*
|
||||
* This check ensures we don't have too _many_ back edges.
|
||||
*/
|
||||
query predicate lostReachability(IRBlock block) {
|
||||
exists(IRFunction f, IRBlock entry |
|
||||
entry = f.getEntryBlock() and
|
||||
entry.getASuccessor+() = block and
|
||||
not forwardEdge+(entry, block) and
|
||||
not Language::hasGoto(f.getFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the number of back edges differs between the `Instruction` graph
|
||||
* and the `IRBlock` graph.
|
||||
*/
|
||||
query predicate backEdgeCountMismatch(Language::Function f, int fromInstr, int fromBlock) {
|
||||
fromInstr =
|
||||
count(Instruction i1, Instruction i2 |
|
||||
i1.getEnclosingFunction() = f and i1.getBackEdgeSuccessor(_) = i2
|
||||
) and
|
||||
fromBlock =
|
||||
count(IRBlock b1, IRBlock b2 |
|
||||
b1.getEnclosingFunction() = f and b1.getBackEdgeSuccessor(_) = b2
|
||||
) and
|
||||
fromInstr != fromBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the point in the function at which the specified operand is evaluated. For most operands,
|
||||
* this is at the instruction that consumes the use. For a `PhiInputOperand`, the effective point
|
||||
* of evaluation is at the end of the corresponding predecessor block.
|
||||
*/
|
||||
private predicate pointOfEvaluation(Operand operand, IRBlock block, int index) {
|
||||
block = operand.(PhiInputOperand).getPredecessorBlock() and
|
||||
index = block.getInstructionCount()
|
||||
or
|
||||
exists(Instruction use |
|
||||
use = operand.(NonPhiOperand).getUse() and
|
||||
block.getInstruction(index) = use
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `useOperand` has a definition that does not dominate the use.
|
||||
*/
|
||||
query predicate useNotDominatedByDefinition(
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
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
|
||||
(
|
||||
defInstr instanceof PhiInstruction and
|
||||
defBlock = defInstr.getBlock() and
|
||||
defIndex = -1
|
||||
or
|
||||
defBlock.getInstruction(defIndex) = defInstr
|
||||
) and
|
||||
not (
|
||||
defBlock.strictlyDominates(useBlock)
|
||||
or
|
||||
defBlock = useBlock and
|
||||
defIndex < useIndex
|
||||
) and
|
||||
message =
|
||||
"Operand '" + useOperand.toString() +
|
||||
"' is not dominated by its definition in function '$@'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `Instruction` that is contained in `IRFunction`, and has a location with the specified
|
||||
* `File` and line number. Used for assigning register names when printing IR.
|
||||
@@ -1192,6 +934,11 @@ class CallInstruction extends Instruction {
|
||||
final Instruction getPositionalArgument(int index) {
|
||||
result = getPositionalArgumentOperand(index).getDef()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of arguments of the call, including the `this` pointer, if any.
|
||||
*/
|
||||
final int getNumberOfArguments() { result = count(this.getAnArgumentOperand()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1340,6 +1087,26 @@ class SizedBufferMayWriteSideEffectInstruction extends WriteSideEffectInstructio
|
||||
Instruction getSizeDef() { result = getAnOperand().(BufferSizeOperand).getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing the initial value of newly allocated memory, e.g. the result of a
|
||||
* call to `malloc`.
|
||||
*/
|
||||
class InitializeDynamicAllocationInstruction extends SideEffectInstruction {
|
||||
InitializeDynamicAllocationInstruction() {
|
||||
getOpcode() instanceof Opcode::InitializeDynamicAllocation
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address of the allocation this instruction is initializing.
|
||||
*/
|
||||
final AddressOperand getAllocationAddressOperand() { result = getAnOperand() }
|
||||
|
||||
/**
|
||||
* Gets the operand for the allocation this instruction is initializing.
|
||||
*/
|
||||
final Instruction getAllocationAddress() { result = getAllocationAddressOperand().getDef() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An instruction representing a GNU or MSVC inline assembly statement.
|
||||
*/
|
||||
|
||||
@@ -27,19 +27,19 @@ class ValueNumber extends TValueNumber {
|
||||
final Language::Location getLocation() {
|
||||
if
|
||||
exists(Instruction i |
|
||||
i = getAnInstruction() and not i.getLocation() instanceof UnknownLocation
|
||||
i = getAnInstruction() and not i.getLocation() instanceof Language::UnknownLocation
|
||||
)
|
||||
then
|
||||
result =
|
||||
min(Language::Location l |
|
||||
l = getAnInstruction().getLocation() and not l instanceof UnknownLocation
|
||||
l = getAnInstruction().getLocation() and not l instanceof Language::UnknownLocation
|
||||
|
|
||||
l
|
||||
order by
|
||||
l.getFile().getAbsolutePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(),
|
||||
l.getEndColumn()
|
||||
)
|
||||
else result instanceof UnknownDefaultLocation
|
||||
else result instanceof Language::UnknownDefaultLocation
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import semmle.code.cpp.ir.implementation.aliased_ssa.IR
|
||||
import semmle.code.cpp.ir.internal.Overlap
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import semmle.code.cpp.Location
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
private import ValueNumberingImports
|
||||
private import cpp
|
||||
|
||||
newtype TValueNumber =
|
||||
TVariableAddressValueNumber(IRFunction irFunc, Language::AST ast) {
|
||||
@@ -15,7 +14,7 @@ newtype TValueNumber =
|
||||
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
|
||||
stringConstantValueNumber(_, irFunc, type, value)
|
||||
} or
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Field field, TValueNumber objectAddress) {
|
||||
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, TValueNumber objectAddress) {
|
||||
fieldAddressValueNumber(_, irFunc, field, objectAddress)
|
||||
} or
|
||||
TBinaryValueNumber(
|
||||
@@ -33,7 +32,8 @@ newtype TValueNumber =
|
||||
unaryValueNumber(_, irFunc, opcode, operand)
|
||||
} or
|
||||
TInheritanceConversionValueNumber(
|
||||
IRFunction irFunc, Opcode opcode, Class baseClass, Class derivedClass, TValueNumber operand
|
||||
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
|
||||
TValueNumber operand
|
||||
) {
|
||||
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
|
||||
} or
|
||||
@@ -136,7 +136,7 @@ private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRF
|
||||
instr.getEnclosingIRFunction() = irFunc
|
||||
}
|
||||
|
||||
predicate constantValueNumber(
|
||||
private predicate constantValueNumber(
|
||||
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
|
||||
) {
|
||||
instr.getEnclosingIRFunction() = irFunc and
|
||||
|
||||
@@ -665,17 +665,18 @@ module DefUse {
|
||||
private predicate definitionReachesRank(
|
||||
Alias::MemoryLocation useLocation, OldBlock block, int defRank, int reachesRank
|
||||
) {
|
||||
// The def always reaches the next use, even if there is also a def on the
|
||||
// use instruction.
|
||||
hasDefinitionAtRank(useLocation, _, block, defRank, _) and
|
||||
reachesRank <= exitRank(useLocation, block) and // Without this, the predicate would be infinite.
|
||||
(
|
||||
// The def always reaches the next use, even if there is also a def on the
|
||||
// use instruction.
|
||||
reachesRank = defRank + 1
|
||||
or
|
||||
// If the def reached the previous rank, it also reaches the current rank,
|
||||
// unless there was another def at the previous rank.
|
||||
definitionReachesRank(useLocation, block, defRank, reachesRank - 1) and
|
||||
not hasDefinitionAtRank(useLocation, _, block, reachesRank - 1, _)
|
||||
reachesRank = defRank + 1
|
||||
or
|
||||
// If the def reached the previous rank, it also reaches the current rank,
|
||||
// unless there was another def at the previous rank.
|
||||
exists(int prevRank |
|
||||
reachesRank = prevRank + 1 and
|
||||
definitionReachesRank(useLocation, block, defRank, prevRank) and
|
||||
not prevRank = exitRank(useLocation, block) and
|
||||
not hasDefinitionAtRank(useLocation, _, block, prevRank, _)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ private import semmle.code.cpp.ir.IR
|
||||
* methods.
|
||||
*/
|
||||
class GVN extends TValueNumber {
|
||||
pragma[noinline]
|
||||
GVN() {
|
||||
exists(Instruction instr |
|
||||
this = tvalueNumber(instr) and exists(instr.getUnconvertedResultExpression())
|
||||
|
||||
@@ -13,6 +13,10 @@ class Function = Cpp::Function;
|
||||
|
||||
class Location = Cpp::Location;
|
||||
|
||||
class UnknownLocation = Cpp::UnknownLocation;
|
||||
|
||||
class UnknownDefaultLocation = Cpp::UnknownDefaultLocation;
|
||||
|
||||
class File = Cpp::File;
|
||||
|
||||
class AST = Cpp::Locatable;
|
||||
|
||||
@@ -80,7 +80,7 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int i) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { none() }
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
|
||||
@@ -18,14 +18,18 @@ abstract class SideEffectFunction extends Function {
|
||||
/**
|
||||
* Holds if the function never reads from memory that was defined before entry to the function.
|
||||
* This memory could be from global variables, or from other memory that was reachable from a
|
||||
* pointer that was passed into the function.
|
||||
* pointer that was passed into the function. Input side-effects, and reads from memory that
|
||||
* cannot be visible to the caller (for example a buffer inside an I/O library) are not modeled
|
||||
* here.
|
||||
*/
|
||||
abstract predicate hasOnlySpecificReadSideEffects();
|
||||
|
||||
/**
|
||||
* Holds if the function never writes to memory that remains allocated after the function
|
||||
* returns. This memory could be from global variables, or from other memory that was reachable
|
||||
* from a pointer that was passed into the function.
|
||||
* from a pointer that was passed into the function. Output side-effects, and writes to memory
|
||||
* that cannot be visible to the caller (for example a buffer inside an I/O library) are not
|
||||
* modeled here.
|
||||
*/
|
||||
abstract predicate hasOnlySpecificWriteSideEffects();
|
||||
|
||||
@@ -43,7 +47,6 @@ abstract class SideEffectFunction extends Function {
|
||||
*/
|
||||
predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) { none() }
|
||||
|
||||
// TODO: name?
|
||||
/**
|
||||
* Gets the index of the parameter that indicates the size of the buffer pointed to by the
|
||||
* parameter at index `i`.
|
||||
|
||||
@@ -91,13 +91,30 @@ private float wideningUpperBounds(ArithmeticType t) {
|
||||
result = 1.0 / 0.0 // +Inf
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the expression `e`, if it is a constant.
|
||||
* This predicate also handles the case of constant variables initialized in compilation units,
|
||||
* which doesn't necessarily have a getValue() result from the extractor.
|
||||
*/
|
||||
private string getValue(Expr e) {
|
||||
if exists(e.getValue())
|
||||
then result = e.getValue()
|
||||
else
|
||||
exists(VariableAccess access, Variable v |
|
||||
e = access and
|
||||
v = access.getTarget() and
|
||||
v.getUnderlyingType().isConst() and
|
||||
result = getValue(v.getAnAssignedValue())
|
||||
)
|
||||
}
|
||||
|
||||
/** Set of expressions which we know how to analyze. */
|
||||
private predicate analyzableExpr(Expr e) {
|
||||
// The type of the expression must be arithmetic. We reuse the logic in
|
||||
// `exprMinVal` to check this.
|
||||
exists(exprMinVal(e)) and
|
||||
(
|
||||
exists(e.getValue().toFloat()) or
|
||||
exists(getValue(e).toFloat()) or
|
||||
e instanceof UnaryPlusExpr or
|
||||
e instanceof UnaryMinusExpr or
|
||||
e instanceof MinExpr or
|
||||
@@ -365,8 +382,8 @@ private float getTruncatedLowerBounds(Expr expr) {
|
||||
then
|
||||
// If the expression evaluates to a constant, then there is no
|
||||
// need to call getLowerBoundsImpl.
|
||||
if exists(expr.getValue().toFloat())
|
||||
then result = expr.getValue().toFloat()
|
||||
if exists(getValue(expr).toFloat())
|
||||
then result = getValue(expr).toFloat()
|
||||
else (
|
||||
// Some of the bounds computed by getLowerBoundsImpl might
|
||||
// overflow, so we replace invalid bounds with exprMinVal.
|
||||
@@ -418,8 +435,8 @@ private float getTruncatedUpperBounds(Expr expr) {
|
||||
then
|
||||
// If the expression evaluates to a constant, then there is no
|
||||
// need to call getUpperBoundsImpl.
|
||||
if exists(expr.getValue().toFloat())
|
||||
then result = expr.getValue().toFloat()
|
||||
if exists(getValue(expr).toFloat())
|
||||
then result = getValue(expr).toFloat()
|
||||
else (
|
||||
// Some of the bounds computed by `getUpperBoundsImpl`
|
||||
// might overflow, so we replace invalid bounds with
|
||||
|
||||
@@ -13,19 +13,36 @@ predicate guardedAbs(Operation e, Expr use) {
|
||||
)
|
||||
}
|
||||
|
||||
/** This is `BasicBlock.getNode`, restricted to `Stmt` for performance. */
|
||||
pragma[noinline]
|
||||
private int getStmtIndexInBlock(BasicBlock block, Stmt stmt) { block.getNode(result) = stmt }
|
||||
|
||||
pragma[inline]
|
||||
private predicate stmtDominates(Stmt dominator, Stmt dominated) {
|
||||
// In same block
|
||||
exists(BasicBlock block, int dominatorIndex, int dominatedIndex |
|
||||
dominatorIndex = getStmtIndexInBlock(block, dominator) and
|
||||
dominatedIndex = getStmtIndexInBlock(block, dominated) and
|
||||
dominatedIndex >= dominatorIndex
|
||||
)
|
||||
or
|
||||
// In (possibly) different blocks
|
||||
bbStrictlyDominates(dominator.getBasicBlock(), dominated.getBasicBlock())
|
||||
}
|
||||
|
||||
/** is the size of this use guarded to be less than something? */
|
||||
pragma[nomagic]
|
||||
predicate guardedLesser(Operation e, Expr use) {
|
||||
exists(IfStmt c, RelationalOperation guard |
|
||||
use = guard.getLesserOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
iDominates*(c.getThen(), e.getEnclosingStmt())
|
||||
stmtDominates(c.getThen(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(Loop c, RelationalOperation guard |
|
||||
use = guard.getLesserOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
iDominates*(c.getStmt(), e.getEnclosingStmt())
|
||||
stmtDominates(c.getStmt(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(ConditionalExpr c, RelationalOperation guard |
|
||||
@@ -43,13 +60,13 @@ predicate guardedGreater(Operation e, Expr use) {
|
||||
exists(IfStmt c, RelationalOperation guard |
|
||||
use = guard.getGreaterOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
iDominates*(c.getThen(), e.getEnclosingStmt())
|
||||
stmtDominates(c.getThen(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(Loop c, RelationalOperation guard |
|
||||
use = guard.getGreaterOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
iDominates*(c.getStmt(), e.getEnclosingStmt())
|
||||
stmtDominates(c.getStmt(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(ConditionalExpr c, RelationalOperation guard |
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
* Support for tracking tainted data through the program.
|
||||
*/
|
||||
|
||||
import TaintTrackingImpl
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
|
||||
|
||||
@@ -1604,30 +1604,18 @@ class EnumSwitch extends SwitchStmt {
|
||||
* ```
|
||||
* there are results `GREEN` and `BLUE`.
|
||||
*/
|
||||
pragma[noopt]
|
||||
EnumConstant getAMissingCase() {
|
||||
exists(Enum et |
|
||||
exists(Expr e, Type t |
|
||||
e = this.getExpr() and
|
||||
this instanceof EnumSwitch and
|
||||
t = e.getType() and
|
||||
et = t.getUnderlyingType()
|
||||
) and
|
||||
et = this.getExpr().getUnderlyingType() and
|
||||
result = et.getAnEnumConstant() and
|
||||
not exists(string value |
|
||||
exists(SwitchCase sc, Expr e |
|
||||
sc = this.getASwitchCase() and
|
||||
e = sc.getExpr() and
|
||||
value = e.getValue()
|
||||
) and
|
||||
exists(Initializer init, Expr e |
|
||||
init = result.getInitializer() and
|
||||
e = init.getExpr() and
|
||||
e.getValue() = value
|
||||
)
|
||||
)
|
||||
not this.matchesValue(result.getInitializer().getExpr().getValue())
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate matchesValue(string value) {
|
||||
value = this.getASwitchCase().getExpr().getValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1 +1 @@
|
||||
import GlobalValueNumberingImpl
|
||||
import semmle.code.cpp.ir.internal.ASTValueNumbering
|
||||
|
||||
1
cpp/ql/test/experimental/README.md
Normal file
1
cpp/ql/test/experimental/README.md
Normal file
@@ -0,0 +1 @@
|
||||
This directory contains tests for [experimental](../../../../docs/experimental.md) CodeQL queries and libraries.
|
||||
@@ -52,3 +52,18 @@
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:15:75:18 | call to atoi | |
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:25 | call to getenv | |
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:45 | (const char *)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:8:24:8:25 | s1 | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:20:11:21 | s1 | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:17:83:24 | userName | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:33 | call to getenv | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:46 | (const char *)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:85:8:85:11 | copy | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:2:86:7 | call to strcpy | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:9:86:12 | copy | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:15:86:22 | userName | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:6:88:27 | ! ... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:12 | call to strcmp | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import semmle.code.cpp.security.TaintTrackingImpl
|
||||
|
||||
from Expr source, Element tainted, string globalVar
|
||||
where
|
||||
|
||||
@@ -8,3 +8,6 @@
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:69:10:69:13 | copy | AST only |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:12:70:15 | copy | AST only |
|
||||
| test.cpp:68:28:68:33 | call to getenv | test.cpp:71:12:71:15 | copy | AST only |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:85:8:85:11 | copy | AST only |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:9:86:12 | copy | AST only |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import semmle.code.cpp.security.TaintTracking as AST
|
||||
import semmle.code.cpp.security.TaintTrackingImpl as AST
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IR
|
||||
import cpp
|
||||
|
||||
|
||||
@@ -40,3 +40,15 @@
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:15:75:18 | call to atoi | |
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:25 | call to getenv | |
|
||||
| test.cpp:75:20:75:25 | call to getenv | test.cpp:75:20:75:45 | (const char *)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:8:24:8:25 | s1 | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:17:83:24 | userName | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:33 | call to getenv | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:83:28:83:46 | (const char *)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:2:86:7 | call to strcpy | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:86:15:86:22 | userName | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:6:88:27 | ! ... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:12 | call to strcmp | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:7:88:27 | (bool)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | (const char *)... | |
|
||||
| test.cpp:83:28:83:33 | call to getenv | test.cpp:88:14:88:17 | copy | |
|
||||
|
||||
@@ -76,3 +76,16 @@ void guard() {
|
||||
if (len > 1000) return;
|
||||
char **node = (char **) malloc(len * sizeof(char *));
|
||||
}
|
||||
|
||||
const char *alias_global;
|
||||
|
||||
void mallocBuffer() {
|
||||
const char *userName = getenv("USER_NAME");
|
||||
char *alias = (char*)malloc(4096);
|
||||
char *copy = (char*)malloc(4096);
|
||||
strcpy(copy, userName);
|
||||
alias_global = alias; // to force a Chi node on all aliased memory
|
||||
if (!strcmp(copy, "admin")) { // copy should be tainted
|
||||
isAdmin = true;
|
||||
}
|
||||
}
|
||||
@@ -27,3 +27,5 @@
|
||||
| declarationEntry.cpp:31:4:31:19 | myMemberVariable | declarationEntry.cpp:31:4:31:19 | definition of myMemberVariable | 1 | 1 |
|
||||
| declarationEntry.cpp:34:22:34:28 | mtc_int | declarationEntry.cpp:34:22:34:28 | definition of mtc_int | 1 | 1 |
|
||||
| declarationEntry.cpp:35:24:35:32 | mtc_short | declarationEntry.cpp:35:24:35:32 | definition of mtc_short | 1 | 1 |
|
||||
| macro.c:2:1:2:3 | foo | macro.c:2:1:2:3 | declaration of foo | 1 | 1 |
|
||||
| macro.c:4:5:4:8 | main | macro.c:4:5:4:8 | definition of main | 1 | 1 |
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
| declarationEntry.c:2:6:2:20 | declaration of myFirstFunction | |
|
||||
| declarationEntry.c:4:6:4:21 | definition of mySecondFunction | |
|
||||
| declarationEntry.c:8:6:8:20 | definition of myThirdFunction | |
|
||||
| declarationEntry.c:13:2:13:2 | declaration of myFourthFunction | isImplicit |
|
||||
| declarationEntry.c:14:2:14:2 | declaration of myFifthFunction | isImplicit |
|
||||
| declarationEntry.c:17:6:17:21 | declaration of myFourthFunction | |
|
||||
| declarationEntry.cpp:9:6:9:15 | declaration of myFunction | |
|
||||
| declarationEntry.cpp:11:6:11:15 | definition of myFunction | |
|
||||
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | |
|
||||
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | |
|
||||
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | |
|
||||
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | |
|
||||
| declarationEntry.c:2:6:2:20 | declaration of myFirstFunction | | 1 | c_linkage |
|
||||
| declarationEntry.c:4:6:4:21 | definition of mySecondFunction | | 1 | c_linkage |
|
||||
| declarationEntry.c:8:6:8:20 | definition of myThirdFunction | | 1 | c_linkage |
|
||||
| declarationEntry.c:13:2:13:2 | declaration of myFourthFunction | isImplicit | 1 | c_linkage |
|
||||
| declarationEntry.c:14:2:14:2 | declaration of myFifthFunction | isImplicit | 1 | c_linkage |
|
||||
| declarationEntry.c:17:6:17:21 | declaration of myFourthFunction | | 1 | c_linkage |
|
||||
| declarationEntry.cpp:9:6:9:15 | declaration of myFunction | | 0 | |
|
||||
| declarationEntry.cpp:11:6:11:15 | definition of myFunction | | 0 | |
|
||||
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | 0 | |
|
||||
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | 0 | |
|
||||
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | 0 | |
|
||||
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | 0 | |
|
||||
| macro.c:2:1:2:3 | declaration of foo | | 2 | c_linkage, static |
|
||||
| macro.c:4:5:4:8 | definition of main | | 1 | c_linkage |
|
||||
|
||||
@@ -2,4 +2,4 @@ import cpp
|
||||
|
||||
from FunctionDeclarationEntry fde, string imp
|
||||
where if fde.isImplicit() then imp = "isImplicit" else imp = ""
|
||||
select fde, imp
|
||||
select fde, imp, count(fde.getASpecifier()), concat(fde.getASpecifier().toString(), ", ")
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#define Foo static void foo()
|
||||
Foo;
|
||||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -8070,6 +8070,239 @@ ir.cpp:
|
||||
# 1170| Type = [ArrayType] const char[4]
|
||||
# 1170| Value = [StringLiteral] "foo"
|
||||
# 1170| ValueCategory = lvalue
|
||||
# 1173| [TopLevelFunction] void switch1Case(int)
|
||||
# 1173| params:
|
||||
# 1173| 0: [Parameter] x
|
||||
# 1173| Type = [IntType] int
|
||||
# 1173| body: [Block] { ... }
|
||||
# 1174| 0: [DeclStmt] declaration
|
||||
# 1174| 0: [VariableDeclarationEntry] definition of y
|
||||
# 1174| Type = [IntType] int
|
||||
# 1174| init: [Initializer] initializer for y
|
||||
# 1174| expr: [Literal] 0
|
||||
# 1174| Type = [IntType] int
|
||||
# 1174| Value = [Literal] 0
|
||||
# 1174| ValueCategory = prvalue
|
||||
# 1175| 1: [SwitchStmt] switch (...) ...
|
||||
# 1175| 0: [VariableAccess] x
|
||||
# 1175| Type = [IntType] int
|
||||
# 1175| ValueCategory = prvalue(load)
|
||||
# 1175| 1: [Block] { ... }
|
||||
# 1176| 0: [SwitchCase] case ...:
|
||||
# 1176| 0: [Literal] 1
|
||||
# 1176| Type = [IntType] int
|
||||
# 1176| Value = [Literal] 1
|
||||
# 1176| ValueCategory = prvalue
|
||||
# 1177| 1: [ExprStmt] ExprStmt
|
||||
# 1177| 0: [AssignExpr] ... = ...
|
||||
# 1177| Type = [IntType] int
|
||||
# 1177| ValueCategory = lvalue
|
||||
# 1177| 0: [VariableAccess] y
|
||||
# 1177| Type = [IntType] int
|
||||
# 1177| ValueCategory = lvalue
|
||||
# 1177| 1: [Literal] 2
|
||||
# 1177| Type = [IntType] int
|
||||
# 1177| Value = [Literal] 2
|
||||
# 1177| ValueCategory = prvalue
|
||||
# 1179| 2: [DeclStmt] declaration
|
||||
# 1179| 0: [VariableDeclarationEntry] definition of z
|
||||
# 1179| Type = [IntType] int
|
||||
# 1179| init: [Initializer] initializer for z
|
||||
# 1179| expr: [VariableAccess] y
|
||||
# 1179| Type = [IntType] int
|
||||
# 1179| ValueCategory = prvalue(load)
|
||||
# 1180| 3: [ReturnStmt] return ...
|
||||
# 1182| [TopLevelFunction] void switch2Case_fallthrough(int)
|
||||
# 1182| params:
|
||||
# 1182| 0: [Parameter] x
|
||||
# 1182| Type = [IntType] int
|
||||
# 1182| body: [Block] { ... }
|
||||
# 1183| 0: [DeclStmt] declaration
|
||||
# 1183| 0: [VariableDeclarationEntry] definition of y
|
||||
# 1183| Type = [IntType] int
|
||||
# 1183| init: [Initializer] initializer for y
|
||||
# 1183| expr: [Literal] 0
|
||||
# 1183| Type = [IntType] int
|
||||
# 1183| Value = [Literal] 0
|
||||
# 1183| ValueCategory = prvalue
|
||||
# 1184| 1: [SwitchStmt] switch (...) ...
|
||||
# 1184| 0: [VariableAccess] x
|
||||
# 1184| Type = [IntType] int
|
||||
# 1184| ValueCategory = prvalue(load)
|
||||
# 1184| 1: [Block] { ... }
|
||||
# 1185| 0: [SwitchCase] case ...:
|
||||
# 1185| 0: [Literal] 1
|
||||
# 1185| Type = [IntType] int
|
||||
# 1185| Value = [Literal] 1
|
||||
# 1185| ValueCategory = prvalue
|
||||
# 1186| 1: [ExprStmt] ExprStmt
|
||||
# 1186| 0: [AssignExpr] ... = ...
|
||||
# 1186| Type = [IntType] int
|
||||
# 1186| ValueCategory = lvalue
|
||||
# 1186| 0: [VariableAccess] y
|
||||
# 1186| Type = [IntType] int
|
||||
# 1186| ValueCategory = lvalue
|
||||
# 1186| 1: [Literal] 2
|
||||
# 1186| Type = [IntType] int
|
||||
# 1186| Value = [Literal] 2
|
||||
# 1186| ValueCategory = prvalue
|
||||
# 1187| 2: [SwitchCase] case ...:
|
||||
# 1187| 0: [Literal] 2
|
||||
# 1187| Type = [IntType] int
|
||||
# 1187| Value = [Literal] 2
|
||||
# 1187| ValueCategory = prvalue
|
||||
# 1188| 3: [ExprStmt] ExprStmt
|
||||
# 1188| 0: [AssignExpr] ... = ...
|
||||
# 1188| Type = [IntType] int
|
||||
# 1188| ValueCategory = lvalue
|
||||
# 1188| 0: [VariableAccess] y
|
||||
# 1188| Type = [IntType] int
|
||||
# 1188| ValueCategory = lvalue
|
||||
# 1188| 1: [Literal] 3
|
||||
# 1188| Type = [IntType] int
|
||||
# 1188| Value = [Literal] 3
|
||||
# 1188| ValueCategory = prvalue
|
||||
# 1190| 2: [DeclStmt] declaration
|
||||
# 1190| 0: [VariableDeclarationEntry] definition of z
|
||||
# 1190| Type = [IntType] int
|
||||
# 1190| init: [Initializer] initializer for z
|
||||
# 1190| expr: [VariableAccess] y
|
||||
# 1190| Type = [IntType] int
|
||||
# 1190| ValueCategory = prvalue(load)
|
||||
# 1191| 3: [ReturnStmt] return ...
|
||||
# 1193| [TopLevelFunction] void switch2Case(int)
|
||||
# 1193| params:
|
||||
# 1193| 0: [Parameter] x
|
||||
# 1193| Type = [IntType] int
|
||||
# 1193| body: [Block] { ... }
|
||||
# 1194| 0: [DeclStmt] declaration
|
||||
# 1194| 0: [VariableDeclarationEntry] definition of y
|
||||
# 1194| Type = [IntType] int
|
||||
# 1194| init: [Initializer] initializer for y
|
||||
# 1194| expr: [Literal] 0
|
||||
# 1194| Type = [IntType] int
|
||||
# 1194| Value = [Literal] 0
|
||||
# 1194| ValueCategory = prvalue
|
||||
# 1195| 1: [SwitchStmt] switch (...) ...
|
||||
# 1195| 0: [VariableAccess] x
|
||||
# 1195| Type = [IntType] int
|
||||
# 1195| ValueCategory = prvalue(load)
|
||||
# 1195| 1: [Block] { ... }
|
||||
# 1196| 0: [SwitchCase] case ...:
|
||||
# 1196| 0: [Literal] 1
|
||||
# 1196| Type = [IntType] int
|
||||
# 1196| Value = [Literal] 1
|
||||
# 1196| ValueCategory = prvalue
|
||||
# 1197| 1: [ExprStmt] ExprStmt
|
||||
# 1197| 0: [AssignExpr] ... = ...
|
||||
# 1197| Type = [IntType] int
|
||||
# 1197| ValueCategory = lvalue
|
||||
# 1197| 0: [VariableAccess] y
|
||||
# 1197| Type = [IntType] int
|
||||
# 1197| ValueCategory = lvalue
|
||||
# 1197| 1: [Literal] 2
|
||||
# 1197| Type = [IntType] int
|
||||
# 1197| Value = [Literal] 2
|
||||
# 1197| ValueCategory = prvalue
|
||||
# 1198| 2: [BreakStmt] break;
|
||||
# 1199| 3: [SwitchCase] case ...:
|
||||
# 1199| 0: [Literal] 2
|
||||
# 1199| Type = [IntType] int
|
||||
# 1199| Value = [Literal] 2
|
||||
# 1199| ValueCategory = prvalue
|
||||
# 1200| 4: [ExprStmt] ExprStmt
|
||||
# 1200| 0: [AssignExpr] ... = ...
|
||||
# 1200| Type = [IntType] int
|
||||
# 1200| ValueCategory = lvalue
|
||||
# 1200| 0: [VariableAccess] y
|
||||
# 1200| Type = [IntType] int
|
||||
# 1200| ValueCategory = lvalue
|
||||
# 1200| 1: [Literal] 3
|
||||
# 1200| Type = [IntType] int
|
||||
# 1200| Value = [Literal] 3
|
||||
# 1200| ValueCategory = prvalue
|
||||
# 1201| 2: [LabelStmt] label ...:
|
||||
# 1202| 3: [DeclStmt] declaration
|
||||
# 1202| 0: [VariableDeclarationEntry] definition of z
|
||||
# 1202| Type = [IntType] int
|
||||
# 1202| init: [Initializer] initializer for z
|
||||
# 1202| expr: [VariableAccess] y
|
||||
# 1202| Type = [IntType] int
|
||||
# 1202| ValueCategory = prvalue(load)
|
||||
# 1203| 4: [ReturnStmt] return ...
|
||||
# 1205| [TopLevelFunction] void switch2Case_default(int)
|
||||
# 1205| params:
|
||||
# 1205| 0: [Parameter] x
|
||||
# 1205| Type = [IntType] int
|
||||
# 1205| body: [Block] { ... }
|
||||
# 1206| 0: [DeclStmt] declaration
|
||||
# 1206| 0: [VariableDeclarationEntry] definition of y
|
||||
# 1206| Type = [IntType] int
|
||||
# 1206| init: [Initializer] initializer for y
|
||||
# 1206| expr: [Literal] 0
|
||||
# 1206| Type = [IntType] int
|
||||
# 1206| Value = [Literal] 0
|
||||
# 1206| ValueCategory = prvalue
|
||||
# 1207| 1: [SwitchStmt] switch (...) ...
|
||||
# 1207| 0: [VariableAccess] x
|
||||
# 1207| Type = [IntType] int
|
||||
# 1207| ValueCategory = prvalue(load)
|
||||
# 1207| 1: [Block] { ... }
|
||||
# 1208| 0: [SwitchCase] case ...:
|
||||
# 1208| 0: [Literal] 1
|
||||
# 1208| Type = [IntType] int
|
||||
# 1208| Value = [Literal] 1
|
||||
# 1208| ValueCategory = prvalue
|
||||
# 1209| 1: [ExprStmt] ExprStmt
|
||||
# 1209| 0: [AssignExpr] ... = ...
|
||||
# 1209| Type = [IntType] int
|
||||
# 1209| ValueCategory = lvalue
|
||||
# 1209| 0: [VariableAccess] y
|
||||
# 1209| Type = [IntType] int
|
||||
# 1209| ValueCategory = lvalue
|
||||
# 1209| 1: [Literal] 2
|
||||
# 1209| Type = [IntType] int
|
||||
# 1209| Value = [Literal] 2
|
||||
# 1209| ValueCategory = prvalue
|
||||
# 1210| 2: [BreakStmt] break;
|
||||
# 1212| 3: [SwitchCase] case ...:
|
||||
# 1212| 0: [Literal] 2
|
||||
# 1212| Type = [IntType] int
|
||||
# 1212| Value = [Literal] 2
|
||||
# 1212| ValueCategory = prvalue
|
||||
# 1213| 4: [ExprStmt] ExprStmt
|
||||
# 1213| 0: [AssignExpr] ... = ...
|
||||
# 1213| Type = [IntType] int
|
||||
# 1213| ValueCategory = lvalue
|
||||
# 1213| 0: [VariableAccess] y
|
||||
# 1213| Type = [IntType] int
|
||||
# 1213| ValueCategory = lvalue
|
||||
# 1213| 1: [Literal] 3
|
||||
# 1213| Type = [IntType] int
|
||||
# 1213| Value = [Literal] 3
|
||||
# 1213| ValueCategory = prvalue
|
||||
# 1214| 5: [BreakStmt] break;
|
||||
# 1216| 6: [SwitchCase] default:
|
||||
# 1217| 7: [ExprStmt] ExprStmt
|
||||
# 1217| 0: [AssignExpr] ... = ...
|
||||
# 1217| Type = [IntType] int
|
||||
# 1217| ValueCategory = lvalue
|
||||
# 1217| 0: [VariableAccess] y
|
||||
# 1217| Type = [IntType] int
|
||||
# 1217| ValueCategory = lvalue
|
||||
# 1217| 1: [Literal] 4
|
||||
# 1217| Type = [IntType] int
|
||||
# 1217| Value = [Literal] 4
|
||||
# 1217| ValueCategory = prvalue
|
||||
# 1218| 2: [LabelStmt] label ...:
|
||||
# 1219| 3: [DeclStmt] declaration
|
||||
# 1219| 0: [VariableDeclarationEntry] definition of z
|
||||
# 1219| Type = [IntType] int
|
||||
# 1219| init: [Initializer] initializer for z
|
||||
# 1219| expr: [VariableAccess] y
|
||||
# 1219| Type = [IntType] int
|
||||
# 1219| ValueCategory = prvalue(load)
|
||||
# 1220| 4: [ReturnStmt] return ...
|
||||
perf-regression.cpp:
|
||||
# 4| [CopyAssignmentOperator] Big& Big::operator=(Big const&)
|
||||
# 4| params:
|
||||
|
||||
@@ -19,6 +19,7 @@ containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -19,6 +19,7 @@ containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -1170,4 +1170,53 @@ String ReturnObjectImpl() {
|
||||
return String("foo");
|
||||
}
|
||||
|
||||
void switch1Case(int x) {
|
||||
int y = 0;
|
||||
switch(x) {
|
||||
case 1:
|
||||
y = 2;
|
||||
}
|
||||
int z = y;
|
||||
}
|
||||
|
||||
void switch2Case_fallthrough(int x) {
|
||||
int y = 0;
|
||||
switch(x) {
|
||||
case 1:
|
||||
y = 2;
|
||||
case 2:
|
||||
y = 3;
|
||||
}
|
||||
int z = y;
|
||||
}
|
||||
|
||||
void switch2Case(int x) {
|
||||
int y = 0;
|
||||
switch(x) {
|
||||
case 1:
|
||||
y = 2;
|
||||
break;
|
||||
case 2:
|
||||
y = 3;
|
||||
}
|
||||
int z = y;
|
||||
}
|
||||
|
||||
void switch2Case_default(int x) {
|
||||
int y = 0;
|
||||
switch(x) {
|
||||
case 1:
|
||||
y = 2;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
y = 3;
|
||||
break;
|
||||
|
||||
default:
|
||||
y = 4;
|
||||
}
|
||||
int z = y;
|
||||
}
|
||||
|
||||
// semmle-extractor-options: -std=c++17 --clang
|
||||
|
||||
@@ -6052,6 +6052,182 @@ ir.cpp:
|
||||
# 1169| v1169_8(void) = AliasedUse : ~mu1169_4
|
||||
# 1169| v1169_9(void) = ExitFunction :
|
||||
|
||||
# 1173| void switch1Case(int)
|
||||
# 1173| Block 0
|
||||
# 1173| v1173_1(void) = EnterFunction :
|
||||
# 1173| mu1173_2(unknown) = AliasedDefinition :
|
||||
# 1173| mu1173_3(unknown) = InitializeNonLocal :
|
||||
# 1173| mu1173_4(unknown) = UnmodeledDefinition :
|
||||
# 1173| r1173_5(glval<int>) = VariableAddress[x] :
|
||||
# 1173| mu1173_6(int) = InitializeParameter[x] : &:r1173_5
|
||||
# 1174| r1174_1(glval<int>) = VariableAddress[y] :
|
||||
# 1174| r1174_2(int) = Constant[0] :
|
||||
# 1174| mu1174_3(int) = Store : &:r1174_1, r1174_2
|
||||
# 1175| r1175_1(glval<int>) = VariableAddress[x] :
|
||||
# 1175| r1175_2(int) = Load : &:r1175_1, ~mu1173_4
|
||||
# 1175| v1175_3(void) = Switch : r1175_2
|
||||
#-----| Case[1] -> Block 1
|
||||
#-----| Default -> Block 2
|
||||
|
||||
# 1176| Block 1
|
||||
# 1176| v1176_1(void) = NoOp :
|
||||
# 1177| r1177_1(int) = Constant[2] :
|
||||
# 1177| r1177_2(glval<int>) = VariableAddress[y] :
|
||||
# 1177| mu1177_3(int) = Store : &:r1177_2, r1177_1
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 1179| Block 2
|
||||
# 1179| r1179_1(glval<int>) = VariableAddress[z] :
|
||||
# 1179| r1179_2(glval<int>) = VariableAddress[y] :
|
||||
# 1179| r1179_3(int) = Load : &:r1179_2, ~mu1173_4
|
||||
# 1179| mu1179_4(int) = Store : &:r1179_1, r1179_3
|
||||
# 1180| v1180_1(void) = NoOp :
|
||||
# 1173| v1173_7(void) = ReturnVoid :
|
||||
# 1173| v1173_8(void) = UnmodeledUse : mu*
|
||||
# 1173| v1173_9(void) = AliasedUse : ~mu1173_4
|
||||
# 1173| v1173_10(void) = ExitFunction :
|
||||
|
||||
# 1182| void switch2Case_fallthrough(int)
|
||||
# 1182| Block 0
|
||||
# 1182| v1182_1(void) = EnterFunction :
|
||||
# 1182| mu1182_2(unknown) = AliasedDefinition :
|
||||
# 1182| mu1182_3(unknown) = InitializeNonLocal :
|
||||
# 1182| mu1182_4(unknown) = UnmodeledDefinition :
|
||||
# 1182| r1182_5(glval<int>) = VariableAddress[x] :
|
||||
# 1182| mu1182_6(int) = InitializeParameter[x] : &:r1182_5
|
||||
# 1183| r1183_1(glval<int>) = VariableAddress[y] :
|
||||
# 1183| r1183_2(int) = Constant[0] :
|
||||
# 1183| mu1183_3(int) = Store : &:r1183_1, r1183_2
|
||||
# 1184| r1184_1(glval<int>) = VariableAddress[x] :
|
||||
# 1184| r1184_2(int) = Load : &:r1184_1, ~mu1182_4
|
||||
# 1184| v1184_3(void) = Switch : r1184_2
|
||||
#-----| Case[1] -> Block 1
|
||||
#-----| Case[2] -> Block 2
|
||||
#-----| Default -> Block 3
|
||||
|
||||
# 1185| Block 1
|
||||
# 1185| v1185_1(void) = NoOp :
|
||||
# 1186| r1186_1(int) = Constant[2] :
|
||||
# 1186| r1186_2(glval<int>) = VariableAddress[y] :
|
||||
# 1186| mu1186_3(int) = Store : &:r1186_2, r1186_1
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 1187| Block 2
|
||||
# 1187| v1187_1(void) = NoOp :
|
||||
# 1188| r1188_1(int) = Constant[3] :
|
||||
# 1188| r1188_2(glval<int>) = VariableAddress[y] :
|
||||
# 1188| mu1188_3(int) = Store : &:r1188_2, r1188_1
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 1190| Block 3
|
||||
# 1190| r1190_1(glval<int>) = VariableAddress[z] :
|
||||
# 1190| r1190_2(glval<int>) = VariableAddress[y] :
|
||||
# 1190| r1190_3(int) = Load : &:r1190_2, ~mu1182_4
|
||||
# 1190| mu1190_4(int) = Store : &:r1190_1, r1190_3
|
||||
# 1191| v1191_1(void) = NoOp :
|
||||
# 1182| v1182_7(void) = ReturnVoid :
|
||||
# 1182| v1182_8(void) = UnmodeledUse : mu*
|
||||
# 1182| v1182_9(void) = AliasedUse : ~mu1182_4
|
||||
# 1182| v1182_10(void) = ExitFunction :
|
||||
|
||||
# 1193| void switch2Case(int)
|
||||
# 1193| Block 0
|
||||
# 1193| v1193_1(void) = EnterFunction :
|
||||
# 1193| mu1193_2(unknown) = AliasedDefinition :
|
||||
# 1193| mu1193_3(unknown) = InitializeNonLocal :
|
||||
# 1193| mu1193_4(unknown) = UnmodeledDefinition :
|
||||
# 1193| r1193_5(glval<int>) = VariableAddress[x] :
|
||||
# 1193| mu1193_6(int) = InitializeParameter[x] : &:r1193_5
|
||||
# 1194| r1194_1(glval<int>) = VariableAddress[y] :
|
||||
# 1194| r1194_2(int) = Constant[0] :
|
||||
# 1194| mu1194_3(int) = Store : &:r1194_1, r1194_2
|
||||
# 1195| r1195_1(glval<int>) = VariableAddress[x] :
|
||||
# 1195| r1195_2(int) = Load : &:r1195_1, ~mu1193_4
|
||||
# 1195| v1195_3(void) = Switch : r1195_2
|
||||
#-----| Case[1] -> Block 1
|
||||
#-----| Case[2] -> Block 2
|
||||
#-----| Default -> Block 3
|
||||
|
||||
# 1196| Block 1
|
||||
# 1196| v1196_1(void) = NoOp :
|
||||
# 1197| r1197_1(int) = Constant[2] :
|
||||
# 1197| r1197_2(glval<int>) = VariableAddress[y] :
|
||||
# 1197| mu1197_3(int) = Store : &:r1197_2, r1197_1
|
||||
# 1198| v1198_1(void) = NoOp :
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 1199| Block 2
|
||||
# 1199| v1199_1(void) = NoOp :
|
||||
# 1200| r1200_1(int) = Constant[3] :
|
||||
# 1200| r1200_2(glval<int>) = VariableAddress[y] :
|
||||
# 1200| mu1200_3(int) = Store : &:r1200_2, r1200_1
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 1201| Block 3
|
||||
# 1201| v1201_1(void) = NoOp :
|
||||
# 1202| r1202_1(glval<int>) = VariableAddress[z] :
|
||||
# 1202| r1202_2(glval<int>) = VariableAddress[y] :
|
||||
# 1202| r1202_3(int) = Load : &:r1202_2, ~mu1193_4
|
||||
# 1202| mu1202_4(int) = Store : &:r1202_1, r1202_3
|
||||
# 1203| v1203_1(void) = NoOp :
|
||||
# 1193| v1193_7(void) = ReturnVoid :
|
||||
# 1193| v1193_8(void) = UnmodeledUse : mu*
|
||||
# 1193| v1193_9(void) = AliasedUse : ~mu1193_4
|
||||
# 1193| v1193_10(void) = ExitFunction :
|
||||
|
||||
# 1205| void switch2Case_default(int)
|
||||
# 1205| Block 0
|
||||
# 1205| v1205_1(void) = EnterFunction :
|
||||
# 1205| mu1205_2(unknown) = AliasedDefinition :
|
||||
# 1205| mu1205_3(unknown) = InitializeNonLocal :
|
||||
# 1205| mu1205_4(unknown) = UnmodeledDefinition :
|
||||
# 1205| r1205_5(glval<int>) = VariableAddress[x] :
|
||||
# 1205| mu1205_6(int) = InitializeParameter[x] : &:r1205_5
|
||||
# 1206| r1206_1(glval<int>) = VariableAddress[y] :
|
||||
# 1206| r1206_2(int) = Constant[0] :
|
||||
# 1206| mu1206_3(int) = Store : &:r1206_1, r1206_2
|
||||
# 1207| r1207_1(glval<int>) = VariableAddress[x] :
|
||||
# 1207| r1207_2(int) = Load : &:r1207_1, ~mu1205_4
|
||||
# 1207| v1207_3(void) = Switch : r1207_2
|
||||
#-----| Case[1] -> Block 1
|
||||
#-----| Case[2] -> Block 2
|
||||
#-----| Default -> Block 3
|
||||
|
||||
# 1208| Block 1
|
||||
# 1208| v1208_1(void) = NoOp :
|
||||
# 1209| r1209_1(int) = Constant[2] :
|
||||
# 1209| r1209_2(glval<int>) = VariableAddress[y] :
|
||||
# 1209| mu1209_3(int) = Store : &:r1209_2, r1209_1
|
||||
# 1210| v1210_1(void) = NoOp :
|
||||
#-----| Goto -> Block 4
|
||||
|
||||
# 1212| Block 2
|
||||
# 1212| v1212_1(void) = NoOp :
|
||||
# 1213| r1213_1(int) = Constant[3] :
|
||||
# 1213| r1213_2(glval<int>) = VariableAddress[y] :
|
||||
# 1213| mu1213_3(int) = Store : &:r1213_2, r1213_1
|
||||
# 1214| v1214_1(void) = NoOp :
|
||||
#-----| Goto -> Block 4
|
||||
|
||||
# 1216| Block 3
|
||||
# 1216| v1216_1(void) = NoOp :
|
||||
# 1217| r1217_1(int) = Constant[4] :
|
||||
# 1217| r1217_2(glval<int>) = VariableAddress[y] :
|
||||
# 1217| mu1217_3(int) = Store : &:r1217_2, r1217_1
|
||||
#-----| Goto -> Block 4
|
||||
|
||||
# 1218| Block 4
|
||||
# 1218| v1218_1(void) = NoOp :
|
||||
# 1219| r1219_1(glval<int>) = VariableAddress[z] :
|
||||
# 1219| r1219_2(glval<int>) = VariableAddress[y] :
|
||||
# 1219| r1219_3(int) = Load : &:r1219_2, ~mu1205_4
|
||||
# 1219| mu1219_4(int) = Store : &:r1219_1, r1219_3
|
||||
# 1220| v1220_1(void) = NoOp :
|
||||
# 1205| v1205_7(void) = ReturnVoid :
|
||||
# 1205| v1205_8(void) = UnmodeledUse : mu*
|
||||
# 1205| v1205_9(void) = AliasedUse : ~mu1205_4
|
||||
# 1205| v1205_10(void) = ExitFunction :
|
||||
|
||||
perf-regression.cpp:
|
||||
# 6| void Big::Big()
|
||||
# 6| Block 0
|
||||
|
||||
@@ -19,6 +19,7 @@ containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -19,6 +19,7 @@ containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -19,6 +19,7 @@ containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -886,27 +886,25 @@ ssa.cpp:
|
||||
# 199| r199_7(char *) = Load : &:r199_6, m198_11
|
||||
# 199| r199_8(char *) = Convert : r199_7
|
||||
# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8
|
||||
# 199| v199_10(void) = ^CallReadSideEffect : ~m198_4
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_9
|
||||
# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_13
|
||||
# 199| m199_13(int) = Store : &:r199_1, r199_9
|
||||
# 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_9
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_13
|
||||
# 199| m199_12(int) = Store : &:r199_1, r199_9
|
||||
# 200| r200_1(glval<unknown>) = FunctionAddress[strlen] :
|
||||
# 200| r200_2(glval<char *>) = VariableAddress[str1] :
|
||||
# 200| r200_3(char *) = Load : &:r200_2, m198_7
|
||||
# 200| r200_4(char *) = Convert : r200_3
|
||||
# 200| r200_5(int) = Call : func:r200_1, 0:r200_4
|
||||
# 200| v200_6(void) = ^CallReadSideEffect : ~m198_4
|
||||
# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_9
|
||||
# 200| r200_8(glval<int>) = VariableAddress[ret] :
|
||||
# 200| r200_9(int) = Load : &:r200_8, m199_13
|
||||
# 200| r200_10(int) = Add : r200_9, r200_5
|
||||
# 200| m200_11(int) = Store : &:r200_8, r200_10
|
||||
# 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_9
|
||||
# 200| r200_7(glval<int>) = VariableAddress[ret] :
|
||||
# 200| r200_8(int) = Load : &:r200_7, m199_12
|
||||
# 200| r200_9(int) = Add : r200_8, r200_5
|
||||
# 200| m200_10(int) = Store : &:r200_7, r200_9
|
||||
# 201| r201_1(glval<unknown>) = FunctionAddress[abs] :
|
||||
# 201| r201_2(glval<int>) = VariableAddress[x] :
|
||||
# 201| r201_3(int) = Load : &:r201_2, m198_15
|
||||
# 201| r201_4(int) = Call : func:r201_1, 0:r201_3
|
||||
# 201| r201_5(glval<int>) = VariableAddress[ret] :
|
||||
# 201| r201_6(int) = Load : &:r201_5, m200_11
|
||||
# 201| r201_6(int) = Load : &:r201_5, m200_10
|
||||
# 201| r201_7(int) = Add : r201_6, r201_4
|
||||
# 201| m201_8(int) = Store : &:r201_5, r201_7
|
||||
# 202| r202_1(glval<int>) = VariableAddress[#return] :
|
||||
@@ -1240,3 +1238,47 @@ ssa.cpp:
|
||||
# 254| v254_10(void) = UnmodeledUse : mu*
|
||||
# 254| v254_11(void) = AliasedUse : ~m262_1
|
||||
# 254| v254_12(void) = ExitFunction :
|
||||
|
||||
# 268| void* MallocAliasing(void*, int)
|
||||
# 268| Block 0
|
||||
# 268| v268_1(void) = EnterFunction :
|
||||
# 268| m268_2(unknown) = AliasedDefinition :
|
||||
# 268| m268_3(unknown) = InitializeNonLocal :
|
||||
# 268| m268_4(unknown) = Chi : total:m268_2, partial:m268_3
|
||||
# 268| mu268_5(unknown) = UnmodeledDefinition :
|
||||
# 268| r268_6(glval<void *>) = VariableAddress[s] :
|
||||
# 268| m268_7(void *) = InitializeParameter[s] : &:r268_6
|
||||
# 268| r268_8(void *) = Load : &:r268_6, m268_7
|
||||
# 268| m268_9(unknown) = InitializeIndirection[s] : &:r268_8
|
||||
# 268| r268_10(glval<int>) = VariableAddress[size] :
|
||||
# 268| m268_11(int) = InitializeParameter[size] : &:r268_10
|
||||
# 269| r269_1(glval<void *>) = VariableAddress[buf] :
|
||||
# 269| r269_2(glval<unknown>) = FunctionAddress[malloc] :
|
||||
# 269| r269_3(glval<int>) = VariableAddress[size] :
|
||||
# 269| r269_4(int) = Load : &:r269_3, m268_11
|
||||
# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4
|
||||
# 269| m269_6(unknown) = ^CallSideEffect : ~m268_9
|
||||
# 269| m269_7(unknown) = Chi : total:m268_9, partial:m269_6
|
||||
# 269| m269_8(unknown) = ^InitializeDynamicAllocation : &:r269_5
|
||||
# 269| m269_9(void *) = Store : &:r269_1, r269_5
|
||||
# 270| r270_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 270| r270_2(glval<void *>) = VariableAddress[buf] :
|
||||
# 270| r270_3(void *) = Load : &:r270_2, m269_9
|
||||
# 270| r270_4(glval<void *>) = VariableAddress[s] :
|
||||
# 270| r270_5(void *) = Load : &:r270_4, m268_7
|
||||
# 270| r270_6(glval<int>) = VariableAddress[size] :
|
||||
# 270| r270_7(int) = Load : &:r270_6, m268_11
|
||||
# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7
|
||||
# 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m269_8
|
||||
# 270| m270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7
|
||||
# 270| m270_11(unknown) = Chi : total:m269_8, partial:m270_10
|
||||
# 271| r271_1(glval<void *>) = VariableAddress[#return] :
|
||||
# 271| r271_2(glval<void *>) = VariableAddress[buf] :
|
||||
# 271| r271_3(void *) = Load : &:r271_2, m269_9
|
||||
# 271| m271_4(void *) = Store : &:r271_1, r271_3
|
||||
# 268| v268_12(void) = ReturnIndirection : &:r268_8, ~m270_11
|
||||
# 268| r268_13(glval<void *>) = VariableAddress[#return] :
|
||||
# 268| v268_14(void) = ReturnValue : &:r268_13, m271_4
|
||||
# 268| v268_15(void) = UnmodeledUse : mu*
|
||||
# 268| v268_16(void) = AliasedUse : ~m270_11
|
||||
# 268| v268_17(void) = ExitFunction :
|
||||
|
||||
@@ -883,27 +883,25 @@ ssa.cpp:
|
||||
# 199| r199_7(char *) = Load : &:r199_6, m198_11
|
||||
# 199| r199_8(char *) = Convert : r199_7
|
||||
# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8
|
||||
# 199| v199_10(void) = ^CallReadSideEffect : ~m198_4
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_9
|
||||
# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_13
|
||||
# 199| m199_13(int) = Store : &:r199_1, r199_9
|
||||
# 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~m198_9
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~m198_13
|
||||
# 199| m199_12(int) = Store : &:r199_1, r199_9
|
||||
# 200| r200_1(glval<unknown>) = FunctionAddress[strlen] :
|
||||
# 200| r200_2(glval<char *>) = VariableAddress[str1] :
|
||||
# 200| r200_3(char *) = Load : &:r200_2, m198_7
|
||||
# 200| r200_4(char *) = Convert : r200_3
|
||||
# 200| r200_5(int) = Call : func:r200_1, 0:r200_4
|
||||
# 200| v200_6(void) = ^CallReadSideEffect : ~m198_4
|
||||
# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_9
|
||||
# 200| r200_8(glval<int>) = VariableAddress[ret] :
|
||||
# 200| r200_9(int) = Load : &:r200_8, m199_13
|
||||
# 200| r200_10(int) = Add : r200_9, r200_5
|
||||
# 200| m200_11(int) = Store : &:r200_8, r200_10
|
||||
# 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~m198_9
|
||||
# 200| r200_7(glval<int>) = VariableAddress[ret] :
|
||||
# 200| r200_8(int) = Load : &:r200_7, m199_12
|
||||
# 200| r200_9(int) = Add : r200_8, r200_5
|
||||
# 200| m200_10(int) = Store : &:r200_7, r200_9
|
||||
# 201| r201_1(glval<unknown>) = FunctionAddress[abs] :
|
||||
# 201| r201_2(glval<int>) = VariableAddress[x] :
|
||||
# 201| r201_3(int) = Load : &:r201_2, m198_15
|
||||
# 201| r201_4(int) = Call : func:r201_1, 0:r201_3
|
||||
# 201| r201_5(glval<int>) = VariableAddress[ret] :
|
||||
# 201| r201_6(int) = Load : &:r201_5, m200_11
|
||||
# 201| r201_6(int) = Load : &:r201_5, m200_10
|
||||
# 201| r201_7(int) = Add : r201_6, r201_4
|
||||
# 201| m201_8(int) = Store : &:r201_5, r201_7
|
||||
# 202| r202_1(glval<int>) = VariableAddress[#return] :
|
||||
@@ -1235,3 +1233,47 @@ ssa.cpp:
|
||||
# 254| v254_10(void) = UnmodeledUse : mu*
|
||||
# 254| v254_11(void) = AliasedUse : ~m262_1
|
||||
# 254| v254_12(void) = ExitFunction :
|
||||
|
||||
# 268| void* MallocAliasing(void*, int)
|
||||
# 268| Block 0
|
||||
# 268| v268_1(void) = EnterFunction :
|
||||
# 268| m268_2(unknown) = AliasedDefinition :
|
||||
# 268| m268_3(unknown) = InitializeNonLocal :
|
||||
# 268| m268_4(unknown) = Chi : total:m268_2, partial:m268_3
|
||||
# 268| mu268_5(unknown) = UnmodeledDefinition :
|
||||
# 268| r268_6(glval<void *>) = VariableAddress[s] :
|
||||
# 268| m268_7(void *) = InitializeParameter[s] : &:r268_6
|
||||
# 268| r268_8(void *) = Load : &:r268_6, m268_7
|
||||
# 268| m268_9(unknown) = InitializeIndirection[s] : &:r268_8
|
||||
# 268| r268_10(glval<int>) = VariableAddress[size] :
|
||||
# 268| m268_11(int) = InitializeParameter[size] : &:r268_10
|
||||
# 269| r269_1(glval<void *>) = VariableAddress[buf] :
|
||||
# 269| r269_2(glval<unknown>) = FunctionAddress[malloc] :
|
||||
# 269| r269_3(glval<int>) = VariableAddress[size] :
|
||||
# 269| r269_4(int) = Load : &:r269_3, m268_11
|
||||
# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4
|
||||
# 269| m269_6(unknown) = ^CallSideEffect : ~m268_4
|
||||
# 269| m269_7(unknown) = Chi : total:m268_4, partial:m269_6
|
||||
# 269| m269_8(unknown) = ^InitializeDynamicAllocation : &:r269_5
|
||||
# 269| m269_9(void *) = Store : &:r269_1, r269_5
|
||||
# 270| r270_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 270| r270_2(glval<void *>) = VariableAddress[buf] :
|
||||
# 270| r270_3(void *) = Load : &:r270_2, m269_9
|
||||
# 270| r270_4(glval<void *>) = VariableAddress[s] :
|
||||
# 270| r270_5(void *) = Load : &:r270_4, m268_7
|
||||
# 270| r270_6(glval<int>) = VariableAddress[size] :
|
||||
# 270| r270_7(int) = Load : &:r270_6, m268_11
|
||||
# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7
|
||||
# 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~m268_9
|
||||
# 270| m270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7
|
||||
# 270| m270_11(unknown) = Chi : total:m269_8, partial:m270_10
|
||||
# 271| r271_1(glval<void *>) = VariableAddress[#return] :
|
||||
# 271| r271_2(glval<void *>) = VariableAddress[buf] :
|
||||
# 271| r271_3(void *) = Load : &:r271_2, m269_9
|
||||
# 271| m271_4(void *) = Store : &:r271_1, r271_3
|
||||
# 268| v268_12(void) = ReturnIndirection : &:r268_8, m268_9
|
||||
# 268| r268_13(glval<void *>) = VariableAddress[#return] :
|
||||
# 268| v268_14(void) = ReturnValue : &:r268_13, m271_4
|
||||
# 268| v268_15(void) = UnmodeledUse : mu*
|
||||
# 268| v268_16(void) = AliasedUse : ~m269_7
|
||||
# 268| v268_17(void) = ExitFunction :
|
||||
|
||||
@@ -15,6 +15,7 @@ containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -15,6 +15,7 @@ containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -262,3 +262,11 @@ char StringLiteralAliasing2(bool b) {
|
||||
const char* s = "Literal";
|
||||
return s[2];
|
||||
}
|
||||
|
||||
void *malloc(int size);
|
||||
|
||||
void *MallocAliasing(void *s, int size) {
|
||||
void *buf = malloc(size);
|
||||
memcpy(buf, s, size);
|
||||
return buf;
|
||||
}
|
||||
@@ -829,27 +829,25 @@ ssa.cpp:
|
||||
# 199| r199_7(char *) = Load : &:r199_6, m198_10
|
||||
# 199| r199_8(char *) = Convert : r199_7
|
||||
# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8
|
||||
# 199| v199_10(void) = ^CallReadSideEffect : ~mu198_4
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~mu198_4
|
||||
# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~mu198_4
|
||||
# 199| m199_13(int) = Store : &:r199_1, r199_9
|
||||
# 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~mu198_4
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~mu198_4
|
||||
# 199| m199_12(int) = Store : &:r199_1, r199_9
|
||||
# 200| r200_1(glval<unknown>) = FunctionAddress[strlen] :
|
||||
# 200| r200_2(glval<char *>) = VariableAddress[str1] :
|
||||
# 200| r200_3(char *) = Load : &:r200_2, m198_6
|
||||
# 200| r200_4(char *) = Convert : r200_3
|
||||
# 200| r200_5(int) = Call : func:r200_1, 0:r200_4
|
||||
# 200| v200_6(void) = ^CallReadSideEffect : ~mu198_4
|
||||
# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~mu198_4
|
||||
# 200| r200_8(glval<int>) = VariableAddress[ret] :
|
||||
# 200| r200_9(int) = Load : &:r200_8, m199_13
|
||||
# 200| r200_10(int) = Add : r200_9, r200_5
|
||||
# 200| m200_11(int) = Store : &:r200_8, r200_10
|
||||
# 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~mu198_4
|
||||
# 200| r200_7(glval<int>) = VariableAddress[ret] :
|
||||
# 200| r200_8(int) = Load : &:r200_7, m199_12
|
||||
# 200| r200_9(int) = Add : r200_8, r200_5
|
||||
# 200| m200_10(int) = Store : &:r200_7, r200_9
|
||||
# 201| r201_1(glval<unknown>) = FunctionAddress[abs] :
|
||||
# 201| r201_2(glval<int>) = VariableAddress[x] :
|
||||
# 201| r201_3(int) = Load : &:r201_2, m198_14
|
||||
# 201| r201_4(int) = Call : func:r201_1, 0:r201_3
|
||||
# 201| r201_5(glval<int>) = VariableAddress[ret] :
|
||||
# 201| r201_6(int) = Load : &:r201_5, m200_11
|
||||
# 201| r201_6(int) = Load : &:r201_5, m200_10
|
||||
# 201| r201_7(int) = Add : r201_6, r201_4
|
||||
# 201| m201_8(int) = Store : &:r201_5, r201_7
|
||||
# 202| r202_1(glval<int>) = VariableAddress[#return] :
|
||||
@@ -1149,3 +1147,44 @@ ssa.cpp:
|
||||
# 254| v254_9(void) = UnmodeledUse : mu*
|
||||
# 254| v254_10(void) = AliasedUse : ~mu254_4
|
||||
# 254| v254_11(void) = ExitFunction :
|
||||
|
||||
# 268| void* MallocAliasing(void*, int)
|
||||
# 268| Block 0
|
||||
# 268| v268_1(void) = EnterFunction :
|
||||
# 268| mu268_2(unknown) = AliasedDefinition :
|
||||
# 268| mu268_3(unknown) = InitializeNonLocal :
|
||||
# 268| mu268_4(unknown) = UnmodeledDefinition :
|
||||
# 268| r268_5(glval<void *>) = VariableAddress[s] :
|
||||
# 268| m268_6(void *) = InitializeParameter[s] : &:r268_5
|
||||
# 268| r268_7(void *) = Load : &:r268_5, m268_6
|
||||
# 268| mu268_8(unknown) = InitializeIndirection[s] : &:r268_7
|
||||
# 268| r268_9(glval<int>) = VariableAddress[size] :
|
||||
# 268| m268_10(int) = InitializeParameter[size] : &:r268_9
|
||||
# 269| r269_1(glval<void *>) = VariableAddress[buf] :
|
||||
# 269| r269_2(glval<unknown>) = FunctionAddress[malloc] :
|
||||
# 269| r269_3(glval<int>) = VariableAddress[size] :
|
||||
# 269| r269_4(int) = Load : &:r269_3, m268_10
|
||||
# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4
|
||||
# 269| mu269_6(unknown) = ^CallSideEffect : ~mu268_4
|
||||
# 269| mu269_7(unknown) = ^InitializeDynamicAllocation : &:r269_5
|
||||
# 269| m269_8(void *) = Store : &:r269_1, r269_5
|
||||
# 270| r270_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 270| r270_2(glval<void *>) = VariableAddress[buf] :
|
||||
# 270| r270_3(void *) = Load : &:r270_2, m269_8
|
||||
# 270| r270_4(glval<void *>) = VariableAddress[s] :
|
||||
# 270| r270_5(void *) = Load : &:r270_4, m268_6
|
||||
# 270| r270_6(glval<int>) = VariableAddress[size] :
|
||||
# 270| r270_7(int) = Load : &:r270_6, m268_10
|
||||
# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7
|
||||
# 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~mu268_4
|
||||
# 270| mu270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7
|
||||
# 271| r271_1(glval<void *>) = VariableAddress[#return] :
|
||||
# 271| r271_2(glval<void *>) = VariableAddress[buf] :
|
||||
# 271| r271_3(void *) = Load : &:r271_2, m269_8
|
||||
# 271| m271_4(void *) = Store : &:r271_1, r271_3
|
||||
# 268| v268_11(void) = ReturnIndirection : &:r268_7, ~mu268_4
|
||||
# 268| r268_12(glval<void *>) = VariableAddress[#return] :
|
||||
# 268| v268_13(void) = ReturnValue : &:r268_12, m271_4
|
||||
# 268| v268_14(void) = UnmodeledUse : mu*
|
||||
# 268| v268_15(void) = AliasedUse : ~mu268_4
|
||||
# 268| v268_16(void) = ExitFunction :
|
||||
|
||||
@@ -829,27 +829,25 @@ ssa.cpp:
|
||||
# 199| r199_7(char *) = Load : &:r199_6, m198_10
|
||||
# 199| r199_8(char *) = Convert : r199_7
|
||||
# 199| r199_9(int) = Call : func:r199_2, 0:r199_5, 1:r199_8
|
||||
# 199| v199_10(void) = ^CallReadSideEffect : ~mu198_4
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[0] : &:r199_5, ~mu198_4
|
||||
# 199| v199_12(void) = ^BufferReadSideEffect[1] : &:r199_8, ~mu198_4
|
||||
# 199| m199_13(int) = Store : &:r199_1, r199_9
|
||||
# 199| v199_10(void) = ^BufferReadSideEffect[0] : &:r199_5, ~mu198_4
|
||||
# 199| v199_11(void) = ^BufferReadSideEffect[1] : &:r199_8, ~mu198_4
|
||||
# 199| m199_12(int) = Store : &:r199_1, r199_9
|
||||
# 200| r200_1(glval<unknown>) = FunctionAddress[strlen] :
|
||||
# 200| r200_2(glval<char *>) = VariableAddress[str1] :
|
||||
# 200| r200_3(char *) = Load : &:r200_2, m198_6
|
||||
# 200| r200_4(char *) = Convert : r200_3
|
||||
# 200| r200_5(int) = Call : func:r200_1, 0:r200_4
|
||||
# 200| v200_6(void) = ^CallReadSideEffect : ~mu198_4
|
||||
# 200| v200_7(void) = ^BufferReadSideEffect[0] : &:r200_4, ~mu198_4
|
||||
# 200| r200_8(glval<int>) = VariableAddress[ret] :
|
||||
# 200| r200_9(int) = Load : &:r200_8, m199_13
|
||||
# 200| r200_10(int) = Add : r200_9, r200_5
|
||||
# 200| m200_11(int) = Store : &:r200_8, r200_10
|
||||
# 200| v200_6(void) = ^BufferReadSideEffect[0] : &:r200_4, ~mu198_4
|
||||
# 200| r200_7(glval<int>) = VariableAddress[ret] :
|
||||
# 200| r200_8(int) = Load : &:r200_7, m199_12
|
||||
# 200| r200_9(int) = Add : r200_8, r200_5
|
||||
# 200| m200_10(int) = Store : &:r200_7, r200_9
|
||||
# 201| r201_1(glval<unknown>) = FunctionAddress[abs] :
|
||||
# 201| r201_2(glval<int>) = VariableAddress[x] :
|
||||
# 201| r201_3(int) = Load : &:r201_2, m198_14
|
||||
# 201| r201_4(int) = Call : func:r201_1, 0:r201_3
|
||||
# 201| r201_5(glval<int>) = VariableAddress[ret] :
|
||||
# 201| r201_6(int) = Load : &:r201_5, m200_11
|
||||
# 201| r201_6(int) = Load : &:r201_5, m200_10
|
||||
# 201| r201_7(int) = Add : r201_6, r201_4
|
||||
# 201| m201_8(int) = Store : &:r201_5, r201_7
|
||||
# 202| r202_1(glval<int>) = VariableAddress[#return] :
|
||||
@@ -1149,3 +1147,44 @@ ssa.cpp:
|
||||
# 254| v254_9(void) = UnmodeledUse : mu*
|
||||
# 254| v254_10(void) = AliasedUse : ~mu254_4
|
||||
# 254| v254_11(void) = ExitFunction :
|
||||
|
||||
# 268| void* MallocAliasing(void*, int)
|
||||
# 268| Block 0
|
||||
# 268| v268_1(void) = EnterFunction :
|
||||
# 268| mu268_2(unknown) = AliasedDefinition :
|
||||
# 268| mu268_3(unknown) = InitializeNonLocal :
|
||||
# 268| mu268_4(unknown) = UnmodeledDefinition :
|
||||
# 268| r268_5(glval<void *>) = VariableAddress[s] :
|
||||
# 268| m268_6(void *) = InitializeParameter[s] : &:r268_5
|
||||
# 268| r268_7(void *) = Load : &:r268_5, m268_6
|
||||
# 268| mu268_8(unknown) = InitializeIndirection[s] : &:r268_7
|
||||
# 268| r268_9(glval<int>) = VariableAddress[size] :
|
||||
# 268| m268_10(int) = InitializeParameter[size] : &:r268_9
|
||||
# 269| r269_1(glval<void *>) = VariableAddress[buf] :
|
||||
# 269| r269_2(glval<unknown>) = FunctionAddress[malloc] :
|
||||
# 269| r269_3(glval<int>) = VariableAddress[size] :
|
||||
# 269| r269_4(int) = Load : &:r269_3, m268_10
|
||||
# 269| r269_5(void *) = Call : func:r269_2, 0:r269_4
|
||||
# 269| mu269_6(unknown) = ^CallSideEffect : ~mu268_4
|
||||
# 269| mu269_7(unknown) = ^InitializeDynamicAllocation : &:r269_5
|
||||
# 269| m269_8(void *) = Store : &:r269_1, r269_5
|
||||
# 270| r270_1(glval<unknown>) = FunctionAddress[memcpy] :
|
||||
# 270| r270_2(glval<void *>) = VariableAddress[buf] :
|
||||
# 270| r270_3(void *) = Load : &:r270_2, m269_8
|
||||
# 270| r270_4(glval<void *>) = VariableAddress[s] :
|
||||
# 270| r270_5(void *) = Load : &:r270_4, m268_6
|
||||
# 270| r270_6(glval<int>) = VariableAddress[size] :
|
||||
# 270| r270_7(int) = Load : &:r270_6, m268_10
|
||||
# 270| r270_8(void *) = Call : func:r270_1, 0:r270_3, 1:r270_5, 2:r270_7
|
||||
# 270| v270_9(void) = ^SizedBufferReadSideEffect[1] : &:r270_5, r270_7, ~mu268_4
|
||||
# 270| mu270_10(unknown) = ^SizedBufferMustWriteSideEffect[0] : &:r270_3, r270_7
|
||||
# 271| r271_1(glval<void *>) = VariableAddress[#return] :
|
||||
# 271| r271_2(glval<void *>) = VariableAddress[buf] :
|
||||
# 271| r271_3(void *) = Load : &:r271_2, m269_8
|
||||
# 271| m271_4(void *) = Store : &:r271_1, r271_3
|
||||
# 268| v268_11(void) = ReturnIndirection : &:r268_7, ~mu268_4
|
||||
# 268| r268_12(glval<void *>) = VariableAddress[#return] :
|
||||
# 268| v268_13(void) = ReturnValue : &:r268_12, m271_4
|
||||
# 268| v268_14(void) = UnmodeledUse : mu*
|
||||
# 268| v268_15(void) = AliasedUse : ~mu268_4
|
||||
# 268| v268_16(void) = ExitFunction :
|
||||
|
||||
@@ -15,6 +15,7 @@ containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -15,6 +15,7 @@ containsLoopOfForwardEdges
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -213,11 +213,9 @@
|
||||
| file://:0:0:0:0 | const lambda [] type at line 3, col. 5 |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 3, col. 5 & |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 3, col. 5 * |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 3, col. 5 *const |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 9, col. 5 |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 9, col. 5 & |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 9, col. 5 * |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 9, col. 5 *const |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 9, col. 15 |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 9, col. 15 & |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 15, col. 5 |
|
||||
@@ -225,7 +223,6 @@
|
||||
| file://:0:0:0:0 | const lambda [] type at line 22, col. 19 |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 22, col. 19 & |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 22, col. 19 * |
|
||||
| file://:0:0:0:0 | const lambda [] type at line 22, col. 19 *const |
|
||||
| file://:0:0:0:0 | declaration of 1st parameter |
|
||||
| file://:0:0:0:0 | declaration of 1st parameter |
|
||||
| file://:0:0:0:0 | declaration of 1st parameter |
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
bodies
|
||||
| test.cpp:8:3:10:3 | for(...:...) ... | test.cpp:8:28:10:3 | { ... } |
|
||||
| test.cpp:28:3:30:3 | for(...:...) ... | test.cpp:28:28:30:3 | { ... } |
|
||||
| test.cpp:44:3:46:3 | for(...:...) ... | test.cpp:44:27:46:3 | { ... } |
|
||||
variables
|
||||
| test.cpp:8:3:10:3 | for(...:...) ... | test.cpp:8:13:8:17 | value | file://:0:0:0:0 | short |
|
||||
| test.cpp:28:3:30:3 | for(...:...) ... | test.cpp:28:12:28:16 | value | file://:0:0:0:0 | int |
|
||||
| test.cpp:44:3:46:3 | for(...:...) ... | test.cpp:44:13:44:17 | value | file://:0:0:0:0 | long |
|
||||
ranges
|
||||
| test.cpp:8:3:10:3 | for(...:...) ... | test.cpp:8:21:8:25 | array | file://:0:0:0:0 | short[100] |
|
||||
| test.cpp:28:3:30:3 | for(...:...) ... | test.cpp:28:20:28:25 | vector | test.cpp:16:8:16:13 | Vector<int> |
|
||||
| test.cpp:44:3:46:3 | for(...:...) ... | test.cpp:44:21:44:24 | list | file://:0:0:0:0 | const List<long> & |
|
||||
rangeVariables
|
||||
| test.cpp:8:3:10:3 | for(...:...) ... | test.cpp:8:3:8:3 | (__range) | file://:0:0:0:0 | short(&)[100] |
|
||||
| test.cpp:28:3:30:3 | for(...:...) ... | test.cpp:28:3:28:3 | (__range) | file://:0:0:0:0 | Vector<int> & |
|
||||
| test.cpp:44:3:46:3 | for(...:...) ... | test.cpp:44:3:44:3 | (__range) | file://:0:0:0:0 | const List<long> & |
|
||||
conditions
|
||||
| test.cpp:8:3:10:3 | for(...:...) ... | file://:0:0:0:0 | ... != ... | file://:0:0:0:0 | (__begin) | file://:0:0:0:0 | (__end) |
|
||||
| test.cpp:28:3:30:3 | for(...:...) ... | test.cpp:28:20:28:20 | call to operator!= | file://:0:0:0:0 | (__begin) | file://:0:0:0:0 | (__end) |
|
||||
| test.cpp:44:3:46:3 | for(...:...) ... | file://:0:0:0:0 | ... != ... | file://:0:0:0:0 | (__begin) | file://:0:0:0:0 | (__end) |
|
||||
beginVariables
|
||||
| test.cpp:8:3:10:3 | for(...:...) ... | test.cpp:8:3:8:3 | (__begin) | file://:0:0:0:0 | short * |
|
||||
| test.cpp:28:3:30:3 | for(...:...) ... | test.cpp:28:3:28:3 | (__begin) | test.cpp:17:10:17:17 | Iterator |
|
||||
| test.cpp:44:3:46:3 | for(...:...) ... | test.cpp:44:3:44:3 | (__begin) | file://:0:0:0:0 | long * |
|
||||
endVariables
|
||||
| test.cpp:8:3:10:3 | for(...:...) ... | test.cpp:8:3:8:3 | (__end) | file://:0:0:0:0 | short * |
|
||||
| test.cpp:28:3:30:3 | for(...:...) ... | test.cpp:28:3:28:3 | (__end) | test.cpp:17:10:17:17 | Iterator |
|
||||
| test.cpp:44:3:46:3 | for(...:...) ... | test.cpp:44:3:44:3 | (__end) | file://:0:0:0:0 | long * |
|
||||
updates
|
||||
| test.cpp:8:3:10:3 | for(...:...) ... | file://:0:0:0:0 | ++ ... | file://:0:0:0:0 | (__begin) |
|
||||
| test.cpp:28:3:30:3 | for(...:...) ... | test.cpp:28:20:28:20 | call to operator++ | file://:0:0:0:0 | (__begin) |
|
||||
| test.cpp:44:3:46:3 | for(...:...) ... | file://:0:0:0:0 | ++ ... | file://:0:0:0:0 | (__begin) |
|
||||
52
cpp/ql/test/library-tests/range_based_for/range_based_for.ql
Normal file
52
cpp/ql/test/library-tests/range_based_for/range_based_for.ql
Normal file
@@ -0,0 +1,52 @@
|
||||
import cpp
|
||||
|
||||
query predicate bodies(RangeBasedForStmt rbf, Stmt body) { body = rbf.getStmt() }
|
||||
|
||||
query predicate variables(RangeBasedForStmt rbf, LocalVariable v, Type t) {
|
||||
v = rbf.getVariable() and
|
||||
t = v.getType()
|
||||
}
|
||||
|
||||
query predicate ranges(RangeBasedForStmt rbf, Expr range, Type t) {
|
||||
range = rbf.getRange() and
|
||||
t = range.getType()
|
||||
}
|
||||
|
||||
query predicate rangeVariables(RangeBasedForStmt rbf, LocalVariable rangeVar, Type t) {
|
||||
rangeVar = rbf.getRangeVariable() and
|
||||
t = rangeVar.getType()
|
||||
}
|
||||
|
||||
query predicate conditions(RangeBasedForStmt rbf, Expr condition, Expr left, Expr right) {
|
||||
condition = rbf.getCondition() and
|
||||
(
|
||||
condition instanceof BinaryOperation and
|
||||
left = condition.(BinaryOperation).getLeftOperand() and
|
||||
right = condition.(BinaryOperation).getRightOperand()
|
||||
or
|
||||
condition instanceof FunctionCall and
|
||||
left = condition.(FunctionCall).getQualifier() and
|
||||
right = condition.(FunctionCall).getArgument(0)
|
||||
)
|
||||
}
|
||||
|
||||
query predicate beginVariables(RangeBasedForStmt rbf, LocalVariable beginVar, Type t) {
|
||||
beginVar = rbf.getBeginVariable() and
|
||||
t = beginVar.getType()
|
||||
}
|
||||
|
||||
query predicate endVariables(RangeBasedForStmt rbf, LocalVariable endVar, Type t) {
|
||||
endVar = rbf.getEndVariable() and
|
||||
t = endVar.getType()
|
||||
}
|
||||
|
||||
query predicate updates(RangeBasedForStmt rbf, Expr update, Expr operand) {
|
||||
update = rbf.getUpdate() and
|
||||
(
|
||||
update instanceof UnaryOperation and
|
||||
operand = update.(UnaryOperation).getOperand()
|
||||
or
|
||||
update instanceof FunctionCall and
|
||||
operand = update.(FunctionCall).getQualifier()
|
||||
)
|
||||
}
|
||||
47
cpp/ql/test/library-tests/range_based_for/test.cpp
Normal file
47
cpp/ql/test/library-tests/range_based_for/test.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
void print_short(short);
|
||||
void print_int(int);
|
||||
void print_long(long);
|
||||
|
||||
short array[100];
|
||||
void loop_over_c_array() {
|
||||
for (auto value : array) {
|
||||
print_short(value);
|
||||
}
|
||||
}
|
||||
|
||||
// A class that can be used with range-based-for, because it has the member
|
||||
// functions `begin` and `end`.
|
||||
template<typename T>
|
||||
struct Vector {
|
||||
struct Iterator {
|
||||
const T& operator*() const;
|
||||
bool operator!=(const Iterator &rhs) const;
|
||||
Iterator operator++();
|
||||
};
|
||||
Iterator begin();
|
||||
Iterator end();
|
||||
};
|
||||
|
||||
Vector<int> vector;
|
||||
void loop_over_vector_object() {
|
||||
for (int value : vector) {
|
||||
print_int(value);
|
||||
}
|
||||
}
|
||||
|
||||
// A class that can be used with range-based-for, because there are `begin` and
|
||||
// `end` functions that take a `List` as their argument.
|
||||
template<typename T>
|
||||
struct List {};
|
||||
|
||||
template<typename T>
|
||||
T* begin(const List<T> &list);
|
||||
template<typename T>
|
||||
T* end(const List<T> &list);
|
||||
|
||||
void loop_over_list_object(const List<long> &list) {
|
||||
for (auto value : list) {
|
||||
print_long(value);
|
||||
}
|
||||
}
|
||||
@@ -568,6 +568,7 @@ lostReachability
|
||||
| range_analysis.c:371:37:371:39 | Constant: 500 |
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -645,6 +645,7 @@ useNotDominatedByDefinition
|
||||
| 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:27:3:30 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | vla.c:3:5:3:8 | IR: main | int main(int, char**) |
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -577,6 +577,7 @@ lostReachability
|
||||
| range_analysis.c:371:37:371:39 | Constant: 500 |
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
switchInstructionWithoutDefaultEdge
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
|
||||
@@ -39,17 +39,13 @@
|
||||
| file://:0:0:0:0 | __va_list_tag && |
|
||||
| file://:0:0:0:0 | abstract |
|
||||
| file://:0:0:0:0 | action<ActionT> * |
|
||||
| file://:0:0:0:0 | action<ActionT> *const |
|
||||
| file://:0:0:0:0 | action<actor1<composite<int>>> & |
|
||||
| file://:0:0:0:0 | action<actor1<composite<int>>> && |
|
||||
| file://:0:0:0:0 | action<actor1<composite<int>>> * |
|
||||
| file://:0:0:0:0 | action<actor1<composite<int>>> *const |
|
||||
| file://:0:0:0:0 | actor1<BaseT> * |
|
||||
| file://:0:0:0:0 | actor1<BaseT> *const |
|
||||
| file://:0:0:0:0 | actor1<composite<int>> & |
|
||||
| file://:0:0:0:0 | actor1<composite<int>> && |
|
||||
| file://:0:0:0:0 | actor1<composite<int>> * |
|
||||
| file://:0:0:0:0 | actor1<composite<int>> *const |
|
||||
| file://:0:0:0:0 | atomic |
|
||||
| file://:0:0:0:0 | auto |
|
||||
| file://:0:0:0:0 | auto |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user