Merge branch 'master' into CWE-643

This commit is contained in:
SpaceWhite 2020-03-07 11:47:53 +09:00
Родитель 949914e48a 3ae1aada37
Коммит 2ec107bc2d
943 изменённых файлов: 49930 добавлений и 23906 удалений

Просмотреть файл

@ -2,5 +2,4 @@
"*/ql/test/qlpack.yml",
"*/upgrades/qlpack.yml",
"misc/legacy-support/*/qlpack.yml",
"misc/suite-helpers/qlpack.yml",
"codeql/.codeqlmanifest.json" ] }
"misc/suite-helpers/qlpack.yml" ] }

1
.gitignore поставляемый
Просмотреть файл

@ -1,6 +1,7 @@
# editor and OS artifacts
*~
.DS_STORE
*.swp
# query compilation caches
.cache

Просмотреть файл

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

Просмотреть файл

@ -19,6 +19,7 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
| Memory is never freed (`cpp/memory-never-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
| Memory may not be freed (`cpp/memory-may-not-be-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
| Missing return statement (`cpp/missing-return`) | Fewer false positive results | Functions containing `asm` statements are no longer highlighted by this query. |
| No space for zero terminator (`cpp/no-space-for-terminator`) | More correct results | String arguments to formatting functions are now (usually) expected to be null terminated strings. |
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | | This query is no longer run on LGTM. |
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |
| Overloaded assignment does not return 'this' (`cpp/assignment-does-not-return-this`) | Fewer false positive results | This query no longer reports incorrect results in template classes. |
@ -26,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`
@ -45,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.

Просмотреть файл

@ -18,23 +18,28 @@ The following changes in version 1.24 affect C# analysis in all applications.
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the variable is named `_` in a `foreach` statement. |
| Potentially dangerous use of non-short-circuit logic (`cs/non-short-circuit`) | Fewer false positive results | Results have been removed when the expression contains an `out` parameter. |
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | More results | Results are reported from parameters with a default value of `null`. |
| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the value assigned is an (implicitly or explicitly) cast default-like value. For example, `var s = (string)null` and `string s = default`. |
## Removal of old queries
## Changes to code extraction
* Tuple expressions, for example `(int,bool)` in `default((int,bool))` are now extracted correctly.
* Expression nullability flow state is extracted.
* Expression nullability flow state is extracted.
* Implicitly typed `stackalloc` expressions are now extracted correctly.
* The difference between `stackalloc` array creations and normal array creations is extracted.
## 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()`.
* `stackalloc` array creations are now represented by the QL class `Stackalloc`. Previously they were represented by the class `ArrayCreation`.
## Changes to autobuilder

Просмотреть файл

@ -5,15 +5,17 @@ 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
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
| Disabled Spring CSRF protection (`java/spring-disabled-csrf-protection`) | security, external/cwe/cwe-352 | Finds disabled Cross-Site Request Forgery (CSRF) protection in Spring. |
| Disabled Spring CSRF protection (`java/spring-disabled-csrf-protection`) | security, external/cwe/cwe-352 | Finds disabled Cross-Site Request Forgery (CSRF) protection in Spring. Results are shown on LGTM by default. |
| Failure to use HTTPS or SFTP URL in Maven artifact upload/download (`java/maven/non-https-url`) | security, external/cwe/cwe-300, external/cwe/cwe-319, external/cwe/cwe-494, external/cwe/cwe-829 | Finds use of insecure protocols during Maven dependency resolution. Results are shown on LGTM by default. |
| LDAP query built from user-controlled sources (`java/ldap-injection`) | security, external/cwe/cwe-090 | Finds LDAP queries vulnerable to injection of unsanitized user-controlled input. Results are shown on LGTM by default. |
| Left shift by more than the type width (`java/lshift-larger-than-type-width`) | correctness | Finds left shifts of ints by 32 bits or more and left shifts of longs by 64 bits or more. Results are shown on LGTM by default. |
| Suspicious date format (`java/suspicious-date-format`) | correctness | Finds date format patterns that use placeholders that are likely to be incorrect. |
| Suspicious date format (`java/suspicious-date-format`) | correctness | Finds date format patterns that use placeholders that are likely to be incorrect. Results are shown on LGTM by default. |
## Changes to existing queries
@ -25,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,18 +11,31 @@
* 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 the following frameworks and libraries has been improved:
- [react](https://www.npmjs.com/package/react)
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
- [Handlebars](https://www.npmjs.com/package/handlebars)
- [Electron](https://electronjs.org/)
- [Handlebars](https://www.npmjs.com/package/handlebars)
- [Koa](https://www.npmjs.com/package/koa)
- [Node.js](https://nodejs.org/)
- [Socket.IO](https://socket.io/)
- [ws](https://github.com/websockets/ws)
- [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
- [Koa](https://www.npmjs.com/package/koa)
- [chrome-remote-interface](https://www.npmjs.com/package/chrome-remote-interface)
- [for-in](https://www.npmjs.com/package/for-in)
- [for-own](https://www.npmjs.com/package/for-own)
- [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)
## New queries
@ -29,7 +44,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
@ -42,6 +61,9 @@
| 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 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. |
## Changes to libraries

Просмотреть файл

@ -0,0 +1,37 @@
# Improvements to Python analysis
The following changes in version 1.24 affect Python analysis in all applications.
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
### Web framework support
The QL-library support for the web frameworks Bottle, CherryPy, Falcon, Pyramid, TurboGears, Tornado, and Twisted have
been fixed so they provide a proper HttpRequestTaintSource, instead of a TaintSource. This will enable results for the following queries:
- py/path-injection
- py/command-line-injection
- py/reflective-xss
- py/sql-injection
- py/code-injection
- py/unsafe-deserialization
- py/url-redirection
The QL-library support for the web framework Twisted have been fixed so they provide a proper
HttpResponseTaintSink, instead of a TaintSink. This will enable results for the following
queries:
- py/reflective-xss
- py/stack-trace-exposure
## Changes to libraries

Просмотреть файл

@ -222,7 +222,14 @@
"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"
],
"IR ValueNumber": [
"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",
"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",
"cpp/ql/src/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
"cpp/ql/src/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",

Просмотреть файл

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

Просмотреть файл

@ -22,16 +22,25 @@ import semmle.code.cpp.models.interfaces.Allocation
predicate terminationProblem(AllocationExpr malloc, string msg) {
// malloc(strlen(...))
exists(StrlenCall strlen | DataFlow::localExprFlow(strlen, malloc.getSizeExpr())) and
// flows into a null-terminated string function
// flows to a call that implies this is a null-terminated string
exists(ArrayFunction af, FunctionCall fc, int arg |
DataFlow::localExprFlow(malloc, fc.getArgument(arg)) and
fc.getTarget() = af and
(
// null terminated string
// flows into null terminated string argument
af.hasArrayWithNullTerminator(arg)
or
// likely a null terminated string (such as `strcpy`, `strcat`)
// flows into likely null terminated string argument (such as `strcpy`, `strcat`)
af.hasArrayWithUnknownSize(arg)
or
// flows into string argument to a formatting function (such as `printf`)
exists(int n, FormatLiteral fl |
fc.getArgument(arg) = fc.(FormattingFunctionCall).getConversionArgument(n) and
fl = fc.(FormattingFunctionCall).getFormat() and
fl.getConversionType(n) instanceof PointerType and // `%s`, `%ws` etc
not fl.getConversionType(n) instanceof VoidPointerType and // exclude: `%p`
not fl.hasPrecision(n) // exclude: `%.*s`
)
)
) and
msg = "This allocation does not include space to null-terminate the string."

Просмотреть файл

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

Просмотреть файл

@ -139,6 +139,13 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
/** Gets a parameter of this function. */
Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) }
/**
* Gets an access of this function.
*
* To get calls to this function, use `getACallToThisFunction` instead.
*/
FunctionAccess getAnAccess() { result.getTarget() = this }
/**
* Gets the number of parameters of this function, _not_ including any
* implicit `this` parameter or any `...` varargs pseudo-parameter.
@ -174,6 +181,7 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
result = getParameter(index).getTypedName() + ", " + getParameterStringFrom(index + 1)
}
/** Gets a call to this function. */
FunctionCall getACallToThisFunction() { result.getTarget() = this }
/**

Просмотреть файл

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

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

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

Просмотреть файл

@ -6,7 +6,6 @@ private import cpp
private import semmle.code.cpp.dataflow.internal.FlowVar
private import semmle.code.cpp.models.interfaces.DataFlow
private import semmle.code.cpp.controlflow.Guards
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
cached
private newtype TNode =
@ -689,9 +688,9 @@ class BarrierGuard extends GuardCondition {
/** Gets a node guarded by this guard. */
final ExprNode getAGuardedNode() {
exists(GVN value, boolean branch |
result.getExpr() = value.getAnExpr() and
this.checks(value.getAnExpr(), branch) and
exists(SsaDefinition def, Variable v, boolean branch |
result.getExpr() = def.getAUse(v) and
this.checks(def.getAUse(v), branch) and
this.controls(result.getExpr().getBasicBlock(), branch)
)
}

Просмотреть файл

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

Просмотреть файл

@ -21,6 +21,40 @@ private predicate predictableInstruction(Instruction instr) {
predictableInstruction(instr.(UnaryInstruction).getUnary())
}
/**
* Functions that we should only allow taint to flow through (to the return
* value) if all but the source argument are 'predictable'. This is done to
* emulate the old security library's implementation rather than due to any
* strong belief that this is the right approach.
*
* Note that the list itself is not very principled; it consists of all the
* functions listed in the old security library's [default] `isPureFunction`
* that have more than one argument, but are not in the old taint tracking
* library's `returnArgument` predicate. In addition, `strlen` is included
* because it's also a special case in flow to return values.
*/
predicate predictableOnlyFlow(string name) {
name = "strcasestr" or
name = "strchnul" or
name = "strchr" or
name = "strchrnul" or
name = "strcmp" or
name = "strcspn" or
name = "strlen" or // special case
name = "strncmp" or
name = "strndup" or
name = "strnlen" or
name = "strrchr" or
name = "strspn" or
name = "strstr" or
name = "strtod" or
name = "strtof" or
name = "strtol" or
name = "strtoll" or
name = "strtoq" or
name = "strtoul"
}
private DataFlow::Node getNodeForSource(Expr source) {
isUserInput(source, _) and
(
@ -35,7 +69,7 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
override predicate isSink(DataFlow::Node sink) { any() }
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
@ -50,18 +84,15 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
override predicate isSource(DataFlow::Node source) { source = getNodeForSource(_) }
override predicate isSink(DataFlow::Node sink) {
exists(GlobalOrNamespaceVariable gv | writesVariable(sink.asInstruction(), gv))
sink.asVariable() instanceof GlobalOrNamespaceVariable
}
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
or
exists(StoreInstruction i1, LoadInstruction i2, GlobalOrNamespaceVariable gv |
writesVariable(i1, gv) and
readsVariable(i2, gv) and
i1 = n1.asInstruction() and
i2 = n2.asInstruction()
)
writesVariable(n1.asInstruction(), n2.asVariable().(GlobalOrNamespaceVariable))
or
readsVariable(n2.asInstruction(), n1.asVariable().(GlobalOrNamespaceVariable))
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
@ -71,19 +102,20 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
FromGlobalVarTaintTrackingCfg() { this = "FromGlobalVarTaintTrackingCfg" }
override predicate isSource(DataFlow::Node source) {
exists(
ToGlobalVarTaintTrackingCfg other, DataFlow::Node prevSink, GlobalOrNamespaceVariable gv
|
other.hasFlowTo(prevSink) and
writesVariable(prevSink.asInstruction(), gv) and
readsVariable(source.asInstruction(), gv)
)
// This set of sources should be reasonably small, which is good for
// performance since the set of sinks is very large.
exists(ToGlobalVarTaintTrackingCfg otherCfg | otherCfg.hasFlowTo(source))
}
override predicate isSink(DataFlow::Node sink) { any() }
override predicate isSink(DataFlow::Node sink) { exists(adjustedSink(sink)) }
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
instructionTaintStep(n1.asInstruction(), n2.asInstruction())
or
// Additional step for flow out of variables. There is no flow _into_
// variables in this configuration, so this step only serves to take flow
// out of a variable that's a source.
readsVariable(n2.asInstruction(), n1.asVariable())
}
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
@ -123,15 +155,16 @@ private predicate nodeIsBarrier(DataFlow::Node node) {
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
// Expressions computed from tainted data are also tainted
i2 =
any(CallInstruction call |
isPureFunction(call.getStaticCallTarget().getName()) and
call.getAnArgument() = i1 and
forall(Instruction arg | arg = call.getAnArgument() | arg = i1 or predictableInstruction(arg)) and
// flow through `strlen` tends to cause dubious results, if the length is
// bounded.
not call.getStaticCallTarget().getName() = "strlen"
)
exists(CallInstruction call, int argIndex | call = i2 |
isPureFunction(call.getStaticCallTarget().getName()) and
i1 = getACallArgumentOrIndirection(call, argIndex) and
forall(Instruction arg | arg = call.getAnArgument() |
arg = getACallArgumentOrIndirection(call, argIndex) or predictableInstruction(arg)
) and
// flow through `strlen` tends to cause dubious results, if the length is
// bounded.
not call.getStaticCallTarget().getName() = "strlen"
)
or
// Flow through pointer dereference
i2.(LoadInstruction).getSourceAddress() = i1
@ -172,7 +205,8 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
any(CallInstruction call |
exists(int indexIn |
modelTaintToReturnValue(call.getStaticCallTarget(), indexIn) and
i1 = getACallArgumentOrIndirection(call, indexIn)
i1 = getACallArgumentOrIndirection(call, indexIn) and
not predictableOnlyFlow(call.getStaticCallTarget().getName())
)
)
or
@ -233,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) {
@ -241,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) {
@ -301,8 +335,15 @@ private Element adjustedSink(DataFlow::Node sink) {
// For compatibility, send flow into a `NotExpr` even if it's part of a
// short-circuiting condition and thus might get skipped.
result.(NotExpr).getOperand() = sink.asExpr()
or
// Taint postfix and prefix crement operations when their operand is tainted.
result.(CrementOperation).getAnOperand() = sink.asExpr()
or
// Taint `e1 += e2`, `e &= e2` and friends when `e1` or `e2` is tainted.
result.(AssignOperation).getAnOperand() = sink.asExpr()
}
cached
predicate tainted(Expr source, Element tainted) {
exists(DefaultTaintTrackingCfg cfg, DataFlow::Node sink |
cfg.hasFlow(getNodeForSource(source), sink) and
@ -310,28 +351,18 @@ predicate tainted(Expr source, Element tainted) {
)
}
cached
predicate taintedIncludingGlobalVars(Expr source, Element tainted, string globalVar) {
tainted(source, tainted) and
globalVar = ""
or
exists(
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg, DataFlow::Node store,
GlobalOrNamespaceVariable global, DataFlow::Node load, DataFlow::Node sink
ToGlobalVarTaintTrackingCfg toCfg, FromGlobalVarTaintTrackingCfg fromCfg,
DataFlow::VariableNode variableNode, GlobalOrNamespaceVariable global, DataFlow::Node sink
|
toCfg.hasFlow(getNodeForSource(source), store) and
store
.asInstruction()
.(StoreInstruction)
.getDestinationAddress()
.(VariableAddressInstruction)
.getASTVariable() = global and
load
.asInstruction()
.(LoadInstruction)
.getSourceAddress()
.(VariableAddressInstruction)
.getASTVariable() = global and
fromCfg.hasFlow(load, sink) and
global = variableNode.getVariable() and
toCfg.hasFlow(getNodeForSource(source), variableNode) and
fromCfg.hasFlow(variableNode, sink) and
tainted = adjustedSink(sink) and
global = globalVarFromId(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()
)
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -7,17 +7,17 @@ private import DataFlowDispatch
* A data flow node that occurs as the argument of a call and is passed as-is
* to the callable. Instance arguments (`this` pointer) are also included.
*/
class ArgumentNode extends Node {
ArgumentNode() { exists(CallInstruction call | this.asInstruction() = call.getAnArgument()) }
class ArgumentNode extends InstructionNode {
ArgumentNode() { exists(CallInstruction call | this.getInstruction() = call.getAnArgument()) }
/**
* Holds if this argument occurs at the given position in the given call.
* The instance argument is considered to have index `-1`.
*/
predicate argumentOf(DataFlowCall call, int pos) {
this.asInstruction() = call.getPositionalArgument(pos)
this.getInstruction() = call.getPositionalArgument(pos)
or
this.asInstruction() = call.getThisArgument() and pos = -1
this.getInstruction() = call.getThisArgument() and pos = -1
}
/** Gets the call in which this node is an argument. */
@ -36,15 +36,15 @@ class ReturnKind extends TReturnKind {
}
/** A data flow node that occurs as the result of a `ReturnStmt`. */
class ReturnNode extends Node {
ReturnNode() { exists(ReturnValueInstruction ret | this.asInstruction() = ret.getReturnValue()) }
class ReturnNode extends InstructionNode {
ReturnNode() { exists(ReturnValueInstruction ret | this.getInstruction() = ret.getReturnValue()) }
/** Gets the kind of this returned value. */
ReturnKind getKind() { result = TNormalReturnKind() }
}
/** A data flow node that represents the output of a call. */
class OutNode extends Node {
class OutNode extends InstructionNode {
override CallInstruction instr;
/** Gets the underlying call. */
@ -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
@ -181,11 +171,17 @@ private predicate suppressUnusedType(Type t) { any() }
// Java QL library compatibility wrappers
//////////////////////////////////////////////////////////////////////////////
/** A node that performs a type cast. */
class CastNode extends Node {
class CastNode extends InstructionNode {
CastNode() { none() } // stub implementation
}
class DataFlowCallable = Function;
/**
* A function that may contain code or a variable that may contain itself. When
* flow crosses from one _enclosing callable_ to another, the interprocedural
* data-flow library discards call contexts and inserts a node in the big-step
* relation used for human-readable path explanations.
*/
class DataFlowCallable = Declaration;
class DataFlowExpr = Expr;
@ -204,3 +200,5 @@ class DataFlowCall extends CallInstruction {
}
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
int accessPathLimit() { result = 5 }

Просмотреть файл

@ -3,16 +3,17 @@
*/
private import cpp
// The `ValueNumbering` library has to be imported right after `cpp` to ensure
// that the cached IR gets the same checksum here as it does in queries that use
// `ValueNumbering` without `DataFlow`.
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.ir.ValueNumbering
private import semmle.code.cpp.models.interfaces.DataFlow
/**
* A newtype wrapper to prevent accidental casts between `Node` and
* `Instruction`. This ensures we can add `Node`s that are not `Instruction`s
* in the future.
*/
private newtype TIRDataFlowNode = MkIRDataFlowNode(Instruction i)
private newtype TIRDataFlowNode =
TInstructionNode(Instruction i) or
TVariableNode(Variable var)
/**
* A node in a data flow graph.
@ -22,21 +23,19 @@ private newtype TIRDataFlowNode = MkIRDataFlowNode(Instruction i)
* `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively.
*/
class Node extends TIRDataFlowNode {
Instruction instr;
Node() { this = MkIRDataFlowNode(instr) }
/**
* INTERNAL: Do not use. Alternative name for `getFunction`.
* INTERNAL: Do not use.
*/
Function getEnclosingCallable() { result = this.getFunction() }
Declaration getEnclosingCallable() { none() } // overridden in subclasses
Function getFunction() { result = instr.getEnclosingFunction() }
/** Gets the function to which this node belongs, if any. */
Function getFunction() { none() } // overridden in subclasses
/** Gets the type of this node. */
Type getType() { result = instr.getResultType() }
Type getType() { none() } // overridden in subclasses
Instruction asInstruction() { this = MkIRDataFlowNode(result) }
/** Gets the instruction corresponding to this node, if any. */
Instruction asInstruction() { result = this.(InstructionNode).getInstruction() }
/**
* Gets the non-conversion expression corresponding to this node, if any. If
@ -44,22 +43,25 @@ class Node extends TIRDataFlowNode {
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
* expression.
*/
Expr asExpr() {
result.getConversion*() = instr.getConvertedResultExpression() and
not result instanceof Conversion
}
Expr asExpr() { result = this.(ExprNode).getExpr() }
/**
* Gets the expression corresponding to this node, if any. The returned
* expression may be a `Conversion`.
*/
Expr asConvertedExpr() { result = instr.getConvertedResultExpression() }
Expr asConvertedExpr() { result = this.(ExprNode).getConvertedExpr() }
/** Gets the argument that defines this `DefinitionByReferenceNode`, if any. */
Expr asDefiningArgument() { result = this.(DefinitionByReferenceNode).getArgument() }
/** Gets the parameter corresponding to this node, if any. */
Parameter asParameter() { result = instr.(InitializeParameterInstruction).getParameter() }
Parameter asParameter() { result = this.(ParameterNode).getParameter() }
/**
* Gets the variable corresponding to this node, if any. This can be used for
* modelling flow in and out of global variables.
*/
Variable asVariable() { result = this.(VariableNode).getVariable() }
/**
* DEPRECATED: See UninitializedNode.
@ -75,7 +77,7 @@ class Node extends TIRDataFlowNode {
Type getTypeBound() { result = getType() }
/** Gets the location of this element. */
Location getLocation() { result = instr.getLocation() }
Location getLocation() { none() } // overridden by subclasses
/**
* Holds if this element is at the specified location.
@ -90,18 +92,38 @@ class Node extends TIRDataFlowNode {
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
string toString() {
/** Gets a textual representation of this element. */
string toString() { none() } // overridden by subclasses
}
class InstructionNode extends Node, TInstructionNode {
Instruction instr;
InstructionNode() { this = TInstructionNode(instr) }
/** Gets the instruction corresponding to this node. */
Instruction getInstruction() { result = instr }
override Declaration getEnclosingCallable() { result = this.getFunction() }
override Function getFunction() { result = instr.getEnclosingFunction() }
override Type getType() { result = instr.getResultType() }
override Location getLocation() { result = instr.getLocation() }
override string toString() {
// This predicate is overridden in subclasses. This default implementation
// does not use `Instruction.toString` because that's expensive to compute.
result = this.asInstruction().getOpcode().toString()
result = this.getInstruction().getOpcode().toString()
}
}
/**
* An expression, viewed as a node in a data flow graph.
*/
class ExprNode extends Node {
ExprNode() { exists(this.asExpr()) }
class ExprNode extends InstructionNode {
ExprNode() { exists(instr.getConvertedResultExpression()) }
/**
* Gets the non-conversion expression corresponding to this node, if any. If
@ -109,13 +131,13 @@ class ExprNode extends Node {
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
* expression.
*/
Expr getExpr() { result = this.asExpr() }
Expr getExpr() { result = instr.getUnconvertedResultExpression() }
/**
* Gets the expression corresponding to this node, if any. The returned
* expression may be a `Conversion`.
*/
Expr getConvertedExpr() { result = this.asConvertedExpr() }
Expr getConvertedExpr() { result = instr.getConvertedResultExpression() }
override string toString() { result = this.asConvertedExpr().toString() }
}
@ -124,7 +146,7 @@ class ExprNode extends Node {
* The value of a parameter at function entry, viewed as a node in a data
* flow graph.
*/
class ParameterNode extends Node {
class ParameterNode extends InstructionNode {
override InitializeParameterInstruction instr;
/**
@ -138,7 +160,7 @@ class ParameterNode extends Node {
override string toString() { result = instr.getParameter().toString() }
}
private class ThisParameterNode extends Node {
private class ThisParameterNode extends InstructionNode {
override InitializeThisInstruction instr;
override string toString() { result = "this" }
@ -175,7 +197,7 @@ deprecated class UninitializedNode extends Node {
* This class exists to match the interface used by Java. There are currently no non-abstract
* classes that extend it. When we implement field flow, we can revisit this.
*/
abstract class PostUpdateNode extends Node {
abstract class PostUpdateNode extends InstructionNode {
/**
* Gets the node before the state update.
*/
@ -192,7 +214,7 @@ abstract class PostUpdateNode extends Node {
* returned. This node will have its `getArgument()` equal to `&x` and its
* `getVariableAccess()` equal to `x`.
*/
class DefinitionByReferenceNode extends Node {
class DefinitionByReferenceNode extends InstructionNode {
override WriteSideEffectInstruction instr;
/** Gets the argument corresponding to this node. */
@ -219,10 +241,41 @@ class DefinitionByReferenceNode extends Node {
}
}
/**
* A `Node` corresponding to a variable in the program, as opposed to the
* value of that variable at some particular point. This can be used for
* modelling flow in and out of global variables.
*/
class VariableNode extends Node, TVariableNode {
Variable v;
VariableNode() { this = TVariableNode(v) }
/** Gets the variable corresponding to this node. */
Variable getVariable() { result = v }
override Function getFunction() { none() }
override Declaration getEnclosingCallable() {
// When flow crosses from one _enclosing callable_ to another, the
// interprocedural data-flow library discards call contexts and inserts a
// node in the big-step relation used for human-readable path explanations.
// Therefore we want a distinct enclosing callable for each `VariableNode`,
// and that can be the `Variable` itself.
result = v
}
override Type getType() { result = v.getType() }
override Location getLocation() { result = v.getLocation() }
override string toString() { result = v.toString() }
}
/**
* Gets the node corresponding to `instr`.
*/
Node instructionNode(Instruction instr) { result.asInstruction() = instr }
InstructionNode instructionNode(Instruction instr) { result.getInstruction() = instr }
DefinitionByReferenceNode definitionByReferenceNode(Expr e) { result.getArgument() = e }
@ -243,11 +296,16 @@ ExprNode convertedExprNode(Expr e) { result.getExpr() = e }
*/
ParameterNode parameterNode(Parameter p) { result.getParameter() = p }
/** Gets the `VariableNode` corresponding to the variable `v`. */
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
@ -289,6 +347,62 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// Flow through the partial operand belongs in the taint-tracking libraries
// for now.
iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
or
// Flow through modeled functions
modelFlow(iFrom, iTo)
}
private predicate modelFlow(Instruction iFrom, Instruction iTo) {
exists(
CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut
|
call.getStaticCallTarget() = func and
func.hasDataFlow(modelIn, modelOut)
|
(
modelOut.isReturnValue() and
iTo = call
or
// TODO: Add write side effects for return values
modelOut.isReturnValueDeref() and
iTo = call
or
exists(int index, WriteSideEffectInstruction outNode |
modelOut.isParameterDeref(index) and
iTo = outNode and
outNode = getSideEffectFor(call, index)
)
// TODO: add write side effects for qualifiers
) and
(
exists(int index |
modelIn.isParameter(index) and
iFrom = call.getPositionalArgument(index)
)
or
exists(int index, ReadSideEffectInstruction read |
modelIn.isParameterDeref(index) and
read = getSideEffectFor(call, index) and
iFrom = read.getSideEffectOperand().getAnyDef()
)
or
modelIn.isQualifierAddress() and
iFrom = call.getThisArgument()
// TODO: add read side effects for qualifiers
)
)
}
/**
* Holds if the result is a side effect for instruction `call` on argument
* index `argument`. This helper predicate makes it easy to join on both of
* these columns at once, avoiding pathological join orders in case the
* argument index should get joined first.
*/
pragma[noinline]
SideEffectInstruction getSideEffectFor(CallInstruction call, int argument) {
call = result.getPrimaryInstruction() and
argument = result.(IndexedInstruction).getIndex()
}
/**

Просмотреть файл

@ -63,6 +63,7 @@ private newtype TOpcode =
TUnmodeledDefinition() or
TUnmodeledUse() or
TAliasedDefinition() or
TInitializeNonLocal() or
TAliasedUse() or
TPhi() or
TBuiltIn() or
@ -81,6 +82,7 @@ private newtype TOpcode =
TSizedBufferReadSideEffect() or
TSizedBufferMustWriteSideEffect() or
TSizedBufferMayWriteSideEffect() or
TInitializeDynamicAllocation() or
TChi() or
TInlineAsm() or
TUnreached() or
@ -212,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 }
}
@ -260,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() }
}
@ -596,6 +601,14 @@ module Opcode {
final override MemoryAccessKind getWriteMemoryAccess() { result instanceof EscapedMemoryAccess }
}
class InitializeNonLocal extends Opcode, TInitializeNonLocal {
final override string toString() { result = "InitializeNonLocal" }
final override MemoryAccessKind getWriteMemoryAccess() {
result instanceof NonLocalMemoryAccess
}
}
class AliasedUse extends Opcode, TAliasedUse {
final override string toString() { result = "AliasedUse" }
@ -657,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" }
}
@ -686,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" }

Просмотреть файл

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

Просмотреть файл

@ -266,6 +266,16 @@ module InstructionSanity {
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())
}
}
/**
@ -1192,6 +1202,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 +1355,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.
*/

Просмотреть файл

@ -11,13 +11,19 @@ cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(
Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
(
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
or
tag instanceof UnmodeledUseOperandTag
)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap

Просмотреть файл

@ -1,6 +1,5 @@
private import internal.ValueNumberingInternal
private import internal.ValueNumberingImports
private import IR
/**
* Provides additional information about value numbering in IR dumps.
@ -10,57 +9,38 @@ class ValueNumberPropertyProvider extends IRPropertyProvider {
exists(ValueNumber vn |
vn = valueNumber(instr) and
key = "valnum" and
if strictcount(vn.getAnInstruction()) > 1 then result = vn.toString() else result = "unique"
if strictcount(vn.getAnInstruction()) > 1
then result = vn.getDebugString()
else result = "unique"
)
}
}
newtype TValueNumber =
TVariableAddressValueNumber(IRFunction irFunc, IRVariable var) {
variableAddressValueNumber(_, irFunc, var)
} or
TInitializeParameterValueNumber(IRFunction irFunc, IRVariable var) {
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
stringConstantValueNumber(_, irFunc, type, value)
} or
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) {
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
ValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
} or
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, type, operand)
} or
TInheritanceConversionValueNumber(
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
ValueNumber operand
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
/**
* The value number assigned to a particular set of instructions that produce equivalent results.
*/
class ValueNumber extends TValueNumber {
final string toString() { result = getExampleInstruction().getResultId() }
final string toString() { result = "GVN" }
final Language::Location getLocation() { result = getExampleInstruction().getLocation() }
final string getDebugString() { result = strictconcat(getAnInstruction().getResultId(), ", ") }
final Language::Location getLocation() {
if
exists(Instruction i |
i = getAnInstruction() and not i.getLocation() instanceof Language::UnknownLocation
)
then
result =
min(Language::Location l |
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 Language::UnknownDefaultLocation
}
/**
* Gets the instructions that have been assigned this value number. This will always produce at
@ -85,236 +65,39 @@ class ValueNumber extends TValueNumber {
* Gets an `Operand` whose definition is exact and has this value number.
*/
final Operand getAUse() { this = valueNumber(result.getDef()) }
}
/**
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
* operand.
* For example:
* ```
* Point p = { 1, 2 };
* Point q = p;
* int a = p.x;
* ```
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
* definition because it accesses the exact same memory.
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
*/
private class CongruentCopyInstruction extends CopyInstruction {
CongruentCopyInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
final string getKind() {
this instanceof TVariableAddressValueNumber and result = "VariableAddress"
or
this instanceof TInitializeParameterValueNumber and result = "InitializeParameter"
or
this instanceof TInitializeThisValueNumber and result = "InitializeThis"
or
this instanceof TStringConstantValueNumber and result = "StringConstant"
or
this instanceof TFieldAddressValueNumber and result = "FieldAddress"
or
this instanceof TBinaryValueNumber and result = "Binary"
or
this instanceof TPointerArithmeticValueNumber and result = "PointerArithmetic"
or
this instanceof TUnaryValueNumber and result = "Unary"
or
this instanceof TInheritanceConversionValueNumber and result = "InheritanceConversion"
or
this instanceof TLoadTotalOverlapValueNumber and result = "LoadTotalOverlap"
or
this instanceof TUniqueValueNumber and result = "Unique"
}
}
/**
* Holds if this library knows how to assign a value number to the specified instruction, other than
* a `unique` value number that is never shared by multiple instructions.
*/
private predicate numberableInstruction(Instruction instr) {
instr instanceof VariableAddressInstruction
or
instr instanceof InitializeParameterInstruction
or
instr instanceof InitializeThisInstruction
or
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
or
instr instanceof FieldAddressInstruction
or
instr instanceof BinaryInstruction
or
instr instanceof UnaryInstruction and not instr instanceof CopyInstruction
or
instr instanceof PointerArithmeticInstruction
or
instr instanceof CongruentCopyInstruction
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc
}
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
instr.getValue() = value
}
private predicate stringConstantValueNumber(
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
private predicate fieldAddressValueNumber(
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
valueNumber(instr.getObjectAddress()) = objectAddress
}
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand,
ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
instr.getElementSize() = elementSize and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
valueNumber(instr.getUnary()) = operand
}
private predicate inheritanceConversionValueNumber(
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
valueNumber(instr.getUnary()) = operand
}
/**
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
not instr.getResultIRType() instanceof IRVoidType and
not numberableInstruction(instr)
}
/**
* Gets the value number assigned to `instr`, if any. Returns at most one result.
*/
cached
ValueNumber valueNumber(Instruction instr) {
result = nonUniqueValueNumber(instr)
or
exists(IRFunction irFunc |
uniqueValueNumber(instr, irFunc) and
result = TUniqueValueNumber(irFunc, instr)
)
}
ValueNumber valueNumber(Instruction instr) { result = tvalueNumber(instr) }
/**
* Gets the value number assigned to the exact definition of `op`, if any.
* Returns at most one result.
*/
ValueNumber valueNumberOfOperand(Operand op) { result = valueNumber(op.getDef()) }
/**
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
* value number.
*/
private ValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(IRVariable var |
variableAddressValueNumber(instr, irFunc, var) and
result = TVariableAddressValueNumber(irFunc, var)
)
or
exists(IRVariable var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
exists(IRType type, string value |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, ValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
)
or
exists(Opcode opcode, IRType type, ValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, type, operand) and
result = TUnaryValueNumber(irFunc, opcode, type, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(
Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
ValueNumber rightOperand
|
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
)
or
// The value number of a copy is just the value number of its source value.
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
)
)
}
ValueNumber valueNumberOfOperand(Operand op) { result = tvalueNumberOfOperand(op) }

Просмотреть файл

@ -1,2 +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

Просмотреть файл

@ -1 +1,321 @@
import semmle.code.cpp.ir.implementation.aliased_ssa.IR as IR
private import ValueNumberingImports
newtype TValueNumber =
TVariableAddressValueNumber(IRFunction irFunc, Language::AST ast) {
variableAddressValueNumber(_, irFunc, ast)
} or
TInitializeParameterValueNumber(IRFunction irFunc, Language::AST var) {
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
stringConstantValueNumber(_, irFunc, type, value)
} or
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, TValueNumber objectAddress) {
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand,
TValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, elementSize, leftOperand, rightOperand)
} or
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, TValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, operand)
} or
TInheritanceConversionValueNumber(
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
TValueNumber operand
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
TLoadTotalOverlapValueNumber(
IRFunction irFunc, IRType type, TValueNumber memOperand, TValueNumber operand
) {
loadTotalOverlapValueNumber(_, irFunc, type, memOperand, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
/**
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
* operand.
* For example:
* ```
* Point p = { 1, 2 };
* Point q = p;
* int a = p.x;
* ```
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
* definition because it accesses the exact same memory.
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
*/
class CongruentCopyInstruction extends CopyInstruction {
CongruentCopyInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
}
}
class LoadTotalOverlapInstruction extends LoadInstruction {
LoadTotalOverlapInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustTotallyOverlap
}
}
/**
* Holds if this library knows how to assign a value number to the specified instruction, other than
* a `unique` value number that is never shared by multiple instructions.
*/
private predicate numberableInstruction(Instruction instr) {
instr instanceof VariableAddressInstruction
or
instr instanceof InitializeParameterInstruction
or
instr instanceof InitializeThisInstruction
or
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
or
instr instanceof FieldAddressInstruction
or
instr instanceof BinaryInstruction
or
instr instanceof UnaryInstruction and not instr instanceof CopyInstruction
or
instr instanceof PointerArithmeticInstruction
or
instr instanceof CongruentCopyInstruction
or
instr instanceof LoadTotalOverlapInstruction
}
private predicate filteredNumberableInstruction(Instruction instr) {
// count rather than strictcount to handle missing AST elements
// separate instanceof and inline casts to avoid failed casts with a count of 0
instr instanceof VariableAddressInstruction and
count(instr.(VariableAddressInstruction).getIRVariable().getAST()) != 1
or
instr instanceof ConstantInstruction and
count(instr.getResultIRType()) != 1
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
) {
instr.getEnclosingIRFunction() = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = ast and
strictcount(instr.getIRVariable().getAST()) = 1
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, Language::AST var
) {
instr.getEnclosingIRFunction() = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc
}
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
instr.getValue() = value
}
private predicate stringConstantValueNumber(
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
private predicate fieldAddressValueNumber(
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field,
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getElementSize() = elementSize and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
instr.getOpcode() = opcode and
tvalueNumber(instr.getUnary()) = operand
}
private predicate inheritanceConversionValueNumber(
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
tvalueNumber(instr.getUnary()) = operand
}
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
tvalueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand and
instr.getResultIRType() = type
}
/**
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
not instr.getResultIRType() instanceof IRVoidType and
(
not numberableInstruction(instr)
or
filteredNumberableInstruction(instr)
)
}
/**
* Gets the value number assigned to `instr`, if any. Returns at most one result.
*/
cached
TValueNumber tvalueNumber(Instruction instr) {
result = nonUniqueValueNumber(instr)
or
exists(IRFunction irFunc |
uniqueValueNumber(instr, irFunc) and
result = TUniqueValueNumber(irFunc, instr)
)
}
/**
* Gets the value number assigned to the exact definition of `op`, if any.
* Returns at most one result.
*/
TValueNumber tvalueNumberOfOperand(Operand op) { result = tvalueNumber(op.getDef()) }
/**
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
* value number.
*/
private TValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, TValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
)
)
}

Просмотреть файл

@ -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
@ -220,9 +224,12 @@ class VariableMemoryLocation extends TVariableMemoryLocation, AllocationMemoryLo
/**
* Holds if this memory location covers the entire variable.
*/
final predicate coversEntireVariable() {
startBitOffset = 0 and
endBitOffset = var.getIRType().getByteSize() * 8
final predicate coversEntireVariable() { varIRTypeHasBitRange(startBitOffset, endBitOffset) }
pragma[noinline]
private predicate varIRTypeHasBitRange(int start, int end) {
start = 0 and
end = var.getIRType().getByteSize() * 8
}
}
@ -298,7 +305,7 @@ class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
final override string toStringInternal() { result = "{AllNonLocal}" }
final override VirtualVariable getVirtualVariable() { result = TAllAliasedMemory(irFunc, false) }
final override AliasedVirtualVariable getVirtualVariable() { result.getIRFunction() = irFunc }
final override Language::LanguageType getType() {
result = any(IRUnknownType type).getCanonicalLanguageType()
@ -311,6 +318,14 @@ class AllNonLocalMemory extends TAllNonLocalMemory, MemoryLocation {
final override string getUniqueId() { result = "{AllNonLocal}" }
final override predicate isMayAccess() { isMayAccess = true }
override predicate canDefineReadOnly() {
// A "must" access that defines all non-local memory appears only on the `InitializeNonLocal`
// instruction, which provides the initial definition for all memory outside of the current
// function's stack frame. This memory includes string literals and other read-only globals, so
// we allow such an access to be the definition for a use of a read-only location.
not isMayAccess()
}
}
/**
@ -341,16 +356,6 @@ class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation {
class AliasedVirtualVariable extends AllAliasedMemory, VirtualVariable {
AliasedVirtualVariable() { not isMayAccess() }
override predicate canDefineReadOnly() {
// A must-def of all aliased memory is only used in two places:
// 1. In the prologue of the function, to provide a definition for all memory defined before the
// function was called. In this case, it needs to provide a definition even for read-only
// non-local variables.
// 2. As the result of a `Chi` instruction. These don't participate in overlap analysis, so it's
// OK if we let this predicate hold in that case.
any()
}
}
/**
@ -405,10 +410,16 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
use instanceof AllNonLocalMemory and
result instanceof MustExactlyOverlap
or
// AllNonLocalMemory may partially overlap any location within the same virtual variable,
// except a local variable.
result instanceof MayPartiallyOverlap and
not use.isAlwaysAllocatedOnStack()
not use instanceof AllNonLocalMemory and
not use.isAlwaysAllocatedOnStack() and
if use instanceof VariableMemoryLocation
then
// AllNonLocalMemory totally overlaps any non-local variable.
result instanceof MustTotallyOverlap
else
// AllNonLocalMemory may partially overlap any other location within the same virtual
// variable, except a stack variable.
result instanceof MayPartiallyOverlap
)
or
def.getVirtualVariable() = use.getVirtualVariable() and

Просмотреть файл

@ -96,7 +96,7 @@ private module Cached {
}
cached
Instruction getMemoryOperandDefinition(
private Instruction getMemoryOperandDefinition0(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
exists(OldInstruction oldInstruction, OldIR::NonPhiMemoryOperand oldOperand |
@ -142,6 +142,19 @@ private module Cached {
overlap instanceof MustExactlyOverlap
}
cached
Instruction getMemoryOperandDefinition(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
// getMemoryOperandDefinition0 currently has a bug where it can match with multiple overlaps.
// This predicate ensures that the chosen overlap is the most conservative if there's any doubt.
result = getMemoryOperandDefinition0(instruction, tag, overlap) and
not (
overlap instanceof MustExactlyOverlap and
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
)
}
/**
* 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.
@ -652,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, _)
)
}
@ -759,7 +773,21 @@ module DefUse {
then defLocation = useLocation
else (
definitionHasPhiNode(defLocation, block) and
defLocation = useLocation.getVirtualVariable()
defLocation = useLocation.getVirtualVariable() and
// Handle the unusual case where a virtual variable does not overlap one of its member
// locations. For example, a definition of the virtual variable representing all aliased
// memory does not overlap a use of a string literal, because the contents of a string
// literal can never be redefined. The string literal's location could still be a member of
// the `AliasedVirtualVariable` due to something like:
// ```
// char s[10];
// strcpy(s, p);
// const char* p = b ? "SomeLiteral" : s;
// return p[3];
// ```
// In the above example, `p[3]` may access either the string literal or the local variable
// `s`, so both of those locations must be members of the `AliasedVirtualVariable`.
exists(Alias::getOverlap(defLocation, useLocation))
)
)
or

Просмотреть файл

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

Просмотреть файл

@ -266,6 +266,16 @@ module InstructionSanity {
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())
}
}
/**
@ -1192,6 +1202,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 +1355,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.
*/

Просмотреть файл

@ -11,13 +11,19 @@ cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(
Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
(
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
or
tag instanceof UnmodeledUseOperandTag
)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap

Просмотреть файл

@ -1,6 +1,5 @@
private import internal.ValueNumberingInternal
private import internal.ValueNumberingImports
private import IR
/**
* Provides additional information about value numbering in IR dumps.
@ -10,57 +9,38 @@ class ValueNumberPropertyProvider extends IRPropertyProvider {
exists(ValueNumber vn |
vn = valueNumber(instr) and
key = "valnum" and
if strictcount(vn.getAnInstruction()) > 1 then result = vn.toString() else result = "unique"
if strictcount(vn.getAnInstruction()) > 1
then result = vn.getDebugString()
else result = "unique"
)
}
}
newtype TValueNumber =
TVariableAddressValueNumber(IRFunction irFunc, IRVariable var) {
variableAddressValueNumber(_, irFunc, var)
} or
TInitializeParameterValueNumber(IRFunction irFunc, IRVariable var) {
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
stringConstantValueNumber(_, irFunc, type, value)
} or
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) {
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
ValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
} or
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, type, operand)
} or
TInheritanceConversionValueNumber(
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
ValueNumber operand
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
/**
* The value number assigned to a particular set of instructions that produce equivalent results.
*/
class ValueNumber extends TValueNumber {
final string toString() { result = getExampleInstruction().getResultId() }
final string toString() { result = "GVN" }
final Language::Location getLocation() { result = getExampleInstruction().getLocation() }
final string getDebugString() { result = strictconcat(getAnInstruction().getResultId(), ", ") }
final Language::Location getLocation() {
if
exists(Instruction i |
i = getAnInstruction() and not i.getLocation() instanceof Language::UnknownLocation
)
then
result =
min(Language::Location l |
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 Language::UnknownDefaultLocation
}
/**
* Gets the instructions that have been assigned this value number. This will always produce at
@ -85,236 +65,39 @@ class ValueNumber extends TValueNumber {
* Gets an `Operand` whose definition is exact and has this value number.
*/
final Operand getAUse() { this = valueNumber(result.getDef()) }
}
/**
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
* operand.
* For example:
* ```
* Point p = { 1, 2 };
* Point q = p;
* int a = p.x;
* ```
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
* definition because it accesses the exact same memory.
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
*/
private class CongruentCopyInstruction extends CopyInstruction {
CongruentCopyInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
final string getKind() {
this instanceof TVariableAddressValueNumber and result = "VariableAddress"
or
this instanceof TInitializeParameterValueNumber and result = "InitializeParameter"
or
this instanceof TInitializeThisValueNumber and result = "InitializeThis"
or
this instanceof TStringConstantValueNumber and result = "StringConstant"
or
this instanceof TFieldAddressValueNumber and result = "FieldAddress"
or
this instanceof TBinaryValueNumber and result = "Binary"
or
this instanceof TPointerArithmeticValueNumber and result = "PointerArithmetic"
or
this instanceof TUnaryValueNumber and result = "Unary"
or
this instanceof TInheritanceConversionValueNumber and result = "InheritanceConversion"
or
this instanceof TLoadTotalOverlapValueNumber and result = "LoadTotalOverlap"
or
this instanceof TUniqueValueNumber and result = "Unique"
}
}
/**
* Holds if this library knows how to assign a value number to the specified instruction, other than
* a `unique` value number that is never shared by multiple instructions.
*/
private predicate numberableInstruction(Instruction instr) {
instr instanceof VariableAddressInstruction
or
instr instanceof InitializeParameterInstruction
or
instr instanceof InitializeThisInstruction
or
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
or
instr instanceof FieldAddressInstruction
or
instr instanceof BinaryInstruction
or
instr instanceof UnaryInstruction and not instr instanceof CopyInstruction
or
instr instanceof PointerArithmeticInstruction
or
instr instanceof CongruentCopyInstruction
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc
}
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
instr.getValue() = value
}
private predicate stringConstantValueNumber(
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
private predicate fieldAddressValueNumber(
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
valueNumber(instr.getObjectAddress()) = objectAddress
}
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand,
ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
instr.getElementSize() = elementSize and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
valueNumber(instr.getUnary()) = operand
}
private predicate inheritanceConversionValueNumber(
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
valueNumber(instr.getUnary()) = operand
}
/**
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
not instr.getResultIRType() instanceof IRVoidType and
not numberableInstruction(instr)
}
/**
* Gets the value number assigned to `instr`, if any. Returns at most one result.
*/
cached
ValueNumber valueNumber(Instruction instr) {
result = nonUniqueValueNumber(instr)
or
exists(IRFunction irFunc |
uniqueValueNumber(instr, irFunc) and
result = TUniqueValueNumber(irFunc, instr)
)
}
ValueNumber valueNumber(Instruction instr) { result = tvalueNumber(instr) }
/**
* Gets the value number assigned to the exact definition of `op`, if any.
* Returns at most one result.
*/
ValueNumber valueNumberOfOperand(Operand op) { result = valueNumber(op.getDef()) }
/**
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
* value number.
*/
private ValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(IRVariable var |
variableAddressValueNumber(instr, irFunc, var) and
result = TVariableAddressValueNumber(irFunc, var)
)
or
exists(IRVariable var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
exists(IRType type, string value |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, ValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
)
or
exists(Opcode opcode, IRType type, ValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, type, operand) and
result = TUnaryValueNumber(irFunc, opcode, type, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(
Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
ValueNumber rightOperand
|
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
)
or
// The value number of a copy is just the value number of its source value.
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
)
)
}
ValueNumber valueNumberOfOperand(Operand op) { result = tvalueNumberOfOperand(op) }

Просмотреть файл

@ -1,2 +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

Просмотреть файл

@ -1 +1,321 @@
import semmle.code.cpp.ir.implementation.raw.IR as IR
private import ValueNumberingImports
newtype TValueNumber =
TVariableAddressValueNumber(IRFunction irFunc, Language::AST ast) {
variableAddressValueNumber(_, irFunc, ast)
} or
TInitializeParameterValueNumber(IRFunction irFunc, Language::AST var) {
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
stringConstantValueNumber(_, irFunc, type, value)
} or
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, TValueNumber objectAddress) {
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand,
TValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, elementSize, leftOperand, rightOperand)
} or
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, TValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, operand)
} or
TInheritanceConversionValueNumber(
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
TValueNumber operand
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
TLoadTotalOverlapValueNumber(
IRFunction irFunc, IRType type, TValueNumber memOperand, TValueNumber operand
) {
loadTotalOverlapValueNumber(_, irFunc, type, memOperand, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
/**
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
* operand.
* For example:
* ```
* Point p = { 1, 2 };
* Point q = p;
* int a = p.x;
* ```
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
* definition because it accesses the exact same memory.
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
*/
class CongruentCopyInstruction extends CopyInstruction {
CongruentCopyInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
}
}
class LoadTotalOverlapInstruction extends LoadInstruction {
LoadTotalOverlapInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustTotallyOverlap
}
}
/**
* Holds if this library knows how to assign a value number to the specified instruction, other than
* a `unique` value number that is never shared by multiple instructions.
*/
private predicate numberableInstruction(Instruction instr) {
instr instanceof VariableAddressInstruction
or
instr instanceof InitializeParameterInstruction
or
instr instanceof InitializeThisInstruction
or
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
or
instr instanceof FieldAddressInstruction
or
instr instanceof BinaryInstruction
or
instr instanceof UnaryInstruction and not instr instanceof CopyInstruction
or
instr instanceof PointerArithmeticInstruction
or
instr instanceof CongruentCopyInstruction
or
instr instanceof LoadTotalOverlapInstruction
}
private predicate filteredNumberableInstruction(Instruction instr) {
// count rather than strictcount to handle missing AST elements
// separate instanceof and inline casts to avoid failed casts with a count of 0
instr instanceof VariableAddressInstruction and
count(instr.(VariableAddressInstruction).getIRVariable().getAST()) != 1
or
instr instanceof ConstantInstruction and
count(instr.getResultIRType()) != 1
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
) {
instr.getEnclosingIRFunction() = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = ast and
strictcount(instr.getIRVariable().getAST()) = 1
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, Language::AST var
) {
instr.getEnclosingIRFunction() = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc
}
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
instr.getValue() = value
}
private predicate stringConstantValueNumber(
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
private predicate fieldAddressValueNumber(
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field,
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getElementSize() = elementSize and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
instr.getOpcode() = opcode and
tvalueNumber(instr.getUnary()) = operand
}
private predicate inheritanceConversionValueNumber(
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
tvalueNumber(instr.getUnary()) = operand
}
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
tvalueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand and
instr.getResultIRType() = type
}
/**
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
not instr.getResultIRType() instanceof IRVoidType and
(
not numberableInstruction(instr)
or
filteredNumberableInstruction(instr)
)
}
/**
* Gets the value number assigned to `instr`, if any. Returns at most one result.
*/
cached
TValueNumber tvalueNumber(Instruction instr) {
result = nonUniqueValueNumber(instr)
or
exists(IRFunction irFunc |
uniqueValueNumber(instr, irFunc) and
result = TUniqueValueNumber(irFunc, instr)
)
}
/**
* Gets the value number assigned to the exact definition of `op`, if any.
* Returns at most one result.
*/
TValueNumber tvalueNumberOfOperand(Operand op) { result = tvalueNumber(op.getDef()) }
/**
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
* value number.
*/
private TValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, TValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
)
)
}

Просмотреть файл

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

Просмотреть файл

@ -12,12 +12,10 @@ newtype TInstructionTag =
ZeroPadStringElementIndexTag() or
ZeroPadStringElementAddressTag() or
ZeroPadStringStoreTag() or
AssignOperationLoadTag() or
AssignOperationConvertLeftTag() or
AssignOperationOpTag() or
AssignOperationConvertResultTag() or
AssignmentStoreTag() or
CrementLoadTag() or
CrementConstantTag() or
CrementOpTag() or
CrementStoreTag() or
@ -28,6 +26,7 @@ newtype TInstructionTag =
UnmodeledDefinitionTag() or
UnmodeledUseTag() or
AliasedDefinitionTag() or
InitializeNonLocalTag() or
AliasedUseTag() or
SwitchBranchTag() or
CallTargetTag() or
@ -94,8 +93,6 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = ZeroPadStringStoreTag() and result = "ZeroPadStore"
or
tag = AssignOperationLoadTag() and result = "AssignOpLoad"
or
tag = AssignOperationConvertLeftTag() and result = "AssignOpConvLeft"
or
tag = AssignOperationOpTag() and result = "AssignOpOp"
@ -104,8 +101,6 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = AssignmentStoreTag() and result = "AssignStore"
or
tag = CrementLoadTag() and result = "CrementLoad"
or
tag = CrementConstantTag() and result = "CrementConst"
or
tag = CrementOpTag() and result = "CrementOp"
@ -126,6 +121,8 @@ string getInstructionTagId(TInstructionTag tag) {
or
tag = AliasedDefinitionTag() and result = "AliasedDef"
or
tag = InitializeNonLocalTag() and result = "InitNonLocal"
or
tag = AliasedUseTag() and result = "AliasedUse"
or
tag = SwitchBranchTag() and result = "SwitchBranch"

Просмотреть файл

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

Просмотреть файл

@ -208,7 +208,7 @@ private predicate usedAsCondition(Expr expr) {
* AST as an lvalue-to-rvalue conversion, but the IR represents both a function
* lvalue and a function pointer prvalue the same.
*/
predicate ignoreLoad(Expr expr) {
private predicate ignoreLoad(Expr expr) {
expr.hasLValueToRValueConversion() and
(
expr instanceof ThisExpr or
@ -220,6 +220,34 @@ predicate ignoreLoad(Expr expr) {
)
}
/**
* Holds if `expr` should have a load on it because it will be loaded as part
* of the translation of its parent. We want to associate this load with `expr`
* itself rather than its parent since in practical applications like data flow
* we maintain that the value of the `x` in `x++` should be what's loaded from
* `x`.
*/
private predicate needsLoadForParentExpr(Expr expr) {
exists(CrementOperation crement | expr = crement.getOperand().getFullyConverted())
or
exists(AssignOperation ao | expr = ao.getLValue().getFullyConverted())
}
/**
* Holds if `expr` should have a `TranslatedLoad` on it.
*/
predicate hasTranslatedLoad(Expr expr) {
(
expr.hasLValueToRValueConversion()
or
needsLoadForParentExpr(expr)
) and
not ignoreExpr(expr) and
not isNativeCondition(expr) and
not isFlexibleCondition(expr) and
not ignoreLoad(expr)
}
newtype TTranslatedElement =
// An expression that is not being consumed as a condition
TTranslatedValueExpr(Expr expr) {
@ -229,21 +257,12 @@ newtype TTranslatedElement =
} or
// A separate element to handle the lvalue-to-rvalue conversion step of an
// expression.
TTranslatedLoad(Expr expr) {
not ignoreExpr(expr) and
not isNativeCondition(expr) and
not isFlexibleCondition(expr) and
expr.hasLValueToRValueConversion() and
not ignoreLoad(expr)
} or
TTranslatedLoad(Expr expr) { hasTranslatedLoad(expr) } or
// For expressions that would not otherwise generate an instruction.
TTranslatedResultCopy(Expr expr) {
not ignoreExpr(expr) and
exprNeedsCopyIfNotLoaded(expr) and
// Doesn't have a TTranslatedLoad
not (
expr.hasLValueToRValueConversion() and
not ignoreLoad(expr)
)
not hasTranslatedLoad(expr)
} or
// An expression most naturally translated as control flow.
TTranslatedNativeCondition(Expr expr) {
@ -392,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) {
(

Просмотреть файл

@ -63,9 +63,12 @@ abstract class TranslatedExpr extends TranslatedElement {
* Holds if the result of this `TranslatedExpr` is a glvalue.
*/
predicate isResultGLValue() {
// This implementation is overridden in `TranslatedCoreExpr` to mark them
// as glvalues if they have loads on them. It's not overridden in
// `TranslatedResultCopy` since result copies never have loads.
// This implementation is overridden in `TranslatedCoreExpr` to mark them as
// glvalues if they have loads on them. It's also overridden in
// `TranslatedLoad` to always mark loads as glvalues since a
// `TranslatedLoad` may have been created as a result of
// `needsLoadForParentExpr`. It's not overridden in `TranslatedResultCopy`
// since result copies never have loads.
expr.isGLValueCategory()
}
@ -103,18 +106,13 @@ abstract class TranslatedCoreExpr extends TranslatedExpr {
or
// If this TranslatedExpr doesn't produce the result, then it must represent
// a glvalue that is then loaded by a TranslatedLoad.
hasLoad()
}
final predicate hasLoad() {
expr.hasLValueToRValueConversion() and
not ignoreLoad(expr)
hasTranslatedLoad(expr)
}
final override predicate producesExprResult() {
// If there's no load, then this is the only TranslatedExpr for this
// expression.
not hasLoad() and
not hasTranslatedLoad(expr) and
// If there's a result copy, then this expression's result is the copy.
not exprNeedsCopyIfNotLoaded(expr)
}
@ -270,6 +268,8 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
resultType = getResultType()
}
override predicate isResultGLValue() { none() }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = LoadTag() and
result = getParent().getChildSuccessor(this) and
@ -298,7 +298,7 @@ class TranslatedLoad extends TranslatedExpr, TTranslatedLoad {
any()
}
private TranslatedCoreExpr getOperand() { result.getExpr() = expr }
TranslatedCoreExpr getOperand() { result.getExpr() = expr }
}
/**
@ -387,7 +387,7 @@ private int getElementSize(Type type) {
abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
override CrementOperation expr;
final override TranslatedElement getChild(int id) { id = 0 and result = getOperand() }
final override TranslatedElement getChild(int id) { id = 0 and result = getLoadedOperand() }
final override string getInstructionConstantValue(InstructionTag tag) {
tag = CrementConstantTag() and
@ -416,10 +416,6 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
}
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = CrementLoadTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(expr.getType())
or
tag = CrementConstantTag() and
opcode instanceof Opcode::Constant and
resultType = getConstantType()
@ -434,19 +430,10 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
}
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = CrementLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getOperand().getResult()
or
operandTag instanceof LoadOperandTag and
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
tag = CrementOpTag() and
(
operandTag instanceof LeftOperandTag and
result = getInstruction(CrementLoadTag())
result = getLoadedOperand().getResult()
or
operandTag instanceof RightOperandTag and
result = getInstruction(CrementConstantTag())
@ -455,21 +442,20 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
tag = CrementStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getOperand().getResult()
result = getUnloadedOperand().getResult()
or
operandTag instanceof StoreValueOperandTag and
result = getInstruction(CrementOpTag())
)
}
final override Instruction getFirstInstruction() { result = getOperand().getFirstInstruction() }
final override Instruction getFirstInstruction() {
result = getLoadedOperand().getFirstInstruction()
}
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
kind instanceof GotoEdge and
(
tag = CrementLoadTag() and
result = getInstruction(CrementConstantTag())
or
tag = CrementConstantTag() and
result = getInstruction(CrementOpTag())
or
@ -482,7 +468,7 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
}
final override Instruction getChildSuccessor(TranslatedElement child) {
child = getOperand() and result = getInstruction(CrementLoadTag())
child = getLoadedOperand() and result = getInstruction(CrementConstantTag())
}
final override int getInstructionElementSize(InstructionTag tag) {
@ -494,10 +480,20 @@ abstract class TranslatedCrementOperation extends TranslatedNonConstantExpr {
result = getElementSize(expr.getType())
}
final TranslatedExpr getOperand() {
/**
* Gets the `TranslatedLoad` on the `e` in this `e++`, which is the element
* that holds the value to be cremented. It's guaranteed that there's a load
* on `e` because of the `needsLoadForParentExpr` predicate.
*/
final TranslatedLoad getLoadedOperand() {
result = getTranslatedExpr(expr.getOperand().getFullyConverted())
}
/**
* Gets the address to which the result of this crement will be stored.
*/
final TranslatedExpr getUnloadedOperand() { result = getLoadedOperand().getOperand() }
final Opcode getOpcode() {
exists(Type resultType |
resultType = expr.getUnspecifiedType() and
@ -534,17 +530,14 @@ class TranslatedPrefixCrementOperation extends TranslatedCrementOperation {
else
// This is C++, where the result is an lvalue for the operand, and that
// lvalue is not being loaded as part of this expression.
result = getOperand().getResult()
result = getUnloadedOperand().getResult()
}
}
class TranslatedPostfixCrementOperation extends TranslatedCrementOperation {
override PostfixCrementOperation expr;
override Instruction getResult() {
// The result is a prvalue copy of the original value
result = getInstruction(CrementLoadTag())
}
override Instruction getResult() { result = getLoadedOperand().getResult() }
}
/**
@ -1231,8 +1224,8 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
}
}
abstract class TranslatedAssignment extends TranslatedNonConstantExpr {
override Assignment expr;
class TranslatedAssignExpr extends TranslatedNonConstantExpr {
override AssignExpr expr;
final override TranslatedElement getChild(int id) {
id = 0 and result = getLeftOperand()
@ -1252,7 +1245,7 @@ abstract class TranslatedAssignment extends TranslatedNonConstantExpr {
// value assigned to the left operand. If this is C++, then the result is
// an lvalue, but that lvalue is being loaded as part of this expression.
// EDG doesn't mark this as a load.
result = getStoredValue()
result = getRightOperand().getResult()
else
// This is C++, where the result is an lvalue for the left operand,
// and that lvalue is not being loaded as part of this expression.
@ -1268,10 +1261,6 @@ abstract class TranslatedAssignment extends TranslatedNonConstantExpr {
final TranslatedExpr getRightOperand() {
result = getTranslatedExpr(expr.getRValue().getFullyConverted())
}
}
class TranslatedAssignExpr extends TranslatedAssignment {
TranslatedAssignExpr() { expr instanceof AssignExpr }
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
tag = AssignmentStoreTag() and
@ -1304,23 +1293,57 @@ class TranslatedAssignExpr extends TranslatedAssignment {
result = getRightOperand().getResult()
)
}
override Instruction getStoredValue() { result = getRightOperand().getResult() }
}
class TranslatedAssignOperation extends TranslatedAssignment {
class TranslatedAssignOperation extends TranslatedNonConstantExpr {
override AssignOperation expr;
final override TranslatedElement getChild(int id) {
id = 0 and result = getLoadedLeftOperand()
or
id = 1 and result = getRightOperand()
}
final override Instruction getFirstInstruction() {
// Evaluation is right-to-left
result = getRightOperand().getFirstInstruction()
}
final override Instruction getResult() {
if expr.isPRValueCategory()
then
// If this is C, then the result of an assignment is a prvalue for the new
// value assigned to the left operand. If this is C++, then the result is
// an lvalue, but that lvalue is being loaded as part of this expression.
// EDG doesn't mark this as a load.
result = getStoredValue()
else
// This is C++, where the result is an lvalue for the left operand,
// and that lvalue is not being loaded as part of this expression.
result = getUnloadedLeftOperand().getResult()
}
final TranslatedExpr getUnloadedLeftOperand() { result = getLoadedLeftOperand().getOperand() }
/**
* Gets the `TranslatedLoad` on the `e` in this `e += ...` which is the
* element that holds the value to be cremented. It's guaranteed that there's
* a load on `e` because of the `needsLoadForParentExpr` predicate.
*/
final TranslatedLoad getLoadedLeftOperand() {
result = getTranslatedExpr(expr.getLValue().getFullyConverted())
}
/**
* Gets the address to which the result of this operation will be stored.
*/
final TranslatedExpr getRightOperand() {
result = getTranslatedExpr(expr.getRValue().getFullyConverted())
}
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
kind instanceof GotoEdge and
(
(
tag = AssignOperationLoadTag() and
if leftOperandNeedsConversion()
then result = getInstruction(AssignOperationConvertLeftTag())
else result = getInstruction(AssignOperationOpTag())
)
or
tag = AssignOperationConvertLeftTag() and
result = getInstruction(AssignOperationOpTag())
or
@ -1342,13 +1365,15 @@ class TranslatedAssignOperation extends TranslatedAssignment {
override Instruction getChildSuccessor(TranslatedElement child) {
// Operands are evaluated right-to-left.
child = getRightOperand() and
result = getLeftOperand().getFirstInstruction()
result = getLoadedLeftOperand().getFirstInstruction()
or
child = getLeftOperand() and
result = getInstruction(AssignOperationLoadTag())
child = getLoadedLeftOperand() and
if leftOperandNeedsConversion()
then result = getInstruction(AssignOperationConvertLeftTag())
else result = getInstruction(AssignOperationOpTag())
}
override Instruction getStoredValue() {
private Instruction getStoredValue() {
if leftOperandNeedsConversion()
then result = getInstruction(AssignOperationConvertResultTag())
else result = getInstruction(AssignOperationOpTag())
@ -1368,7 +1393,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
// anyway. If we really want to model this case perfectly, we'll need the
// extractor to tell us what the promoted type of the left operand would
// be.
result = getLeftOperand().getExpr().getType()
result = getLoadedLeftOperand().getExpr().getType()
else
// The right operand has already been converted to the type of the op.
result = getRightOperand().getExpr().getType()
@ -1376,7 +1401,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
private predicate leftOperandNeedsConversion() {
getConvertedLeftOperandType().getUnspecifiedType() !=
getLeftOperand().getExpr().getUnspecifiedType()
getLoadedLeftOperand().getExpr().getUnspecifiedType()
}
private Opcode getOpcode() {
@ -1406,10 +1431,6 @@ class TranslatedAssignOperation extends TranslatedAssignment {
}
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
tag = AssignOperationLoadTag() and
opcode instanceof Opcode::Load and
resultType = getTypeForPRValue(getLeftOperand().getExpr().getType())
or
tag = AssignOperationOpTag() and
opcode = getOpcode() and
resultType = getTypeForPRValue(getConvertedLeftOperandType())
@ -1425,7 +1446,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
resultType = getTypeForPRValue(getConvertedLeftOperandType())
or
tag = AssignOperationConvertResultTag() and
resultType = getTypeForPRValue(getLeftOperand().getExpr().getType())
resultType = getTypeForPRValue(getLoadedLeftOperand().getExpr().getType())
)
}
@ -1439,19 +1460,10 @@ class TranslatedAssignOperation extends TranslatedAssignment {
}
override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
tag = AssignOperationLoadTag() and
(
operandTag instanceof AddressOperandTag and
result = getLeftOperand().getResult()
or
operandTag instanceof LoadOperandTag and
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
)
or
leftOperandNeedsConversion() and
tag = AssignOperationConvertLeftTag() and
operandTag instanceof UnaryOperandTag and
result = getInstruction(AssignOperationLoadTag())
result = getLoadedLeftOperand().getResult()
or
tag = AssignOperationOpTag() and
(
@ -1459,7 +1471,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
operandTag instanceof LeftOperandTag and
if leftOperandNeedsConversion()
then result = getInstruction(AssignOperationConvertLeftTag())
else result = getInstruction(AssignOperationLoadTag())
else result = getLoadedLeftOperand().getResult()
)
or
operandTag instanceof RightOperandTag and
@ -1474,7 +1486,7 @@ class TranslatedAssignOperation extends TranslatedAssignment {
tag = AssignmentStoreTag() and
(
operandTag instanceof AddressOperandTag and
result = getLeftOperand().getResult()
result = getUnloadedLeftOperand().getResult()
or
operandTag instanceof StoreValueOperandTag and
result = getStoredValue()
@ -2457,6 +2469,9 @@ predicate exprNeedsCopyIfNotLoaded(Expr expr) {
expr instanceof PrefixCrementOperation and
not expr.isPRValueCategory() // is C++
or
// Because the load is on the `e` in `e++`.
expr instanceof PostfixCrementOperation
or
expr instanceof PointerDereferenceExpr
or
expr instanceof AddressOfExpr

Просмотреть файл

@ -71,6 +71,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
result = getInstruction(AliasedDefinitionTag())
or
tag = AliasedDefinitionTag() and
result = getInstruction(InitializeNonLocalTag())
or
tag = InitializeNonLocalTag() and
result = getInstruction(UnmodeledDefinitionTag())
or
(
@ -144,6 +147,10 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
opcode instanceof Opcode::AliasedDefinition and
resultType = getUnknownType()
or
tag = InitializeNonLocalTag() and
opcode instanceof Opcode::InitializeNonLocal and
resultType = getUnknownType()
or
tag = InitializeThisTag() and
opcode instanceof Opcode::InitializeThis and
resultType = getTypeForGLValue(getThisType())

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -266,6 +266,16 @@ module InstructionSanity {
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())
}
}
/**
@ -1192,6 +1202,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 +1355,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.
*/

Просмотреть файл

@ -11,13 +11,19 @@ cached
private newtype TOperand =
TRegisterOperand(Instruction useInstr, RegisterOperandTag tag, Instruction defInstr) {
defInstr = Construction::getRegisterOperandDefinition(useInstr, tag) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
strictcount(Construction::getRegisterOperandDefinition(useInstr, tag)) = 1
} or
TNonPhiMemoryOperand(
Instruction useInstr, MemoryOperandTag tag, Instruction defInstr, Overlap overlap
) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not Construction::isInCycle(useInstr)
not Construction::isInCycle(useInstr) and
(
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
or
tag instanceof UnmodeledUseOperandTag
)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap

Просмотреть файл

@ -1,6 +1,5 @@
private import internal.ValueNumberingInternal
private import internal.ValueNumberingImports
private import IR
/**
* Provides additional information about value numbering in IR dumps.
@ -10,57 +9,38 @@ class ValueNumberPropertyProvider extends IRPropertyProvider {
exists(ValueNumber vn |
vn = valueNumber(instr) and
key = "valnum" and
if strictcount(vn.getAnInstruction()) > 1 then result = vn.toString() else result = "unique"
if strictcount(vn.getAnInstruction()) > 1
then result = vn.getDebugString()
else result = "unique"
)
}
}
newtype TValueNumber =
TVariableAddressValueNumber(IRFunction irFunc, IRVariable var) {
variableAddressValueNumber(_, irFunc, var)
} or
TInitializeParameterValueNumber(IRFunction irFunc, IRVariable var) {
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
stringConstantValueNumber(_, irFunc, type, value)
} or
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, ValueNumber objectAddress) {
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, type, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
IRFunction irFunc, Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
ValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, type, elementSize, leftOperand, rightOperand)
} or
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, type, operand)
} or
TInheritanceConversionValueNumber(
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
ValueNumber operand
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
/**
* The value number assigned to a particular set of instructions that produce equivalent results.
*/
class ValueNumber extends TValueNumber {
final string toString() { result = getExampleInstruction().getResultId() }
final string toString() { result = "GVN" }
final Language::Location getLocation() { result = getExampleInstruction().getLocation() }
final string getDebugString() { result = strictconcat(getAnInstruction().getResultId(), ", ") }
final Language::Location getLocation() {
if
exists(Instruction i |
i = getAnInstruction() and not i.getLocation() instanceof Language::UnknownLocation
)
then
result =
min(Language::Location l |
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 Language::UnknownDefaultLocation
}
/**
* Gets the instructions that have been assigned this value number. This will always produce at
@ -85,236 +65,39 @@ class ValueNumber extends TValueNumber {
* Gets an `Operand` whose definition is exact and has this value number.
*/
final Operand getAUse() { this = valueNumber(result.getDef()) }
}
/**
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
* operand.
* For example:
* ```
* Point p = { 1, 2 };
* Point q = p;
* int a = p.x;
* ```
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
* definition because it accesses the exact same memory.
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
*/
private class CongruentCopyInstruction extends CopyInstruction {
CongruentCopyInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
final string getKind() {
this instanceof TVariableAddressValueNumber and result = "VariableAddress"
or
this instanceof TInitializeParameterValueNumber and result = "InitializeParameter"
or
this instanceof TInitializeThisValueNumber and result = "InitializeThis"
or
this instanceof TStringConstantValueNumber and result = "StringConstant"
or
this instanceof TFieldAddressValueNumber and result = "FieldAddress"
or
this instanceof TBinaryValueNumber and result = "Binary"
or
this instanceof TPointerArithmeticValueNumber and result = "PointerArithmetic"
or
this instanceof TUnaryValueNumber and result = "Unary"
or
this instanceof TInheritanceConversionValueNumber and result = "InheritanceConversion"
or
this instanceof TLoadTotalOverlapValueNumber and result = "LoadTotalOverlap"
or
this instanceof TUniqueValueNumber and result = "Unique"
}
}
/**
* Holds if this library knows how to assign a value number to the specified instruction, other than
* a `unique` value number that is never shared by multiple instructions.
*/
private predicate numberableInstruction(Instruction instr) {
instr instanceof VariableAddressInstruction
or
instr instanceof InitializeParameterInstruction
or
instr instanceof InitializeThisInstruction
or
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
or
instr instanceof FieldAddressInstruction
or
instr instanceof BinaryInstruction
or
instr instanceof UnaryInstruction and not instr instanceof CopyInstruction
or
instr instanceof PointerArithmeticInstruction
or
instr instanceof CongruentCopyInstruction
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, IRVariable var
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getIRVariable() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc
}
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
instr.getValue() = value
}
private predicate stringConstantValueNumber(
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
private predicate fieldAddressValueNumber(
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field, ValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
valueNumber(instr.getObjectAddress()) = objectAddress
}
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber leftOperand,
ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, IRType type,
int elementSize, ValueNumber leftOperand, ValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
instr.getElementSize() = elementSize and
valueNumber(instr.getLeft()) = leftOperand and
valueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, IRType type, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
instr.getOpcode() = opcode and
instr.getResultIRType() = type and
valueNumber(instr.getUnary()) = operand
}
private predicate inheritanceConversionValueNumber(
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
valueNumber(instr.getUnary()) = operand
}
/**
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
not instr.getResultIRType() instanceof IRVoidType and
not numberableInstruction(instr)
}
/**
* Gets the value number assigned to `instr`, if any. Returns at most one result.
*/
cached
ValueNumber valueNumber(Instruction instr) {
result = nonUniqueValueNumber(instr)
or
exists(IRFunction irFunc |
uniqueValueNumber(instr, irFunc) and
result = TUniqueValueNumber(irFunc, instr)
)
}
ValueNumber valueNumber(Instruction instr) { result = tvalueNumber(instr) }
/**
* Gets the value number assigned to the exact definition of `op`, if any.
* Returns at most one result.
*/
ValueNumber valueNumberOfOperand(Operand op) { result = valueNumber(op.getDef()) }
/**
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
* value number.
*/
private ValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(IRVariable var |
variableAddressValueNumber(instr, irFunc, var) and
result = TVariableAddressValueNumber(irFunc, var)
)
or
exists(IRVariable var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
exists(IRType type, string value |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, ValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, IRType type, ValueNumber leftOperand, ValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, type, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, type, leftOperand, rightOperand)
)
or
exists(Opcode opcode, IRType type, ValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, type, operand) and
result = TUnaryValueNumber(irFunc, opcode, type, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, ValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(
Opcode opcode, IRType type, int elementSize, ValueNumber leftOperand,
ValueNumber rightOperand
|
pointerArithmeticValueNumber(instr, irFunc, opcode, type, elementSize, leftOperand,
rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, type, elementSize, leftOperand, rightOperand)
)
or
// The value number of a copy is just the value number of its source value.
result = valueNumber(instr.(CongruentCopyInstruction).getSourceValue())
)
)
}
ValueNumber valueNumberOfOperand(Operand op) { result = tvalueNumberOfOperand(op) }

Просмотреть файл

@ -1,2 +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

Просмотреть файл

@ -1 +1,321 @@
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
private import ValueNumberingImports
newtype TValueNumber =
TVariableAddressValueNumber(IRFunction irFunc, Language::AST ast) {
variableAddressValueNumber(_, irFunc, ast)
} or
TInitializeParameterValueNumber(IRFunction irFunc, Language::AST var) {
initializeParameterValueNumber(_, irFunc, var)
} or
TInitializeThisValueNumber(IRFunction irFunc) { initializeThisValueNumber(_, irFunc) } or
TConstantValueNumber(IRFunction irFunc, IRType type, string value) {
constantValueNumber(_, irFunc, type, value)
} or
TStringConstantValueNumber(IRFunction irFunc, IRType type, string value) {
stringConstantValueNumber(_, irFunc, type, value)
} or
TFieldAddressValueNumber(IRFunction irFunc, Language::Field field, TValueNumber objectAddress) {
fieldAddressValueNumber(_, irFunc, field, objectAddress)
} or
TBinaryValueNumber(
IRFunction irFunc, Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand
) {
binaryValueNumber(_, irFunc, opcode, leftOperand, rightOperand)
} or
TPointerArithmeticValueNumber(
IRFunction irFunc, Opcode opcode, int elementSize, TValueNumber leftOperand,
TValueNumber rightOperand
) {
pointerArithmeticValueNumber(_, irFunc, opcode, elementSize, leftOperand, rightOperand)
} or
TUnaryValueNumber(IRFunction irFunc, Opcode opcode, TValueNumber operand) {
unaryValueNumber(_, irFunc, opcode, operand)
} or
TInheritanceConversionValueNumber(
IRFunction irFunc, Opcode opcode, Language::Class baseClass, Language::Class derivedClass,
TValueNumber operand
) {
inheritanceConversionValueNumber(_, irFunc, opcode, baseClass, derivedClass, operand)
} or
TLoadTotalOverlapValueNumber(
IRFunction irFunc, IRType type, TValueNumber memOperand, TValueNumber operand
) {
loadTotalOverlapValueNumber(_, irFunc, type, memOperand, operand)
} or
TUniqueValueNumber(IRFunction irFunc, Instruction instr) { uniqueValueNumber(instr, irFunc) }
/**
* A `CopyInstruction` whose source operand's value is congruent to the definition of that source
* operand.
* For example:
* ```
* Point p = { 1, 2 };
* Point q = p;
* int a = p.x;
* ```
* The use of `p` on line 2 is linked to the definition of `p` on line 1, and is congruent to that
* definition because it accesses the exact same memory.
* The use of `p.x` on line 3 is linked to the definition of `p` on line 1 as well, but is not
* congruent to that definition because `p.x` accesses only a subset of the memory defined by `p`.
*/
class CongruentCopyInstruction extends CopyInstruction {
CongruentCopyInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustExactlyOverlap
}
}
class LoadTotalOverlapInstruction extends LoadInstruction {
LoadTotalOverlapInstruction() {
this.getSourceValueOperand().getDefinitionOverlap() instanceof MustTotallyOverlap
}
}
/**
* Holds if this library knows how to assign a value number to the specified instruction, other than
* a `unique` value number that is never shared by multiple instructions.
*/
private predicate numberableInstruction(Instruction instr) {
instr instanceof VariableAddressInstruction
or
instr instanceof InitializeParameterInstruction
or
instr instanceof InitializeThisInstruction
or
instr instanceof ConstantInstruction
or
instr instanceof StringConstantInstruction
or
instr instanceof FieldAddressInstruction
or
instr instanceof BinaryInstruction
or
instr instanceof UnaryInstruction and not instr instanceof CopyInstruction
or
instr instanceof PointerArithmeticInstruction
or
instr instanceof CongruentCopyInstruction
or
instr instanceof LoadTotalOverlapInstruction
}
private predicate filteredNumberableInstruction(Instruction instr) {
// count rather than strictcount to handle missing AST elements
// separate instanceof and inline casts to avoid failed casts with a count of 0
instr instanceof VariableAddressInstruction and
count(instr.(VariableAddressInstruction).getIRVariable().getAST()) != 1
or
instr instanceof ConstantInstruction and
count(instr.getResultIRType()) != 1
or
instr instanceof FieldAddressInstruction and
count(instr.(FieldAddressInstruction).getField()) != 1
}
private predicate variableAddressValueNumber(
VariableAddressInstruction instr, IRFunction irFunc, Language::AST ast
) {
instr.getEnclosingIRFunction() = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = ast and
strictcount(instr.getIRVariable().getAST()) = 1
}
private predicate initializeParameterValueNumber(
InitializeParameterInstruction instr, IRFunction irFunc, Language::AST var
) {
instr.getEnclosingIRFunction() = irFunc and
// The underlying AST element is used as value-numbering key instead of the
// `IRVariable` to work around a problem where a variable or expression with
// multiple types gives rise to multiple `IRVariable`s.
instr.getIRVariable().getAST() = var
}
private predicate initializeThisValueNumber(InitializeThisInstruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc
}
private predicate constantValueNumber(
ConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
strictcount(instr.getResultIRType()) = 1 and
instr.getResultIRType() = type and
instr.getValue() = value
}
private predicate stringConstantValueNumber(
StringConstantInstruction instr, IRFunction irFunc, IRType type, string value
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getResultIRType() = type and
instr.getValue().getValue() = value
}
private predicate fieldAddressValueNumber(
FieldAddressInstruction instr, IRFunction irFunc, Language::Field field,
TValueNumber objectAddress
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getField() = field and
strictcount(instr.getField()) = 1 and
tvalueNumber(instr.getObjectAddress()) = objectAddress
}
private predicate binaryValueNumber(
BinaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber leftOperand,
TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof PointerArithmeticInstruction and
instr.getOpcode() = opcode and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate pointerArithmeticValueNumber(
PointerArithmeticInstruction instr, IRFunction irFunc, Opcode opcode, int elementSize,
TValueNumber leftOperand, TValueNumber rightOperand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getElementSize() = elementSize and
tvalueNumber(instr.getLeft()) = leftOperand and
tvalueNumber(instr.getRight()) = rightOperand
}
private predicate unaryValueNumber(
UnaryInstruction instr, IRFunction irFunc, Opcode opcode, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
not instr instanceof InheritanceConversionInstruction and
not instr instanceof CopyInstruction and
not instr instanceof FieldAddressInstruction and
instr.getOpcode() = opcode and
tvalueNumber(instr.getUnary()) = operand
}
private predicate inheritanceConversionValueNumber(
InheritanceConversionInstruction instr, IRFunction irFunc, Opcode opcode,
Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
instr.getOpcode() = opcode and
instr.getBaseClass() = baseClass and
instr.getDerivedClass() = derivedClass and
tvalueNumber(instr.getUnary()) = operand
}
private predicate loadTotalOverlapValueNumber(
LoadTotalOverlapInstruction instr, IRFunction irFunc, IRType type, TValueNumber memOperand,
TValueNumber operand
) {
instr.getEnclosingIRFunction() = irFunc and
tvalueNumber(instr.getAnOperand().(MemoryOperand).getAnyDef()) = memOperand and
tvalueNumberOfOperand(instr.getAnOperand().(AddressOperand)) = operand and
instr.getResultIRType() = type
}
/**
* Holds if `instr` should be assigned a unique value number because this library does not know how
* to determine if two instances of that instruction are equivalent.
*/
private predicate uniqueValueNumber(Instruction instr, IRFunction irFunc) {
instr.getEnclosingIRFunction() = irFunc and
not instr.getResultIRType() instanceof IRVoidType and
(
not numberableInstruction(instr)
or
filteredNumberableInstruction(instr)
)
}
/**
* Gets the value number assigned to `instr`, if any. Returns at most one result.
*/
cached
TValueNumber tvalueNumber(Instruction instr) {
result = nonUniqueValueNumber(instr)
or
exists(IRFunction irFunc |
uniqueValueNumber(instr, irFunc) and
result = TUniqueValueNumber(irFunc, instr)
)
}
/**
* Gets the value number assigned to the exact definition of `op`, if any.
* Returns at most one result.
*/
TValueNumber tvalueNumberOfOperand(Operand op) { result = tvalueNumber(op.getDef()) }
/**
* Gets the value number assigned to `instr`, if any, unless that instruction is assigned a unique
* value number.
*/
private TValueNumber nonUniqueValueNumber(Instruction instr) {
exists(IRFunction irFunc |
irFunc = instr.getEnclosingIRFunction() and
(
exists(Language::AST ast |
variableAddressValueNumber(instr, irFunc, ast) and
result = TVariableAddressValueNumber(irFunc, ast)
)
or
exists(Language::AST var |
initializeParameterValueNumber(instr, irFunc, var) and
result = TInitializeParameterValueNumber(irFunc, var)
)
or
initializeThisValueNumber(instr, irFunc) and
result = TInitializeThisValueNumber(irFunc)
or
exists(string value, IRType type |
constantValueNumber(instr, irFunc, type, value) and
result = TConstantValueNumber(irFunc, type, value)
)
or
exists(IRType type, string value |
stringConstantValueNumber(instr, irFunc, type, value) and
result = TStringConstantValueNumber(irFunc, type, value)
)
or
exists(Language::Field field, TValueNumber objectAddress |
fieldAddressValueNumber(instr, irFunc, field, objectAddress) and
result = TFieldAddressValueNumber(irFunc, field, objectAddress)
)
or
exists(Opcode opcode, TValueNumber leftOperand, TValueNumber rightOperand |
binaryValueNumber(instr, irFunc, opcode, leftOperand, rightOperand) and
result = TBinaryValueNumber(irFunc, opcode, leftOperand, rightOperand)
)
or
exists(Opcode opcode, TValueNumber operand |
unaryValueNumber(instr, irFunc, opcode, operand) and
result = TUnaryValueNumber(irFunc, opcode, operand)
)
or
exists(
Opcode opcode, Language::Class baseClass, Language::Class derivedClass, TValueNumber operand
|
inheritanceConversionValueNumber(instr, irFunc, opcode, baseClass, derivedClass, operand) and
result = TInheritanceConversionValueNumber(irFunc, opcode, baseClass, derivedClass, operand)
)
or
exists(Opcode opcode, int elementSize, TValueNumber leftOperand, TValueNumber rightOperand |
pointerArithmeticValueNumber(instr, irFunc, opcode, elementSize, leftOperand, rightOperand) and
result =
TPointerArithmeticValueNumber(irFunc, opcode, elementSize, leftOperand, rightOperand)
)
or
exists(IRType type, TValueNumber memOperand, TValueNumber operand |
loadTotalOverlapValueNumber(instr, irFunc, type, memOperand, operand) and
result = TLoadTotalOverlapValueNumber(irFunc, type, memOperand, operand)
)
or
// The value number of a copy is just the value number of its source value.
result = tvalueNumber(instr.(CongruentCopyInstruction).getSourceValue())
)
)
}

Просмотреть файл

@ -96,7 +96,7 @@ private module Cached {
}
cached
Instruction getMemoryOperandDefinition(
private Instruction getMemoryOperandDefinition0(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
exists(OldInstruction oldInstruction, OldIR::NonPhiMemoryOperand oldOperand |
@ -142,6 +142,19 @@ private module Cached {
overlap instanceof MustExactlyOverlap
}
cached
Instruction getMemoryOperandDefinition(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
// getMemoryOperandDefinition0 currently has a bug where it can match with multiple overlaps.
// This predicate ensures that the chosen overlap is the most conservative if there's any doubt.
result = getMemoryOperandDefinition0(instruction, tag, overlap) and
not (
overlap instanceof MustExactlyOverlap and
exists(MustTotallyOverlap o | exists(getMemoryOperandDefinition0(instruction, tag, o)))
)
}
/**
* 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.
@ -652,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, _)
)
}
@ -759,7 +773,21 @@ module DefUse {
then defLocation = useLocation
else (
definitionHasPhiNode(defLocation, block) and
defLocation = useLocation.getVirtualVariable()
defLocation = useLocation.getVirtualVariable() and
// Handle the unusual case where a virtual variable does not overlap one of its member
// locations. For example, a definition of the virtual variable representing all aliased
// memory does not overlap a use of a string literal, because the contents of a string
// literal can never be redefined. The string literal's location could still be a member of
// the `AliasedVirtualVariable` due to something like:
// ```
// char s[10];
// strcpy(s, p);
// const char* p = b ? "SomeLiteral" : s;
// return p[3];
// ```
// In the above example, `p[3]` may access either the string literal or the local variable
// `s`, so both of those locations must be members of the `AliasedVirtualVariable`.
exists(Alias::getOverlap(defLocation, useLocation))
)
)
or

Просмотреть файл

@ -0,0 +1,117 @@
/**
* Provides an implementation of Global Value Numbering.
* See https://en.wikipedia.org/wiki/Global_value_numbering
*
* The predicate `globalValueNumber` converts an expression into a `GVN`,
* which is an abstract type representing the value of the expression. If
* two expressions have the same `GVN` then they compute the same value.
* For example:
*
* ```
* void f(int x, int y) {
* g(x+y, x+y);
* }
* ```
*
* In this example, both arguments in the call to `g` compute the same value,
* so both arguments have the same `GVN`. In other words, we can find
* this call with the following query:
*
* ```
* from FunctionCall call, GVN v
* where v = globalValueNumber(call.getArgument(0))
* and v = globalValueNumber(call.getArgument(1))
* select call
* ```
*
* The analysis is conservative, so two expressions might have different
* `GVN`s even though the actually always compute the same value. The most
* common reason for this is that the analysis cannot prove that there
* are no side-effects that might cause the computed value to change.
*/
import cpp
private import semmle.code.cpp.ir.implementation.aliased_ssa.gvn.internal.ValueNumberingInternal
private import semmle.code.cpp.ir.IR
/**
* A Global Value Number. A GVN is an abstract representation of the value
* computed by an expression. The relationship between `Expr` and `GVN` is
* many-to-one: every `Expr` has exactly one `GVN`, but multiple
* expressions can have the same `GVN`. If two expressions have the same
* `GVN`, it means that they compute the same value at run time. The `GVN`
* is an opaque value, so you cannot deduce what the run-time value of an
* expression will be from its `GVN`. The only use for the `GVN` of an
* expression is to find other expressions that compute the same value.
* Use the predicate `globalValueNumber` to get the `GVN` for an `Expr`.
*
* Note: `GVN` has `toString` and `getLocation` methods, so that it can be
* displayed in a results list. These work by picking an arbitrary
* expression with this `GVN` and using its `toString` and `getLocation`
* methods.
*/
class GVN extends TValueNumber {
pragma[noinline]
GVN() {
exists(Instruction instr |
this = tvalueNumber(instr) and exists(instr.getUnconvertedResultExpression())
)
}
private Instruction getAnInstruction() { this = tvalueNumber(result) }
final string toString() { result = "GVN" }
final string getDebugString() { result = strictconcat(getAnExpr().toString(), ", ") }
final Location getLocation() {
if exists(Expr e | e = getAnExpr() and not e.getLocation() instanceof UnknownLocation)
then
result =
min(Location l |
l = getAnExpr().getLocation() and not l instanceof UnknownLocation
|
l
order by
l.getFile().getAbsolutePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(),
l.getEndColumn()
)
else result instanceof UnknownDefaultLocation
}
final string getKind() {
this instanceof TVariableAddressValueNumber and result = "VariableAddress"
or
this instanceof TInitializeParameterValueNumber and result = "InitializeParameter"
or
this instanceof TInitializeThisValueNumber and result = "InitializeThis"
or
this instanceof TStringConstantValueNumber and result = "StringConstant"
or
this instanceof TFieldAddressValueNumber and result = "FieldAddress"
or
this instanceof TBinaryValueNumber and result = "Binary"
or
this instanceof TPointerArithmeticValueNumber and result = "PointerArithmetic"
or
this instanceof TUnaryValueNumber and result = "Unary"
or
this instanceof TInheritanceConversionValueNumber and result = "InheritanceConversion"
or
this instanceof TLoadTotalOverlapValueNumber and result = "LoadTotalOverlap"
or
this instanceof TUniqueValueNumber and result = "Unique"
}
/** Gets an expression that has this GVN. */
Expr getAnExpr() { result = getAnUnconvertedExpr() }
/** Gets an expression that has this GVN. */
Expr getAnUnconvertedExpr() { result = getAnInstruction().getUnconvertedResultExpression() }
/** Gets an expression that has this GVN. */
Expr getAConvertedExpr() { result = getAnInstruction().getConvertedResultExpression() }
}
/** Gets the global value number of expression `e`. */
GVN globalValueNumber(Expr e) { e = result.getAnExpr() }

Просмотреть файл

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

Просмотреть файл

@ -187,6 +187,13 @@ private predicate boundFlowStepSsa(
guard.controls(op2.getUse().getBlock(), testIsTrue) and
reason = TCondReason(guard)
)
or
exists(IRGuardCondition guard, boolean testIsTrue, SafeCastInstruction cast |
valueNumberOfOperand(op2) = valueNumber(cast.getUnary()) and
guard = boundFlowCond(valueNumber(cast), op1, delta, upper, testIsTrue) and
guard.controls(op2.getUse().getBlock(), testIsTrue) and
reason = TCondReason(guard)
)
}
/**
@ -259,7 +266,7 @@ private predicate safeCast(IntegralType fromtyp, IntegralType totyp) {
private class SafeCastInstruction extends ConvertInstruction {
SafeCastInstruction() {
safeCast(getResultType(), getUnary().getResultType())
safeCast(getUnary().getResultType(), getResultType())
or
getResultType() instanceof PointerType and
getUnary().getResultType() instanceof PointerType

Просмотреть файл

@ -94,6 +94,22 @@ abstract class FunctionWithWrappers extends Function {
)
}
/**
* Whether 'func' is a (possibly nested) wrapper function that feeds a parameter at the given index
* through to an interesting parameter of 'this' function.
*
* The 'cause' gives the name of 'this' interesting function and its relevant parameter
* at the end of the call chain.
*
* If there is more than one possible 'cause', a unique one is picked (by lexicographic order).
*/
pragma[nomagic]
private string wrapperFunctionAnyDepthUnique(Function func, int paramIndex) {
result =
toCause(func, paramIndex) + ", which ends up calling " +
min(string targetCause | this.wrapperFunctionAnyDepth(func, paramIndex, targetCause))
}
/**
* Whether 'func' is a (possibly nested) wrapper function that feeds a parameter at the given index
* through to an interesting parameter of 'this' function.
@ -114,13 +130,7 @@ abstract class FunctionWithWrappers extends Function {
)
or
not this.wrapperFunctionLimitedDepth(func, paramIndex, _, _) and
cause =
min(string targetCause, string possibleCause |
this.wrapperFunctionAnyDepth(func, paramIndex, targetCause) and
possibleCause = toCause(func, paramIndex) + ", which ends up calling " + targetCause
|
possibleCause
)
cause = wrapperFunctionAnyDepthUnique(func, paramIndex)
}
/**

Просмотреть файл

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

Просмотреть файл

@ -0,0 +1 @@
This directory contains tests for [experimental](../../../../docs/experimental.md) CodeQL queries and libraries.

Просмотреть файл

@ -77,4 +77,13 @@ void test_dynamic_cast() {
reinterpret_cast<D2*>(b2)->f(getenv("VAR"));
dynamic_cast<D3*>(b2)->f(getenv("VAR")); // tainted [FALSE POSITIVE]
}
namespace std {
template< class T >
T&& move( T&& t ) noexcept;
}
void test_std_move() {
sink(std::move(getenv("VAR")));
}

Просмотреть файл

@ -0,0 +1,4 @@
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:2:17:2:25 | sinkParam | global1 |
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:12:10:12:16 | global1 | global1 |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:2:17:2:25 | sinkParam | global2 |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:19:10:19:16 | global2 | global2 |

Просмотреть файл

@ -0,0 +1,7 @@
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
from Expr source, Element tainted, string globalVar
where
taintedIncludingGlobalVars(source, tainted, globalVar) and
globalVar != ""
select source, tainted, globalVar

Просмотреть файл

@ -0,0 +1,24 @@
char * getenv(const char *);
void sink(char *sinkParam);
void throughLocal() {
char * local = getenv("VAR");
sink(local); // flow
}
char * global1 = 0;
void readWriteGlobal1() {
sink(global1); // flow
global1 = getenv("VAR");
}
static char * global2 = 0;
void readGlobal2() {
sink(global2); // flow
}
void writeGlobal2() {
global2 = getenv("VAR");
}

Просмотреть файл

@ -93,6 +93,22 @@
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:79:30:79:35 | call to getenv |
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | defaulttainttracking.cpp:79:30:79:42 | (const char *)... |
| defaulttainttracking.cpp:79:30:79:35 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:84:17:84:17 | t |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:16 | call to move |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (const char *)... |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:23 | call to getenv |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 |
| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:2:17:2:25 | sinkParam |
| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:5:12:5:16 | local |
| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:5:20:5:25 | call to getenv |
| globals.cpp:5:20:5:25 | call to getenv | globals.cpp:6:10:6:14 | local |
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:9:8:9:14 | global1 |
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:15:13:20 | call to getenv |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:16:15:16:21 | global2 |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:15:23:20 | call to getenv |
| test_diff.cpp:92:10:92:13 | argv | defaulttainttracking.cpp:9:11:9:20 | p#0 |
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:1:11:1:20 | p#0 |
| test_diff.cpp:92:10:92:13 | argv | test_diff.cpp:92:10:92:13 | argv |

Просмотреть файл

@ -9,6 +9,14 @@
| defaulttainttracking.cpp:22:20:22:25 | call to getenv | defaulttainttracking.cpp:24:8:24:10 | array to pointer conversion | IR only |
| defaulttainttracking.cpp:38:25:38:30 | call to getenv | defaulttainttracking.cpp:39:51:39:61 | env_pointer | AST only |
| defaulttainttracking.cpp:64:10:64:15 | call to getenv | defaulttainttracking.cpp:52:24:52:24 | p | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:9:11:9:20 | p#0 | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:16 | call to move | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (const char *)... | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:8:88:32 | (reference dereference) | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | defaulttainttracking.cpp:88:18:88:30 | (reference to) | IR only |
| defaulttainttracking.cpp:88:18:88:23 | call to getenv | test_diff.cpp:1:11:1:20 | p#0 | IR only |
| globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only |
| globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only |
| test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only |
| test_diff.cpp:108:10:108:13 | argv | test_diff.cpp:36:24:36:24 | p | AST only |
| test_diff.cpp:111:10:111:13 | argv | defaulttainttracking.cpp:9:11:9:20 | p#0 | AST only |

Просмотреть файл

@ -0,0 +1,16 @@
void test_crement() {
int x1 = 0;
++x1;
int x2 = 0;
x2++;
int x3 = 0;
x3 -= 1; // flow
int x4 = 0;
x4 |= 1; // flow
int x5 = 0;
x5 = x5 - 1; // flow (to RHS)
}

Просмотреть файл

@ -0,0 +1,5 @@
| crements.cpp:3:5:3:6 | x1 |
| crements.cpp:6:3:6:4 | x2 |
| crements.cpp:9:3:9:4 | x3 |
| crements.cpp:12:3:12:4 | x4 |
| crements.cpp:15:8:15:9 | x5 |

Просмотреть файл

@ -0,0 +1,14 @@
import cpp
import semmle.code.cpp.ir.dataflow.DataFlow
class Cfg extends DataFlow::Configuration {
Cfg() { this = "from0::Cfg" }
override predicate isSource(DataFlow::Node source) { source.asExpr().getValue() = "0" }
override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof VariableAccess }
}
from Cfg cfg, Expr sink
where cfg.hasFlowToExpr(sink)
select sink

Просмотреть файл

@ -48,7 +48,7 @@ struct XY {
void bg_stackstruct(XY s1, XY s2) {
s1.x = source();
if (guarded(s1.x)) {
sink(s1.x); // no flow
sink(s1.x); // no flow [FALSE POSITIVE in AST]
} else if (guarded(s1.y)) {
sink(s1.x); // flow
} else if (guarded(s2.y)) {

Просмотреть файл

@ -36,9 +36,27 @@ class TestAllocationConfig extends DataFlow::Configuration {
)
}
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
exists(GlobalOrNamespaceVariable var | var.getName().matches("flowTestGlobal%") |
writesVariable(n1.asInstruction(), var) and
var = n2.asVariable()
or
readsVariable(n2.asInstruction(), var) and
var = n1.asVariable()
)
}
override predicate isBarrier(DataFlow::Node barrier) {
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
}
override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
}
private predicate readsVariable(LoadInstruction load, Variable var) {
load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
}
private predicate writesVariable(StoreInstruction store, Variable var) {
store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() = var
}

Просмотреть файл

@ -0,0 +1,24 @@
int source();
void sink(int);
void throughLocal() {
int local = source();
sink(local); // flow
}
int flowTestGlobal1 = 0;
void readWriteGlobal1() {
sink(flowTestGlobal1); // flow
flowTestGlobal1 = source();
}
static int flowTestGlobal2 = 0;
void readGlobal2() {
sink(flowTestGlobal2); // flow
}
void writeGlobal2() {
flowTestGlobal2 = source();
}

Просмотреть файл

@ -3,6 +3,7 @@
| BarrierGuard.cpp:25:10:25:15 | source | BarrierGuard.cpp:21:17:21:22 | source |
| BarrierGuard.cpp:31:10:31:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
| BarrierGuard.cpp:33:10:33:15 | source | BarrierGuard.cpp:29:16:29:21 | source |
| BarrierGuard.cpp:51:13:51:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
| BarrierGuard.cpp:53:13:53:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
| BarrierGuard.cpp:55:13:55:13 | x | BarrierGuard.cpp:49:10:49:15 | call to source |
| BarrierGuard.cpp:62:14:62:14 | x | BarrierGuard.cpp:60:11:60:16 | call to source |
@ -21,6 +22,7 @@
| dispatch.cpp:36:16:36:25 | call to notSource2 | dispatch.cpp:10:37:10:42 | call to source |
| dispatch.cpp:43:15:43:24 | call to notSource1 | dispatch.cpp:9:37:9:42 | call to source |
| dispatch.cpp:44:15:44:24 | call to notSource2 | dispatch.cpp:10:37:10:42 | call to source |
| globals.cpp:6:10:6:14 | local | globals.cpp:5:17:5:22 | call to source |
| lambdas.cpp:14:3:14:6 | t | lambdas.cpp:8:10:8:15 | call to source |
| lambdas.cpp:18:8:18:8 | call to operator() | lambdas.cpp:8:10:8:15 | call to source |
| lambdas.cpp:21:3:21:6 | t | lambdas.cpp:8:10:8:15 | call to source |

Просмотреть файл

@ -1,3 +1,4 @@
| BarrierGuard.cpp:49:10:49:15 | BarrierGuard.cpp:51:13:51:13 | AST only |
| BarrierGuard.cpp:60:11:60:16 | BarrierGuard.cpp:62:14:62:14 | AST only |
| clang.cpp:12:9:12:20 | clang.cpp:22:8:22:20 | AST only |
| clang.cpp:28:27:28:32 | clang.cpp:30:27:30:34 | AST only |
@ -16,6 +17,8 @@
| dispatch.cpp:107:17:107:22 | dispatch.cpp:96:8:96:8 | IR only |
| dispatch.cpp:140:8:140:13 | dispatch.cpp:96:8:96:8 | IR only |
| dispatch.cpp:144:8:144:13 | dispatch.cpp:96:8:96:8 | IR only |
| globals.cpp:13:23:13:28 | globals.cpp:12:10:12:24 | IR only |
| globals.cpp:23:23:23:28 | globals.cpp:19:10:19:24 | IR only |
| lambdas.cpp:8:10:8:15 | lambdas.cpp:14:3:14:6 | AST only |
| lambdas.cpp:8:10:8:15 | lambdas.cpp:18:8:18:8 | AST only |
| lambdas.cpp:8:10:8:15 | lambdas.cpp:21:3:21:6 | AST only |

Просмотреть файл

@ -35,6 +35,9 @@
| dispatch.cpp:96:8:96:8 | x | dispatch.cpp:107:17:107:22 | call to source |
| dispatch.cpp:96:8:96:8 | x | dispatch.cpp:140:8:140:13 | call to source |
| dispatch.cpp:96:8:96:8 | x | dispatch.cpp:144:8:144:13 | call to source |
| globals.cpp:6:10:6:14 | local | globals.cpp:5:17:5:22 | call to source |
| globals.cpp:12:10:12:24 | flowTestGlobal1 | globals.cpp:13:23:13:28 | call to source |
| globals.cpp:19:10:19:24 | flowTestGlobal2 | globals.cpp:23:23:23:28 | call to source |
| lambdas.cpp:35:8:35:8 | a | lambdas.cpp:8:10:8:15 | call to source |
| test.cpp:7:8:7:9 | t1 | test.cpp:6:12:6:17 | call to source |
| test.cpp:9:8:9:9 | t1 | test.cpp:6:12:6:17 | call to source |

Просмотреть файл

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

Просмотреть файл

@ -0,0 +1,13 @@
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:15:50:24 | envStr_ptr | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:28:50:40 | & ... | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:50:29:50:40 | envStrGlobal | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:2:52:12 | * ... | AST only |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:3:52:12 | envStr_ptr | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:20:11:21 | s1 | AST only |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:67:7:67:13 | copying | AST only |
| 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 |

Просмотреть файл

@ -0,0 +1,16 @@
import semmle.code.cpp.security.TaintTrackingImpl as AST
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking as IR
import cpp
from Expr source, Element tainted, string side
where
AST::taintedIncludingGlobalVars(source, tainted, _) and
not IR::taintedIncludingGlobalVars(source, tainted, _) and
not tainted.getLocation().getFile().getExtension() = "h" and
side = "AST only"
or
IR::taintedIncludingGlobalVars(source, tainted, _) and
not AST::taintedIncludingGlobalVars(source, tainted, _) and
not tainted.getLocation().getFile().getExtension() = "h" and
side = "IR only"
select source, tainted, side

Просмотреть файл

@ -0,0 +1,54 @@
| test.cpp:23:23:23:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:14:23:19 | envStr | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:28 | call to getenv | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:23:23:23:40 | (const char *)... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:6:25:29 | ! ... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:12 | call to strcmp | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:7:25:29 | (bool)... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:25:14:25:19 | envStr | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:6:29:28 | ! ... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:12 | call to strcmp | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:7:29:28 | (bool)... | |
| test.cpp:23:23:23:28 | call to getenv | test.cpp:29:14:29:19 | envStr | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:14:38:19 | envStr | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:28 | call to getenv | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:38:23:38:40 | (const char *)... | |
| test.cpp:38:23:38:28 | call to getenv | test.cpp:40:14:40:19 | envStr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:8:24:8:25 | s1 | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:45:13:45:24 | envStrGlobal | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:14:49:19 | envStr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:28 | call to getenv | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:49:23:49:40 | (const char *)... | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:52:16:52:21 | envStr | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:6:54:35 | ! ... | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:12 | call to strcmp | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:7:54:35 | (bool)... | |
| test.cpp:49:23:49:28 | call to getenv | test.cpp:54:14:54:25 | envStrGlobal | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:10:27:10:27 | s | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:18:60:25 | userName | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:34 | call to getenv | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:60:29:60:47 | (const char *)... | |
| test.cpp:60:29:60:34 | call to getenv | test.cpp:64:25:64:32 | userName | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:11:36:11:37 | s2 | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:17:68:24 | userName | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:33 | call to getenv | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:68:28:68:46 | (const char *)... | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:5:70:10 | call to strcpy | |
| test.cpp:68:28:68:33 | call to getenv | test.cpp:70:18:70:25 | userName | |
| test.cpp:75:20:75:25 | call to getenv | test.cpp:15:22:15:25 | nptr | |
| 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 | |

Просмотреть файл

@ -0,0 +1,7 @@
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
from Expr source, Element tainted, string globalVar
where
taintedIncludingGlobalVars(source, tainted, globalVar) and
not tainted.getLocation().getFile().getExtension() = "h"
select source, tainted, globalVar

Просмотреть файл

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

Просмотреть файл

@ -34,7 +34,6 @@
| taint.cpp:352:7:352:7 | taint.cpp:330:6:330:11 | AST only |
| taint.cpp:372:7:372:7 | taint.cpp:365:24:365:29 | AST only |
| taint.cpp:374:7:374:7 | taint.cpp:365:24:365:29 | AST only |
| taint.cpp:382:7:382:7 | taint.cpp:377:23:377:28 | AST only |
| taint.cpp:391:7:391:7 | taint.cpp:385:27:385:32 | AST only |
| taint.cpp:423:7:423:7 | taint.cpp:422:14:422:19 | AST only |
| taint.cpp:424:9:424:17 | taint.cpp:422:14:422:19 | AST only |

Просмотреть файл

@ -17,5 +17,6 @@
| taint.cpp:291:7:291:7 | y | taint.cpp:275:6:275:11 | call to source |
| taint.cpp:337:7:337:7 | t | taint.cpp:330:6:330:11 | call to source |
| taint.cpp:350:7:350:7 | t | taint.cpp:330:6:330:11 | call to source |
| taint.cpp:382:7:382:7 | a | taint.cpp:377:23:377:28 | source |
| taint.cpp:429:7:429:7 | b | taint.cpp:428:13:428:18 | call to source |
| taint.cpp:430:9:430:14 | member | taint.cpp:428:13:428:18 | call to source |

Просмотреть файл

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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше