Merge remote-tracking branch 'upstream/master' into dataflow-indirect-args

The conflicts came from how `this` is now a parameter but not a
`Parameter` on `master`.

Conflicts:
	cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
	cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp
	cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected
	cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected
	cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
	cpp/ql/test/library-tests/dataflow/fields/ir-flow.expected
	cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected
This commit is contained in:
Jonas Jensen
2020-06-02 15:35:02 +02:00
803 changed files with 25373 additions and 13592 deletions

View File

@@ -0,0 +1,9 @@
{
"extensions": [
"github.vscode-codeql",
"slevesque.vscode-zipexplorer"
],
"settings": {
"codeQL.experimentalBqrsParsing": true
}
}

View File

@@ -1,6 +1,6 @@
# CodeQL
This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.com) makes available to its customers worldwide.
This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.com) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
## How do I learn CodeQL and run queries?

View File

@@ -0,0 +1,44 @@
# Improvements to C/C++ analysis
The following changes in version 1.25 affect C/C++ analysis in all applications.
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|----------------------------|------------------------|------------------------------------------------------------------|
## Changes to libraries
* The library `VCS.qll` and all queries that imported it have been removed.
* The data-flow library has been improved, which affects most security queries by potentially
adding more results. Flow through functions now takes nested field reads/writes into account.
For example, the library is able to track flow from `taint()` to `sink()` via the method
`getf2f1()` in
```c
struct C {
int f1;
};
struct C2
{
C f2;
int getf2f1() {
return f2.f1; // Nested field read
}
void m() {
f2.f1 = taint();
sink(getf2f1()); // NEW: taint() reaches here
}
};
```
* The security pack taint tracking library (`semmle.code.cpp.security.TaintTracking`) now considers that equality checks may block the flow of taint. This results in fewer false positive results from queries that use this library.
* The length of a tainted string (such as the return value of a call to `strlen` or `strftime` with tainted parameters) is no longer itself considered tainted by the `models` library. This leads to fewer false positive results in queries that use any of our taint libraries.

View File

@@ -18,11 +18,37 @@ The following changes in version 1.25 affect C# analysis in all applications.
## Changes to code extraction
* Index initializers, of the form `{ [1] = "one" }`, are extracted correctly. Previously, the kind of the
expression was incorrect, and the index was not extracted.
## Changes to libraries
* The class `UnboundGeneric` has been refined to only be those declarations that actually
have type parameters. This means that non-generic nested types inside construced types,
have type parameters. This means that non-generic nested types inside constructed types,
such as `A<int>.B`, no longer are considered unbound generics. (Such nested types do,
however, still have relevant `.getSourceDeclaration()`s, for example `A<>.B`.)
* The data-flow library has been improved, which affects most security queries by potentially
adding more results. Flow through methods now takes nested field reads/writes into account.
For example, the library is able to track flow from `"taint"` to `Sink()` via the method
`GetF2F1()` in
```csharp
class C1
{
string F1;
}
class C2
{
C1 F2;
string GetF2F1() => F2.F1; // Nested field read
void M()
{
F2 = new C1() { F1 = "taint" };
Sink(GetF2F1()); // NEW: "taint" reaches here
}
}
```
## Changes to autobuilder

View File

@@ -0,0 +1,41 @@
# Improvements to Java analysis
The following changes in version 1.25 affect Java analysis in all applications.
## General improvements
## New queries
| **Query** | **Tags** | **Purpose** |
|-----------------------------|-----------|--------------------------------------------------------------------|
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|------------------------------|------------------------|-----------------------------------|
## Changes to libraries
* The data-flow library has been improved, which affects most security queries by potentially
adding more results. Flow through methods now takes nested field reads/writes into account.
For example, the library is able to track flow from `"taint"` to `sink()` via the method
`getF2F1()` in
```java
class C1 {
String f1;
C1(String f1) { this.f1 = f1; }
}
class C2 {
C1 f2;
String getF2F1() {
return this.f2.f1; // Nested field read
}
void m() {
this.f2 = new C1("taint");
sink(this.getF2F1()); // NEW: "taint" reaches here
}
}
```

View File

@@ -3,9 +3,28 @@
## General improvements
* Support for the following frameworks and libraries has been improved:
- [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
- [bluebird](http://bluebirdjs.com/)
- [express](https://www.npmjs.com/package/express)
- [fastify](https://www.npmjs.com/package/fastify)
- [fstream](https://www.npmjs.com/package/fstream)
- [jGrowl](https://github.com/stanlemon/jGrowl)
- [jQuery](https://jquery.com/)
- [marsdb](https://www.npmjs.com/package/marsdb)
- [minimongo](https://www.npmjs.com/package/minimongo/)
- [mssql](https://www.npmjs.com/package/mssql)
- [mysql](https://www.npmjs.com/package/mysql)
- [pg](https://www.npmjs.com/package/pg)
- [sequelize](https://www.npmjs.com/package/sequelize)
- [spanner](https://www.npmjs.com/package/spanner)
- [sqlite](https://www.npmjs.com/package/sqlite)
- [ssh2-streams](https://www.npmjs.com/package/ssh2-streams)
- [ssh2](https://www.npmjs.com/package/ssh2)
* TypeScript 3.9 is now supported.
* The analysis of sanitizers has improved, leading to more accurate
results from the security queries.
## New queries
@@ -13,17 +32,55 @@
|---------------------------------------------------------------------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Cross-site scripting through DOM (`js/xss-through-dom`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities where existing text from the DOM is used as HTML. Results are not shown on LGTM by default. |
| Incomplete HTML attribute sanitization (`js/incomplete-html-attribute-sanitization`) | security, external/cwe/cwe-20, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities due to incomplete sanitization of HTML meta-characters. Results are shown on LGTM by default. |
| Unsafe expansion of self-closing HTML tag (`js/unsafe-html-expansion`) | security, external/cwe/cwe-079, external/cwe/cwe-116 | Highlights potential XSS vulnerabilities caused by unsafe expansion of self-closing HTML tags. |
| Unsafe shell command constructed from library input (`js/shell-command-constructed-from-input`) | correctness, security, external/cwe/cwe-078, external/cwe/cwe-088 | Highlights potential command injections due to a shell command being constructed from library inputs. Results are shown on LGTM by default. |
## Changes to existing queries
| **Query** | **Expected impact** | **Change** |
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
| Client-side cross-site scripting (`js/xss`) | Fewer results | This query now recognizes additional safe patterns of constructing HTML. |
| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | Fewer results | This query now recognizes additional safe patterns of doing URL redirects. |
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving NoSQL code operators are now recognized. |
| Expression has no effect (`js/useless-expression`) | Fewer results | This query no longer flags an expression when that expression is the only content of the containing file. |
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. |
| Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. |
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. |
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. |
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. |
| Expression has no effect (`js/useless-expression`) | Less results | This query no longer flags an expression when that expression is the only content of the containing file. |
| Unknown directive (`js/unknown-directive`) | Less results | This query no longer flags directives generated by the Babel compiler. |
| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. |
| Unknown directive (`js/unknown-directive`) | Fewer results | This query no longer flags directives generated by the Babel compiler. |
| Unused property (`js/unused-property`) | Fewer results | This query no longer flags properties of objects that are operands of `yield` expressions. |
| Zip Slip (`js/zipslip`) | More results | This query now recognizes additional vulnerabilities. |
The following low-precision queries are no longer run by default on LGTM (their results already were not displayed):
- `js/angular/dead-event-listener`
- `js/angular/unused-dependency`
- `js/bitwise-sign-check`
- `js/comparison-of-identical-expressions`
- `js/conflicting-html-attribute`
- `js/ignored-setter-parameter`
- `js/jsdoc/malformed-param-tag`
- `js/jsdoc/missing-parameter`
- `js/jsdoc/unknown-parameter`
- `js/json-in-javascript-file`
- `js/misspelled-identifier`
- `js/nested-loops-with-same-variable`
- `js/node/cyclic-import`
- `js/node/unused-npm-dependency`
- `js/omitted-array-element`
- `js/return-outside-function`
- `js/single-run-loop`
- `js/too-many-parameters`
- `js/unused-property`
- `js/useless-assignment-to-global`
## Changes to libraries
* A library `semmle.javascript.explore.CallGraph` has been added to help write queries for exploring the call graph.
* Added data flow for `Map` and `Set`, and added matching type-tracking steps that can accessed using the `CollectionsTypeTracking` module.
* The data-flow node representing a parameter or destructuring pattern is now always the `ValueNode` corresponding to that AST node. This has a few consequences:
- `Parameter.flow()` now gets the correct data flow node for a parameter. Previously this had a result, but the node was disconnected from the data flow graph.
- `ParameterNode.asExpr()` and `.getAstNode()` now gets the parameter's AST node, whereas previously it had no result.
- `Expr.flow()` now has a more meaningful result for destructuring patterns. Previously this node was disconnected from the data flow graph. Now it represents the values being destructured by the pattern.
* The global data-flow and taint-tracking libraries now model indirect parameter accesses through the `arguments` object in some cases, which may lead to additional results from some of the security queries, particularly "Prototype pollution in utility function".

View File

@@ -6,29 +6,50 @@
import cpp
// True if function was ()-declared, but not (void)-declared or K&R-defined
/**
* Holds if `fde` has a parameter declaration that's clear on the minimum
* number of parameters. This is essentially true for everything except
* `()`-declarations.
*/
private predicate hasDefiniteNumberOfParameters(FunctionDeclarationEntry fde) {
fde.hasVoidParamList()
or
fde.getNumberOfParameters() > 0
or
fde.isDefinition()
}
/* Holds if function was ()-declared, but not (void)-declared or K&R-defined. */
private predicate hasZeroParamDecl(Function f) {
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition()
not hasDefiniteNumberOfParameters(fde)
)
}
// True if this file (or header) was compiled as a C file
/* Holds if this file (or header) was compiled as a C file. */
private predicate isCompiledAsC(File f) {
f.compiledAsC()
or
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
}
/** Holds if `fc` is a call to `f` with too few arguments. */
predicate tooFewArguments(FunctionCall fc, Function f) {
f = fc.getTarget() and
not f.isVarargs() and
not f instanceof BuiltInFunction and
// This query should only have results on C (not C++) functions that have a
// `()` parameter list somewhere. If it has results on other functions, then
// it's probably because the extractor only saw a partial compilation.
hasZeroParamDecl(f) and
isCompiledAsC(f.getFile()) and
// There is an explicit declaration of the function whose parameter count is larger
// than the number of call arguments
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
// Produce an alert when all declarations that are authoritative on the
// parameter count specify a parameter count larger than the number of call
// arguments.
forex(FunctionDeclarationEntry fde |
fde = f.getADeclarationEntry() and
hasDefiniteNumberOfParameters(fde)
|
fde.getNumberOfParameters() > fc.getNumberOfArguments()
)
}

View File

@@ -1,27 +0,0 @@
/**
* @name Churned lines per file
* @description Number of churned lines per file, across the revision
* history in the database.
* @kind treemap
* @id cpp/historical-churn
* @treemap.warnOn highValues
* @metricType file
* @metricAggregate avg sum max
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
from File f, int n
where
n =
sum(Commit entry, int churn |
churn = entry.getRecentChurnForFile(f) and
not artificialChange(entry)
|
churn
) and
exists(f.getMetrics().getNumberOfLinesOfCode())
select f, n order by n desc

View File

@@ -1,27 +0,0 @@
/**
* @name Added lines per file
* @description Number of added lines per file, across the revision
* history in the database.
* @kind treemap
* @id cpp/historical-lines-added
* @treemap.warnOn highValues
* @metricType file
* @metricAggregate avg sum max
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
from File f, int n
where
n =
sum(Commit entry, int churn |
churn = entry.getRecentAdditionsForFile(f) and
not artificialChange(entry)
|
churn
) and
exists(f.getMetrics().getNumberOfLinesOfCode())
select f, n order by n desc

View File

@@ -1,27 +0,0 @@
/**
* @name Deleted lines per file
* @description Number of deleted lines per file, across the revision
* history in the database.
* @kind treemap
* @id cpp/historical-lines-deleted
* @treemap.warnOn highValues
* @metricType file
* @metricAggregate avg sum max
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
from File f, int n
where
n =
sum(Commit entry, int churn |
churn = entry.getRecentDeletionsForFile(f) and
not artificialChange(entry)
|
churn
) and
exists(f.getMetrics().getNumberOfLinesOfCode())
select f, n order by n desc

View File

@@ -1,18 +0,0 @@
/**
* @name Number of authors
* @description Number of distinct authors for each file.
* @kind treemap
* @id cpp/historical-number-of-authors
* @treemap.warnOn highValues
* @metricType file
* @metricAggregate avg min max
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
from File f
where exists(f.getMetrics().getNumberOfLinesOfCode())
select f, count(Author author | author.getAnEditedFile() = f)

View File

@@ -1,19 +0,0 @@
/**
* @name Number of file-level changes
* @description The number of file-level changes made (by version
* control history).
* @kind treemap
* @id cpp/historical-number-of-changes
* @treemap.warnOn highValues
* @metricType file
* @metricAggregate avg min max sum
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
from File f
where exists(f.getMetrics().getNumberOfLinesOfCode())
select f, count(Commit svn | f = svn.getAnAffectedFile() and not artificialChange(svn))

View File

@@ -1,21 +0,0 @@
/**
* @name Number of co-committed files
* @description The average number of other files that are touched
* whenever a file is affected by a commit.
* @kind treemap
* @id cpp/historical-number-of-co-commits
* @treemap.warnOn highValues
* @metricType file
* @metricAggregate avg min max
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
int committedFiles(Commit commit) { result = count(commit.getAnAffectedFile()) }
from File f
where exists(f.getMetrics().getNumberOfLinesOfCode())
select f, avg(Commit commit | commit.getAnAffectedFile() = f | committedFiles(commit) - 1)

View File

@@ -1,37 +0,0 @@
/**
* @name Number of re-commits for each file
* @description A re-commit is taken to mean a commit to a file that
* was touched less than five days ago.
* @kind treemap
* @id cpp/historical-number-of-re-commits
* @treemap.warnOn highValues
* @metricType file
* @metricAggregate avg min max
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
predicate inRange(Commit first, Commit second) {
first.getAnAffectedFile() = second.getAnAffectedFile() and
first != second and
exists(int n |
n = first.getDate().daysTo(second.getDate()) and
n >= 0 and
n < 5
)
}
int recommitsForFile(File f) {
result =
count(Commit recommit |
f = recommit.getAnAffectedFile() and
exists(Commit prev | inRange(prev, recommit))
)
}
from File f
where exists(f.getMetrics().getNumberOfLinesOfCode())
select f, recommitsForFile(f)

View File

@@ -1,27 +0,0 @@
/**
* @name Number of recent authors
* @description Number of distinct authors that have recently made
* changes.
* @kind treemap
* @id cpp/historical-number-of-recent-authors
* @treemap.warnOn highValues
* @metricType file
* @metricAggregate avg min max
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
from File f
where exists(f.getMetrics().getNumberOfLinesOfCode())
select f,
count(Author author |
exists(Commit e |
e = author.getACommit() and
f = e.getAnAffectedFile() and
e.daysToNow() <= 180 and
not artificialChange(e)
)
)

View File

@@ -1,24 +0,0 @@
/**
* @name Recently changed files
* @description Number of files recently edited.
* @kind treemap
* @id cpp/historical-number-of-recent-changed-files
* @treemap.warnOn highValues
* @metricType file
* @metricAggregate avg min max sum
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
from File f
where
exists(Commit e |
e.getAnAffectedFile() = f and
e.daysToNow() <= 180 and
not artificialChange(e)
) and
exists(f.getMetrics().getNumberOfLinesOfCode())
select f, 1

View File

@@ -1,25 +0,0 @@
/**
* @name Recent changes
* @description Number of recent commits to this file.
* @kind treemap
* @id cpp/historical-number-of-recent-changes
* @treemap.warnOn highValues
* @metricType file
* @metricAggregate avg min max sum
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
from File f, int n
where
n =
count(Commit e |
e.getAnAffectedFile() = f and
e.daysToNow() <= 180 and
not artificialChange(e)
) and
exists(f.getMetrics().getNumberOfLinesOfCode())
select f, n order by n desc

View File

@@ -1 +1,7 @@
/**
* DEPRECATED: use `import cpp` instead of `import default`.
*
* Provides classes and predicates for working with C/C++ code.
*/
import cpp

View File

@@ -0,0 +1,105 @@
/**
* This library proves that a subset of pointer dereferences in a program are
* safe, i.e. in-bounds.
* It does so by first defining what a pointer dereference is (on the IR
* `Instruction` level), and then using the array length analysis and the range
* analysis together to prove that some of these pointer dereferences are safe.
*
* The analysis is soundy, i.e. it is sound if no undefined behaviour is present
* in the program.
* Furthermore, it crucially depends on the soundiness of the range analysis and
* the array length analysis.
*/
import cpp
private import experimental.semmle.code.cpp.rangeanalysis.ArrayLengthAnalysis
private import semmle.code.cpp.rangeanalysis.RangeAnalysis
/**
* Gets the instruction that computes the address of memory that `i` accesses.
* Only holds if `i` dereferences a pointer, not when the computation of the
* memory address is constant, or if the address of a local variable is loaded/stored to.
*/
private Instruction getMemoryAddressInstruction(Instruction i) {
(
result = i.(FieldAddressInstruction).getObjectAddress() or
result = i.(LoadInstruction).getSourceAddress() or
result = i.(StoreInstruction).getDestinationAddress()
) and
not result instanceof FieldAddressInstruction and
not result instanceof VariableAddressInstruction and
not result instanceof ConstantValueInstruction
}
/**
* All instructions that dereference a pointer.
*/
class PointerDereferenceInstruction extends Instruction {
PointerDereferenceInstruction() { exists(getMemoryAddressInstruction(this)) }
Instruction getAddress() { result = getMemoryAddressInstruction(this) }
}
/**
* Holds if `ptrDeref` can be proven to always access allocated memory.
*/
predicate inBounds(PointerDereferenceInstruction ptrDeref) {
exists(Length length, int lengthDelta, Offset offset, int offsetDelta |
knownArrayLength(ptrDeref.getAddress(), length, lengthDelta, offset, offsetDelta) and
// lower bound - note that we treat a pointer that accesses an array of
// length 0 as on upper-bound violation, but not as a lower-bound violation
(
offset instanceof ZeroOffset and
offsetDelta >= 0
or
offset instanceof OpOffset and
exists(int lowerBoundDelta |
boundedOperand(offset.(OpOffset).getOperand(), any(ZeroBound b), lowerBoundDelta,
/*upper*/ false, _) and
lowerBoundDelta + offsetDelta >= 0
)
) and
// upper bound
(
// both offset and length are only integers
length instanceof ZeroLength and
offset instanceof ZeroOffset and
offsetDelta < lengthDelta
or
exists(int lengthBound |
// array length is variable+integer, and there's a fixed (integer-only)
// lower bound on the variable, so we can guarantee this access is always in-bounds
length instanceof VNLength and
offset instanceof ZeroOffset and
boundedInstruction(length.(VNLength).getInstruction(), any(ZeroBound b), lengthBound,
/* upper*/ false, _) and
offsetDelta < lengthBound + lengthDelta
)
or
exists(int offsetBoundDelta |
length instanceof ZeroLength and
offset instanceof OpOffset and
boundedOperand(offset.(OpOffset).getOperand(), any(ZeroBound b), offsetBoundDelta,
/* upper */ true, _) and
// offset <= offsetBoundDelta, so offset + offsetDelta <= offsetDelta + offsetBoundDelta
// Thus, in-bounds if offsetDelta + offsetBoundDelta < lengthDelta
// as we have length instanceof ZeroLength
offsetDelta + offsetBoundDelta < lengthDelta
)
or
exists(ValueNumberBound b, int offsetBoundDelta |
length instanceof VNLength and
offset instanceof OpOffset and
b.getValueNumber() = length.(VNLength).getValueNumber() and
// It holds that offset <= length + offsetBoundDelta
boundedOperand(offset.(OpOffset).getOperand(), b, offsetBoundDelta, /*upper*/ true, _) and
// it also holds that
offsetDelta < lengthDelta - offsetBoundDelta
// taking both inequalities together we get
// offset <= length + offsetBoundDelta
// => offset + offsetDelta <= length + offsetBoundDelta + offsetDelta < length + offsetBoundDelta + lengthDelta - offsetBoundDelta
// as required
)
)
)
}

View File

@@ -1,3 +1,5 @@
/** Provides classes for detecting duplicate or similar code. */
import cpp
private string relativePath(File file) { result = file.getRelativePath().replaceAll("\\", "/") }
@@ -8,9 +10,12 @@ private predicate tokenLocation(string path, int sl, int sc, int ec, int el, Cop
tokens(copy, index, sl, sc, ec, el)
}
/** A token block used for detection of duplicate and similar code. */
class Copy extends @duplication_or_similarity {
/** Gets the index of the last token in this block. */
private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) }
/** Gets the index of the token in this block starting at the location `loc`, if any. */
int tokenStartingAt(Location loc) {
exists(string filepath, int startline, int startcol |
loc.hasLocationInfo(filepath, startline, startcol, _, _) and
@@ -18,6 +23,7 @@ class Copy extends @duplication_or_similarity {
)
}
/** Gets the index of the token in this block ending at the location `loc`, if any. */
int tokenEndingAt(Location loc) {
exists(string filepath, int endline, int endcol |
loc.hasLocationInfo(filepath, _, _, endline, endcol) and
@@ -25,24 +31,38 @@ class Copy extends @duplication_or_similarity {
)
}
/** Gets the line on which the first token in this block starts. */
int sourceStartLine() { tokens(this, 0, result, _, _, _) }
/** Gets the column on which the first token in this block starts. */
int sourceStartColumn() { tokens(this, 0, _, result, _, _) }
/** Gets the line on which the last token in this block ends. */
int sourceEndLine() { tokens(this, lastToken(), _, _, result, _) }
/** Gets the column on which the last token in this block ends. */
int sourceEndColumn() { tokens(this, lastToken(), _, _, _, result) }
/** Gets the number of lines containing at least (part of) one token in this block. */
int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() }
/** Gets an opaque identifier for the equivalence class of this block. */
int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) }
/** Gets the source file in which this block appears. */
File sourceFile() {
exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) |
name.replaceAll("\\", "/") = relativePath(result)
)
}
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
@@ -53,25 +73,30 @@ class Copy extends @duplication_or_similarity {
endcolumn = sourceEndColumn()
}
/** Gets a textual representation of this element. */
string toString() { none() }
}
/** A block of duplicated code. */
class DuplicateBlock extends Copy, @duplication {
override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." }
}
/** A block of similar code. */
class SimilarBlock extends Copy, @similarity {
override string toString() {
result = "Similar code: " + sourceLines() + " almost duplicated lines."
}
}
/** Gets a function with a body and a location. */
FunctionDeclarationEntry sourceMethod() {
result.isDefinition() and
exists(result.getLocation()) and
numlines(unresolveElement(result.getFunction()), _, _, _)
}
/** Gets the number of member functions in `c` with a body and a location. */
int numberOfSourceMethods(Class c) {
result =
count(FunctionDeclarationEntry m |
@@ -108,6 +133,10 @@ private predicate duplicateStatement(
)
}
/**
* Holds if `m1` is a function with `total` lines, and `m2` is a function
* that has `duplicate` lines in common with `m1`.
*/
predicate duplicateStatements(
FunctionDeclarationEntry m1, FunctionDeclarationEntry m2, int duplicate, int total
) {
@@ -115,13 +144,16 @@ predicate duplicateStatements(
total = strictcount(statementInMethod(m1))
}
/**
* Find pairs of methods are identical
*/
/** Holds if `m` and other are identical functions. */
predicate duplicateMethod(FunctionDeclarationEntry m, FunctionDeclarationEntry other) {
exists(int total | duplicateStatements(m, other, total, total))
}
/**
* INTERNAL: do not use.
*
* Holds if `line` in `f` is similar to a line somewhere else.
*/
predicate similarLines(File f, int line) {
exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()])
}
@@ -152,6 +184,7 @@ private predicate similarLinesCoveredFiles(File f, File otherFile) {
)
}
/** Holds if `coveredLines` lines of `f` are similar to lines in `otherFile`. */
predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
exists(int numLines | numLines = f.getMetrics().getNumberOfLines() |
similarLinesCoveredFiles(f, otherFile) and
@@ -166,6 +199,11 @@ predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
)
}
/**
* INTERNAL: do not use.
*
* Holds if `line` in `f` is duplicated by a line somewhere else.
*/
predicate duplicateLines(File f, int line) {
exists(DuplicateBlock b |
b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]
@@ -182,6 +220,7 @@ private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, F
)
}
/** Holds if `coveredLines` lines of `f` are duplicates of lines in `otherFile`. */
predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
exists(int numLines | numLines = f.getMetrics().getNumberOfLines() |
exists(int coveredApprox |
@@ -206,6 +245,7 @@ predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
)
}
/** Holds if most of `f` (`percent`%) is similar to `other`. */
predicate similarFiles(File f, File other, int percent) {
exists(int covered, int total |
similarLinesCovered(f, covered, other) and
@@ -216,6 +256,7 @@ predicate similarFiles(File f, File other, int percent) {
not duplicateFiles(f, other, _)
}
/** Holds if most of `f` (`percent`%) is duplicated by `other`. */
predicate duplicateFiles(File f, File other, int percent) {
exists(int covered, int total |
duplicateLinesCovered(f, covered, other) and
@@ -225,6 +266,10 @@ predicate duplicateFiles(File f, File other, int percent) {
)
}
/**
* Holds if most member functions of `c` (`numDup` out of `total`) are
* duplicates of member functions in `other`.
*/
predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total) {
numDup =
strictcount(FunctionDeclarationEntry m1 |
@@ -240,6 +285,11 @@ predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total)
(numDup * 100) / total > 80
}
/**
* Holds if most member functions of `c` are duplicates of member functions in
* `other`. Provides the human-readable `message` to describe the amount of
* duplication.
*/
predicate mostlyDuplicateClass(Class c, Class other, string message) {
exists(int numDup, int total |
mostlyDuplicateClassBase(c, other, numDup, total) and
@@ -264,12 +314,21 @@ predicate mostlyDuplicateClass(Class c, Class other, string message) {
)
}
/** Holds if `f` and `other` are similar or duplicates. */
predicate fileLevelDuplication(File f, File other) {
similarFiles(f, other, _) or duplicateFiles(f, other, _)
}
/**
* Holds if most member functions of `c` are duplicates of member functions in
* `other`.
*/
predicate classLevelDuplication(Class c, Class other) { mostlyDuplicateClass(c, other, _) }
/**
* Holds if `line` in `f` should be allowed to be duplicated. This is the case
* for `#include` directives.
*/
predicate whitelistedLineForDuplication(File f, int line) {
exists(Include i | i.getFile() = f and i.getLocation().getStartLine() = line)
}

View File

@@ -1,31 +1,52 @@
/** Provides a class for working with defect query results stored in dashboard databases. */
import cpp
/**
* Holds if `id` in the opaque identifier of a result reported by query `queryPath`,
* such that `message` is the associated message and the location of the result spans
* column `startcolumn` of line `startline` to column `endcolumn` of line `endline`
* in file `filepath`.
*
* For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
external predicate defectResults(
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
string message
);
/**
* A defect query result stored in a dashboard database.
*/
class DefectResult extends int {
DefectResult() { defectResults(this, _, _, _, _, _, _, _) }
/** Gets the path of the query that reported the result. */
string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) }
/** Gets the file in which this query result was reported. */
File getFile() {
exists(string path |
defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
)
}
/** Gets the line on which the location of this query result starts. */
int getStartLine() { defectResults(this, _, _, result, _, _, _, _) }
/** Gets the column on which the location of this query result starts. */
int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) }
/** Gets the line on which the location of this query result ends. */
int getEndLine() { defectResults(this, _, _, _, _, result, _, _) }
/** Gets the column on which the location of this query result ends. */
int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) }
/** Gets the message associated with this query result. */
string getMessage() { defectResults(this, _, _, _, _, _, _, result) }
/** Gets the URL corresponding to the location of this query result. */
string getURL() {
result =
"file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" +

View File

@@ -1,26 +1,45 @@
/**
* Provides classes for working with external data.
*/
import cpp
/**
* An external data item.
*/
class ExternalData extends @externalDataElement {
/** Gets the path of the file this data was loaded from. */
string getDataPath() { externalData(this, result, _, _) }
/**
* Gets the path of the file this data was loaded from, with its
* extension replaced by `.ql`.
*/
string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
/** Gets the number of fields in this data item. */
int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) }
string getField(int index) { externalData(this, _, index, result) }
/** Gets the value of the `i`th field of this data item. */
string getField(int i) { externalData(this, _, i, result) }
int getFieldAsInt(int index) { result = getField(index).toInt() }
/** Gets the integer value of the `i`th field of this data item. */
int getFieldAsInt(int i) { result = getField(i).toInt() }
float getFieldAsFloat(int index) { result = getField(index).toFloat() }
/** Gets the floating-point value of the `i`th field of this data item. */
float getFieldAsFloat(int i) { result = getField(i).toFloat() }
date getFieldAsDate(int index) { result = getField(index).toDate() }
/** Gets the value of the `i`th field of this data item, interpreted as a date. */
date getFieldAsDate(int i) { result = getField(i).toDate() }
/** Gets a textual representation of this data item. */
string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
private string buildTupleString(int start) {
start = getNumFields() - 1 and result = getField(start)
/** Gets a textual representation of this data item, starting with the `n`th field. */
private string buildTupleString(int n) {
n = getNumFields() - 1 and result = getField(n)
or
start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1)
n < getNumFields() - 1 and result = getField(n) + "," + buildTupleString(n + 1)
}
}
@@ -33,7 +52,9 @@ class DefectExternalData extends ExternalData {
this.getNumFields() = 2
}
/** Gets the URL associated with this data item. */
string getURL() { result = getField(0) }
/** Gets the message associated with this data item. */
string getMessage() { result = getField(1) }
}

View File

@@ -1,31 +1,58 @@
/** Provides a class for working with metric query results stored in dashboard databases. */
import cpp
/**
* Holds if `id` in the opaque identifier of a result reported by query `queryPath`,
* such that `value` is the reported metric value and the location of the result spans
* column `startcolumn` of line `startline` to column `endcolumn` of line `endline`
* in file `filepath`.
*
* For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
*/
external predicate metricResults(
int id, string queryPath, string file, int startline, int startcol, int endline, int endcol,
float value
);
/**
* A metric query result stored in a dashboard database.
*/
class MetricResult extends int {
MetricResult() { metricResults(this, _, _, _, _, _, _, _) }
/** Gets the path of the query that reported the result. */
string getQueryPath() { metricResults(this, result, _, _, _, _, _, _) }
/** Gets the file in which this query result was reported. */
File getFile() {
exists(string path |
metricResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
)
}
/** Gets the line on which the location of this query result starts. */
int getStartLine() { metricResults(this, _, _, result, _, _, _, _) }
/** Gets the column on which the location of this query result starts. */
int getStartColumn() { metricResults(this, _, _, _, result, _, _, _) }
/** Gets the line on which the location of this query result ends. */
int getEndLine() { metricResults(this, _, _, _, _, result, _, _) }
/** Gets the column on which the location of this query result ends. */
int getEndColumn() { metricResults(this, _, _, _, _, _, result, _) }
/**
* Holds if there is a `Location` entity whose location is the same as
* the location of this query result.
*/
predicate hasMatchingLocation() { exists(this.getMatchingLocation()) }
/**
* Gets the `Location` entity whose location is the same as the location
* of this query result.
*/
Location getMatchingLocation() {
result.getFile() = this.getFile() and
result.getStartLine() = this.getStartLine() and
@@ -34,8 +61,10 @@ class MetricResult extends int {
result.getEndColumn() = this.getEndColumn()
}
/** Gets the value associated with this query result. */
float getValue() { metricResults(this, _, _, _, _, _, _, result) }
/** Gets the URL corresponding to the location of this query result. */
string getURL() {
result =
"file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" +

View File

@@ -1,92 +0,0 @@
import cpp
class Commit extends @svnentry {
Commit() {
svnaffectedfiles(this, _, _) and
exists(date svnDate, date snapshotDate |
svnentries(this, _, _, svnDate, _) and
snapshotDate(snapshotDate) and
svnDate <= snapshotDate
)
}
string toString() { result = this.getRevisionName() }
string getRevisionName() { svnentries(this, result, _, _, _) }
string getAuthor() { svnentries(this, _, result, _, _) }
date getDate() { svnentries(this, _, _, result, _) }
int getChangeSize() { svnentries(this, _, _, _, result) }
string getMessage() { svnentrymsg(this, result) }
string getAnAffectedFilePath(string action) {
exists(File rawFile | svnaffectedfiles(this, unresolveElement(rawFile), action) |
result = rawFile.getAbsolutePath()
)
}
string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) }
File getAnAffectedFile(string action) {
// Workaround for incorrect keys in SVN data
exists(File svnFile | svnFile.getAbsolutePath() = result.getAbsolutePath() |
svnaffectedfiles(this, unresolveElement(svnFile), action)
) and
exists(result.getMetrics().getNumberOfLinesOfCode())
}
File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) }
private predicate churnForFile(File f, int added, int deleted) {
// Workaround for incorrect keys in SVN data
exists(File svnFile | svnFile.getAbsolutePath() = f.getAbsolutePath() |
svnchurn(this, unresolveElement(svnFile), added, deleted)
) and
exists(f.getMetrics().getNumberOfLinesOfCode())
}
int getRecentChurnForFile(File f) {
exists(int added, int deleted | churnForFile(f, added, deleted) and result = added + deleted)
}
int getRecentAdditionsForFile(File f) { churnForFile(f, result, _) }
int getRecentDeletionsForFile(File f) { churnForFile(f, _, result) }
predicate isRecent() { recentCommit(this) }
int daysToNow() {
exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0)
}
}
class Author extends string {
Author() { exists(Commit e | this = e.getAuthor()) }
Commit getACommit() { result.getAuthor() = this }
File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() }
}
predicate recentCommit(Commit e) {
exists(date snapshotDate, date commitDate, int days |
snapshotDate(snapshotDate) and
e.getDate() = commitDate and
days = commitDate.daysTo(snapshotDate) and
days >= 0 and
days <= 60
)
}
date firstChange(File f) {
result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin)
}
predicate firstCommit(Commit e) {
not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate())
}
predicate artificialChange(Commit e) { firstCommit(e) or e.getChangeSize() >= 50000 }

View File

@@ -1,20 +0,0 @@
/**
* @name Defect from SVN
* @description A test case for creating a defect from SVN data.
* @kind problem
* @problem.severity warning
* @tags external-data
* @deprecated
*/
import cpp
import external.ExternalArtifact
import external.VCS
predicate numCommits(File f, int i) { i = count(Commit e | e.getAnAffectedFile() = f) }
predicate maxCommits(int i) { i = max(File f, int j | numCommits(f, j) | j) }
from File f, int i
where numCommits(f, i) and maxCommits(i)
select f, "This file has " + i + " commits."

View File

@@ -1,17 +0,0 @@
/**
* @name Metric from SVN
* @description Find number of commits for a file
* @treemap.warnOn lowValues
* @metricType file
* @tags external-data
* @deprecated
*/
import cpp
import external.VCS
predicate numCommits(File f, int i) { i = count(Commit e | e.getAnAffectedFile() = f) }
from File f, int i
where numCommits(f, i)
select f, i

View File

@@ -1,25 +0,0 @@
/**
* @name Filter: exclude results from files that have not recently been
* edited
* @description Use this filter to return results only if they are
* located in files that have been modified in the 60 days
* before the date of the snapshot.
* @kind problem
* @id cpp/recent-defects-filter
* @tags filter
* external-data
* @deprecated
*/
import cpp
import external.DefectFilter
import external.VCS
pragma[noopt]
private predicate recent(File file) {
exists(Commit e | file = e.getAnAffectedFile() | e.isRecent() and not artificialChange(e))
}
from DefectResult res
where recent(res.getFile())
select res, res.getMessage()

View File

@@ -1,25 +0,0 @@
/**
* @name Metric filter: exclude results from files that have not
* recently been edited
* @description Use this filter to return results only if they are
* located in files that have been modified in the 60 days
* before the snapshot.
* @kind treemap
* @id cpp/recent-defects-for-metric-filter
* @tags filter
* external-data
* @deprecated
*/
import cpp
import external.MetricFilter
import external.VCS
pragma[noopt]
private predicate recent(File file) {
exists(Commit e | file = e.getAnAffectedFile() | e.isRecent() and not artificialChange(e))
}
from MetricResult res
where recent(res.getFile())
select res, res.getValue()

View File

@@ -1 +1,7 @@
/**
* DEPRECATED: Objective C is no longer supported.
*
* Import `cpp` instead of `objc`.
*/
import cpp

View File

@@ -1,3 +1,8 @@
/**
* Provides a class and predicate for recognizing files that are likely to have been generated
* automatically.
*/
import semmle.code.cpp.Comments
import semmle.code.cpp.File
import semmle.code.cpp.Preprocessor

View File

@@ -1,3 +1,7 @@
/**
* Provides classes representing C++ classes, including structs, unions, and template classes.
*/
import semmle.code.cpp.Type
import semmle.code.cpp.UserType
import semmle.code.cpp.metrics.MetricClass

View File

@@ -1,3 +1,7 @@
/**
* Provides classes representing C and C++ comments.
*/
import semmle.code.cpp.Location
import semmle.code.cpp.Element

View File

@@ -1,3 +1,7 @@
/**
* Provides a class representing individual compiler invocations that occurred during the build.
*/
import semmle.code.cpp.File
/*

View File

@@ -1,3 +1,7 @@
/**
* Provides classes for working with C and C++ declarations.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.Specifier
import semmle.code.cpp.Namespace
@@ -98,7 +102,12 @@ class Declaration extends Locatable, @declaration {
this.hasQualifiedName(namespaceQualifier, "", baseName)
}
override string toString() { result = this.getName() }
/**
* Gets a description of this `Declaration` for display purposes.
*/
string getDescription() { result = this.getName() }
final override string toString() { result = this.getDescription() }
/**
* Gets the name of this declaration.

View File

@@ -1,3 +1,7 @@
/**
* Provides classes representing warnings generated during compilation.
*/
import semmle.code.cpp.Location
/** A compiler-generated error, warning or remark. */

View File

@@ -1,3 +1,8 @@
/**
* Provides the `Element` class, which is the base class for all classes representing C or C++
* program elements.
*/
import semmle.code.cpp.Location
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
@@ -261,8 +266,14 @@ private predicate isFromUninstantiatedTemplateRec(Element e, Element template) {
class StaticAssert extends Locatable, @static_assert {
override string toString() { result = "static_assert(..., \"" + getMessage() + "\")" }
/**
* Gets the expression which this static assertion ensures is true.
*/
Expr getCondition() { static_asserts(underlyingElement(this), unresolveElement(result), _, _) }
/**
* Gets the message which will be reported by the compiler if this static assertion fails.
*/
string getMessage() { static_asserts(underlyingElement(this), _, result, _) }
override Location getLocation() { static_asserts(underlyingElement(this), _, _, result) }

View File

@@ -1,3 +1,7 @@
/**
* Provides predicates for finding the smallest element that encloses an expression or statement.
*/
import cpp
/**

View File

@@ -1,3 +1,7 @@
/**
* Provides classes representing C/C++ enums and enum constants.
*/
import semmle.code.cpp.Type
private import semmle.code.cpp.internal.ResolveClass
@@ -19,6 +23,17 @@ class Enum extends UserType, IntegralOrEnumType {
/** Gets an enumerator of this enumeration. */
EnumConstant getAnEnumConstant() { result.getDeclaringEnum() = this }
/**
* Gets the enumerator of this enumeration that was declared at the zero-based position `index`.
* For example, `zero` is at index 2 in the following declaration:
* ```
* enum ReversedOrder {
* two = 2,
* one = 1,
* zero = 0
* };
* ```
*/
EnumConstant getEnumConstant(int index) {
enumconstants(unresolveElement(result), underlyingElement(this), index, _, _, _)
}

View File

@@ -1,3 +1,7 @@
/**
* Provides classes representing C structure members and C++ non-static member variables.
*/
import semmle.code.cpp.Variable
import semmle.code.cpp.Enum
import semmle.code.cpp.exprs.Access

View File

@@ -1,3 +1,7 @@
/**
* Provides classes representing files and folders.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.Declaration
import semmle.code.cpp.metrics.MetricFile
@@ -261,18 +265,6 @@ class File extends Container, @file {
/** Holds if this file was compiled as C++ (at any point). */
predicate compiledAsCpp() { fileannotations(underlyingElement(this), 1, "compiled as c++", "1") }
/**
* DEPRECATED: Objective-C is no longer supported.
* Holds if this file was compiled as Objective C (at any point).
*/
deprecated predicate compiledAsObjC() { none() }
/**
* DEPRECATED: Objective-C is no longer supported.
* Holds if this file was compiled as Objective C++ (at any point).
*/
deprecated predicate compiledAsObjCpp() { none() }
/**
* Holds if this file was compiled by a Microsoft compiler (at any point).
*
@@ -316,14 +308,6 @@ class File extends Container, @file {
exists(Include i | i.getFile() = this and i.getIncludedFile() = result)
}
/**
* DEPRECATED: use `getParentContainer` instead.
* Gets the folder which contains this file.
*/
deprecated Folder getParent() {
containerparent(unresolveElement(result), underlyingElement(this))
}
/**
* Holds if this file may be from source. This predicate holds for all files
* except the dummy file, whose name is the empty string, which contains
@@ -341,28 +325,6 @@ class File extends Container, @file {
/** Gets the metric file. */
MetricFile getMetrics() { result = this }
/**
* DEPRECATED: Use `getAbsolutePath` instead.
* Gets the full name of this file, for example:
* "/usr/home/me/myprogram.c".
*/
deprecated string getName() { files(underlyingElement(this), result, _, _, _) }
/**
* DEPRECATED: Use `getAbsolutePath` instead.
* Holds if this file has the specified full name.
*
* Example usage: `f.hasName("/usr/home/me/myprogram.c")`.
*/
deprecated predicate hasName(string name) { name = this.getName() }
/**
* DEPRECATED: Use `getAbsolutePath` instead.
* Gets the full name of this file, for example
* "/usr/home/me/myprogram.c".
*/
deprecated string getFullName() { result = this.getName() }
/**
* Gets the remainder of the base name after the first dot character. Note
* that the name of this predicate is in plural form, unlike `getExtension`,
@@ -377,22 +339,6 @@ class File extends Container, @file {
*/
string getExtensions() { files(underlyingElement(this), _, _, result, _) }
/**
* DEPRECATED: Use `getBaseName` instead.
* Gets the name and extension(s), but not path, of a file. For example,
* if the full name is "/path/to/filename.a.bcd" then the filename is
* "filename.a.bcd".
*/
deprecated string getFileName() {
// [a/b.c/d/]fileName
// ^ beginAfter
exists(string fullName, int beginAfter |
fullName = this.getName() and
beginAfter = max(int i | i = -1 or fullName.charAt(i) = "/" | i) and
result = fullName.suffix(beginAfter + 1)
)
}
/**
* Gets the short name of this file, that is, the prefix of its base name up
* to (but not including) the first dot character if there is one, or the

View File

@@ -1,3 +1,7 @@
/**
* Provides a class representing C++ `friend` declarations.
*/
import semmle.code.cpp.Declaration
private import semmle.code.cpp.internal.ResolveClass

View File

@@ -1,3 +1,8 @@
/**
* Provides classes for working with functions, including C++ constructors, destructors,
* user-defined operators, and template functions.
*/
import semmle.code.cpp.Location
import semmle.code.cpp.Member
import semmle.code.cpp.Class
@@ -184,17 +189,8 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
* For example: for a function `int Foo(int p1, int p2)` this would
* return `int p1, int p2`.
*/
string getParameterString() { result = getParameterStringFrom(0) }
private string getParameterStringFrom(int index) {
index = getNumberOfParameters() and
result = ""
or
index = getNumberOfParameters() - 1 and
result = getParameter(index).getTypedName()
or
index < getNumberOfParameters() - 1 and
result = getParameter(index).getTypedName() + ", " + getParameterStringFrom(index + 1)
string getParameterString() {
result = concat(int i | | min(getParameter(i).getTypedName()), ", " order by i)
}
/** Gets a call to this function. */
@@ -616,18 +612,8 @@ class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
* For example: for a function 'int Foo(int p1, int p2)' this would
* return 'int p1, int p2'.
*/
string getParameterString() { result = getParameterStringFrom(0) }
private string getParameterStringFrom(int index) {
index = getNumberOfParameters() and
result = ""
or
index = getNumberOfParameters() - 1 and
result = getParameterDeclarationEntry(index).getTypedName()
or
index < getNumberOfParameters() - 1 and
result =
getParameterDeclarationEntry(index).getTypedName() + ", " + getParameterStringFrom(index + 1)
string getParameterString() {
result = concat(int i | | min(getParameterDeclarationEntry(i).getTypedName()), ", " order by i)
}
/**
@@ -910,8 +896,10 @@ class Constructor extends MemberFunction {
* A function that defines an implicit conversion.
*/
abstract class ImplicitConversionFunction extends MemberFunction {
/** Gets the type this `ImplicitConversionFunction` takes as input. */
abstract Type getSourceType();
/** Gets the type this `ImplicitConversionFunction` converts to. */
abstract Type getDestType();
}

View File

@@ -1,3 +1,8 @@
/**
* Provides classes representing C/C++ `#include`, `#include_next`, and `#import` preprocessor
* directives.
*/
import semmle.code.cpp.Preprocessor
/**

View File

@@ -79,7 +79,10 @@ class Namespace extends NameQualifyingElement, @namespace {
/** Gets the metric namespace. */
MetricNamespace getMetrics() { result = this }
override string toString() { result = this.getQualifiedName() }
/** Gets a version of the `QualifiedName` that is more suitable for display purposes. */
string getFriendlyName() { result = this.getQualifiedName() }
final override string toString() { result = getFriendlyName() }
/** Gets a declaration of (part of) this namespace. */
NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this }
@@ -104,7 +107,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
namespace_decls(underlyingElement(this), unresolveElement(result), _, _)
}
override string toString() { result = this.getNamespace().toString() }
override string toString() { result = this.getNamespace().getFriendlyName() }
/**
* Gets the location of the token preceding the namespace declaration
@@ -150,7 +153,7 @@ class UsingDeclarationEntry extends UsingEntry {
*/
Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _) }
override string toString() { result = "using " + this.getDeclaration().toString() }
override string toString() { result = "using " + this.getDeclaration().getDescription() }
}
/**
@@ -169,7 +172,7 @@ class UsingDirectiveEntry extends UsingEntry {
*/
Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _) }
override string toString() { result = "using namespace " + this.getNamespace().toString() }
override string toString() { result = "using namespace " + this.getNamespace().getFriendlyName() }
}
/**
@@ -204,7 +207,7 @@ class GlobalNamespace extends Namespace {
*/
deprecated string getFullName() { result = this.getName() }
override string toString() { result = "(global namespace)" }
override string getFriendlyName() { result = "(global namespace)" }
}
/**

View File

@@ -260,24 +260,33 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
*/
int getIndex() { param_decl_bind(underlyingElement(this), result, _) }
private string getAnonymousParameterDescription() {
not exists(getName()) and
exists(string idx |
idx =
((getIndex() + 1).toString() + "th")
.replaceAll("1th", "1st")
.replaceAll("2th", "2nd")
.replaceAll("3th", "3rd")
.replaceAll("11st", "11th")
.replaceAll("12nd", "12th")
.replaceAll("13rd", "13th") and
if exists(getCanonicalName())
then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter"
else result = "declaration of " + idx + " parameter"
)
}
override string toString() {
if exists(getName())
then result = super.toString()
else
exists(string idx |
idx =
((getIndex() + 1).toString() + "th")
.replaceAll("1th", "1st")
.replaceAll("2th", "2nd")
.replaceAll("3th", "3rd")
.replaceAll("11st", "11th")
.replaceAll("12nd", "12th")
.replaceAll("13rd", "13th")
|
if exists(getCanonicalName())
then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter"
else result = "declaration of " + idx + " parameter"
)
isDefinition() and
result = "definition of " + getName()
or
not isDefinition() and
if getName() = getCanonicalName()
then result = "declaration of " + getName()
else result = "declaration of " + getCanonicalName() + " as " + getName()
or
result = getAnonymousParameterDescription()
}
/**

View File

@@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File {
XMLFile() { xmlEncoding(this, _) }
/** Gets a printable representation of this XML file. */
override string toString() { result = XMLParent.super.toString() }
override string toString() { result = getName() }
/** Gets the name of this XML file. */
override string getName() { result = File.super.getAbsolutePath() }
@@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
/** Gets a printable representation of this XML element. */
override string toString() { result = XMLParent.super.toString() }
override string toString() { result = getName() }
}
/**

View File

@@ -4,6 +4,11 @@
import cpp
/**
* Gets the number corresponding to the contents of `input` in base-8.
* Note: the first character of `input` must be `0`. For example:
* `parseOctal("012345") = 5349`.
*/
bindingset[input]
int parseOctal(string input) {
input.charAt(0) = "0" and
@@ -15,44 +20,77 @@ int parseOctal(string input) {
)
}
/** Gets the number corresponding to the "set-user-ID on execute bit" in Unix. */
int s_isuid() { result = parseOctal("04000") }
/** Gets the number corresponding to the "set-group-ID on execute bit" in Unix. */
int s_isgid() { result = parseOctal("02000") }
/** Gets the number corresponding to the sticky bit in Unix. */
int s_isvtx() { result = parseOctal("01000") }
/** Gets the number corresponding to the read permission bit for owner of the file in Unix. */
int s_irusr() { result = parseOctal("0400") }
/** Gets the number corresponding to the write permission bit for owner of the file in Unix. */
int s_iwusr() { result = parseOctal("0200") }
/** Gets the number corresponding to the execute permission bit for owner of the file in Unix. */
int s_ixusr() { result = parseOctal("0100") }
/** Gets the number corresponding to the permissions `S_IRUSR | S_IWUSR | S_IXUSR` in Unix. */
int s_irwxu() { result = s_irusr().bitOr(s_iwusr()).bitOr(s_ixusr()) }
/**
* Gets the number corresponding to the read permission bit for the group
* owner of the file in Unix.
*/
int s_irgrp() { result = s_irusr().bitShiftRight(3) }
/**
* Gets the number corresponding to the write permission bit for the group
* owner of the file in Unix.
*/
int s_iwgrp() { result = s_iwusr().bitShiftRight(3) }
/**
* Gets the number corresponding to the execute permission bit for the group
* owner of the file in Unix.
*/
int s_ixgrp() { result = s_ixusr().bitShiftRight(3) }
/** Gets the number corresponding to the permissions `S_IRGRP | S_IWGRP | S_IXGRP` in Unix. */
int s_irwxg() { result = s_irwxu().bitShiftRight(3) }
/** Gets the number corresponding to the read permission bit for other users in Unix. */
int s_iroth() { result = s_irgrp().bitShiftRight(3) }
/** Gets the number corresponding to the write permission bit for other users in Unix. */
int s_iwoth() { result = s_iwgrp().bitShiftRight(3) }
/** Gets the number corresponding to the execute-or-search permission bit for other users in Unix. */
int s_ixoth() { result = s_ixgrp().bitShiftRight(3) }
/** Gets the number corresponding to the permissions `S_IROTH | S_IWOTH | S_IXOTH` in Unix. */
int s_irwxo() { result = s_irwxg().bitShiftRight(3) }
/**
* Gets the number that can be used in a bitwise and with the file status flag
* to produce a number representing the file access mode.
*/
int o_accmode() { result = parseOctal("0003") }
/** Gets the number corresponding to the read-only file access mode. */
int o_rdonly() { result = parseOctal("00") }
/** Gets the number corresponding to the write-only file access mode. */
int o_wronly() { result = parseOctal("01") }
/** Gets the number corresponding to the read-and-write file access mode. */
int o_rdwr() { result = parseOctal("02") }
/** Gets the number corresponding to the file creation flag O_CREAT on Linux. */
int o_creat() { result = parseOctal("0100") }
/** Gets the number corresponding to the file creation flag O_EXCL on Linux. */
int o_excl() { result = parseOctal("0200") }

View File

@@ -1,3 +1,8 @@
/**
* Provides a library for reasoning about control flow at the granularity of basic blocks.
* This is usually much more efficient than reasoning directly at the level of `ControlFlowNode`s.
*/
import cpp
private import internal.PrimitiveBasicBlocks
private import internal.ConstantExprs
@@ -148,22 +153,37 @@ predicate bb_successor = bb_successor_cached/2;
class BasicBlock extends ControlFlowNodeBase {
BasicBlock() { basic_block_entry_node(this) }
/** Holds if this basic block contains `node`. */
predicate contains(ControlFlowNode node) { basic_block_member(node, this, _) }
/** Gets the `ControlFlowNode` at position `pos` in this basic block. */
ControlFlowNode getNode(int pos) { basic_block_member(result, this, pos) }
/** Gets a `ControlFlowNode` in this basic block. */
ControlFlowNode getANode() { basic_block_member(result, this, _) }
/** Gets a `BasicBlock` that is a direct successor of this basic block. */
BasicBlock getASuccessor() { bb_successor(this, result) }
/** Gets a `BasicBlock` that is a direct predecessor of this basic block. */
BasicBlock getAPredecessor() { bb_successor(result, this) }
/**
* Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken
* when the outgoing edge of this basic block is an expression that is true.
*/
BasicBlock getATrueSuccessor() { result.getStart() = this.getEnd().getATrueSuccessor() }
/**
* Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken
* when the outgoing edge of this basic block is an expression that is false.
*/
BasicBlock getAFalseSuccessor() { result.getStart() = this.getEnd().getAFalseSuccessor() }
/** Gets the final `ControlFlowNode` of this basic block. */
ControlFlowNode getEnd() { basic_block_member(result, this, bb_length(this) - 1) }
/** Gets the first `ControlFlowNode` of this basic block. */
ControlFlowNode getStart() { result = this }
/** Gets the number of `ControlFlowNode`s in this basic block. */
@@ -192,6 +212,7 @@ class BasicBlock extends ControlFlowNodeBase {
this.getEnd().getLocation().hasLocationInfo(endf, _, _, endl, endc)
}
/** Gets the function containing this basic block. */
Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() }
/**

View File

@@ -1,3 +1,8 @@
/**
* Provides a library for reasoning about control flow at the granularity of
* individual nodes in the control-flow graph.
*/
import cpp
import BasicBlocks
private import semmle.code.cpp.controlflow.internal.ConstantExprs
@@ -29,8 +34,10 @@ private import semmle.code.cpp.controlflow.internal.CFG
* `Handler`. There are no edges from function calls to `Handler`s.
*/
class ControlFlowNode extends Locatable, ControlFlowNodeBase {
/** Gets a direct successor of this control-flow node, if any. */
ControlFlowNode getASuccessor() { successors_adapted(this, result) }
/** Gets a direct predecessor of this control-flow node, if any. */
ControlFlowNode getAPredecessor() { this = result.getASuccessor() }
/** Gets the function containing this control-flow node. */
@@ -71,6 +78,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
result = getASuccessor()
}
/** Gets the `BasicBlock` containing this control-flow node. */
BasicBlock getBasicBlock() { result.getANode() = this }
}
@@ -86,10 +94,18 @@ import ControlFlowGraphPublic
*/
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
/**
* Holds when `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is true.
*/
predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGTrueSuccessor(n1, n2)
}
/**
* Holds when `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is false.
*/
predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGFalseSuccessor(n1, n2)
}

View File

@@ -15,14 +15,25 @@ import Dereferenced
abstract class DataflowAnnotation extends string {
DataflowAnnotation() { this = "pointer-null" or this = "pointer-valid" }
/** Holds if this annotation is the default annotation. */
abstract predicate isDefault();
/** Holds if this annotation is generated when analyzing expression `e`. */
abstract predicate generatedOn(Expr e);
/**
* Holds if this annotation is generated for the variable `v` when
* the control-flow edge `(src, dest)` is taken.
*/
abstract predicate generatedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest);
/**
* Holds if this annotation is removed for the variable `v` when
* the control-flow edge `(src, dest)` is taken.
*/
abstract predicate killedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest);
/** Holds if expression `e` is given this annotation. */
predicate marks(Expr e) {
this.generatedOn(e) and reachable(e)
or
@@ -31,6 +42,7 @@ abstract class DataflowAnnotation extends string {
exists(LocalScopeVariable v | this.marks(v, e) and e = v.getAnAccess())
}
/** Holds if the variable `v` accessed in control-flow node `n` is given this annotation. */
predicate marks(LocalScopeVariable v, ControlFlowNode n) {
v.getAnAccess().getEnclosingFunction().getBlock() = n and
this.isDefault()
@@ -57,6 +69,10 @@ abstract class DataflowAnnotation extends string {
)
}
/**
* Holds if the variable `v` preserves this annotation when the control-flow
* edge `(src, dest)` is taken.
*/
predicate preservedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) {
this.marks(v, src) and
src.getASuccessor() = dest and
@@ -64,6 +80,10 @@ abstract class DataflowAnnotation extends string {
not v.getAnAssignment() = src
}
/**
* Holds if the variable `v` is assigned this annotation when `src` is an assignment
* expression that assigns to `v` and the control-flow edge `(src, dest)` is taken.
*/
predicate assignedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) {
this.marks(src.(AssignExpr).getRValue()) and
src = v.getAnAssignment() and

View File

@@ -1,3 +1,7 @@
/**
* Provides classes and predicates for reasoning about definitions and uses of variables.
*/
import cpp
private import semmle.code.cpp.controlflow.StackVariableReachability
private import semmle.code.cpp.dataflow.EscapesTree
@@ -135,6 +139,7 @@ library class DefOrUse extends ControlFlowNodeBase {
}
}
/** A definition of a stack variable. */
library class Def extends DefOrUse {
Def() { definition(_, this) }
@@ -149,6 +154,7 @@ private predicate parameterIsOverwritten(Function f, Parameter p) {
definitionBarrier(p, _)
}
/** A definition of a parameter. */
library class ParameterDef extends DefOrUse {
ParameterDef() {
// Optimization: parameters that are not overwritten do not require
@@ -162,6 +168,7 @@ library class ParameterDef extends DefOrUse {
}
}
/** A use of a stack variable. */
library class Use extends DefOrUse {
Use() { useOfVar(_, this) }

View File

@@ -1,3 +1,7 @@
/**
* Provides predicates for detecting whether an expression dereferences a pointer.
*/
import cpp
import Nullness

View File

@@ -1,3 +1,8 @@
/**
* Provides classes and predicates for reasoning about guards and the control
* flow elements controlled by those guards.
*/
import cpp
import semmle.code.cpp.controlflow.BasicBlocks
import semmle.code.cpp.controlflow.SSA

View File

@@ -1,3 +1,8 @@
/**
* Provides classes and predicates for reasoning about guards and the control
* flow elements controlled by those guards.
*/
import cpp
import semmle.code.cpp.ir.IR
@@ -32,7 +37,7 @@ class GuardCondition extends Expr {
}
/**
* Holds if this condition controls `block`, meaning that `block` is only
* Holds if this condition controls `controlled`, meaning that `controlled` is only
* entered if the value of this condition is `testIsTrue`.
*
* Illustration:
@@ -253,7 +258,7 @@ class IRGuardCondition extends Instruction {
IRGuardCondition() { branch = get_branch_for_condition(this) }
/**
* Holds if this condition controls `block`, meaning that `block` is only
* Holds if this condition controls `controlled`, meaning that `controlled` is only
* entered if the value of this condition is `testIsTrue`.
*
* Illustration:
@@ -290,6 +295,10 @@ class IRGuardCondition extends Instruction {
)
}
/**
* Holds if the control-flow edge `(pred, succ)` may be taken only if
* the value of this condition is `testIsTrue`.
*/
cached
predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) {
pred.getASuccessor() = succ and

View File

@@ -1,3 +1,7 @@
/**
* Provides classes and predicates for working with null values and checks for nullness.
*/
import cpp
import DefinitionsAndUses

View File

@@ -1,7 +1,15 @@
/**
* Provides classes and predicates for SSA representation (Static Single Assignment form).
*/
import cpp
import semmle.code.cpp.controlflow.Dominance
import SSAUtils
/**
* The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA.
* This class provides the standard SSA logic.
*/
library class StandardSSA extends SSAHelper {
StandardSSA() { this = 0 }
}
@@ -50,11 +58,13 @@ class SsaDefinition extends ControlFlowNodeBase {
*/
ControlFlowNode getDefinition() { result = this }
/** Gets the `BasicBlock` containing this definition. */
BasicBlock getBasicBlock() { result.contains(getDefinition()) }
/** Holds if this definition is a phi node for variable `v`. */
predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this.(BasicBlock))) }
/** Gets the location of this definition. */
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
/** Holds if the SSA variable `(this, p)` is defined by parameter `p`. */

View File

@@ -1,3 +1,7 @@
/**
* Provides classes and predicates for use in the SSA library.
*/
import cpp
import semmle.code.cpp.controlflow.Dominance
import semmle.code.cpp.controlflow.SSA // must be imported for proper caching of SSAHelper

View File

@@ -1,3 +1,8 @@
/**
* Provides a library for working with local (intra-procedural) control-flow
* reachability involving stack variables.
*/
import cpp
/**

View File

@@ -168,6 +168,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
/** Gets the first control-flow node in this `SubBasicBlock`. */
ControlFlowNode getStart() { result = this }
/** Gets the function that contains this `SubBasicBlock`. */
pragma[noinline]
Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() }
}

View File

@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
/** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }

View File

@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
/** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }

View File

@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
/** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }

View File

@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
/** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }

View File

@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
/** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }

View File

@@ -116,33 +116,12 @@ class ExprNode extends Node, TExprNode {
override string toString() { result = expr.toString() }
override Location getLocation() {
result = getExprLocationOverride(expr)
or
not exists(getExprLocationOverride(expr)) and
result = expr.getLocation()
}
override Location getLocation() { result = expr.getLocation() }
/** Gets the expression corresponding to this node. */
Expr getExpr() { result = expr }
}
/**
* Gets a location for `e` that's more accurate than `e.getLocation()`, if any.
*/
private Location getExprLocationOverride(Expr e) {
// Base case: the parent has a better location than `e`.
e.getLocation() instanceof UnknownExprLocation and
result = e.getParent().getLocation() and
not result instanceof UnknownLocation
or
// Recursive case: the parent has a location override that's better than what
// `e` has.
e.getLocation() instanceof UnknownExprLocation and
result = getExprLocationOverride(e.getParent()) and
not result instanceof UnknownLocation
}
abstract class ParameterNode extends Node, TNode {
/**
* Holds if this node is the parameter of `c` at the specified (zero-based)

View File

@@ -168,6 +168,7 @@ class SubBasicBlock extends ControlFlowNodeBase {
/** Gets the first control-flow node in this `SubBasicBlock`. */
ControlFlowNode getStart() { result = this }
/** Gets the function that contains this `SubBasicBlock`. */
pragma[noinline]
Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() }
}

View File

@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
defaultTaintBarrier(node)
}
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
isSanitizerEdge(node1, node2)
}
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }

View File

@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
defaultTaintBarrier(node)
}
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
isSanitizerEdge(node1, node2)
}
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }

View File

@@ -6,9 +6,13 @@ private import semmle.code.cpp.dataflow.EscapesTree
/**
* A C/C++ access expression. This refers to a function, variable, or enum constant.
*/
abstract class Access extends Expr, NameQualifiableElement {
class Access extends Expr, NameQualifiableElement, @access {
// As `@access` is a union type containing `@routineexpr` (which describes function accesses
// that are called), we need to exclude function calls.
Access() { this instanceof @routineexpr implies not iscall(underlyingElement(this), _) }
/** Gets the accessed function, variable, or enum constant. */
abstract Declaration getTarget();
Declaration getTarget() { none() } // overridden in subclasses
override predicate mayBeImpure() { none() }

View File

@@ -7,7 +7,7 @@ private import semmle.code.cpp.internal.ResolveClass
* Instances of this class are not present in the main AST which is navigated by parent/child links. Instead,
* instances of this class are attached to nodes in the main AST via special conversion links.
*/
abstract class Conversion extends Expr {
class Conversion extends Expr, @conversion {
/** Gets the expression being converted. */
Expr getExpr() { result.getConversion() = this }

View File

@@ -53,7 +53,32 @@ class Expr extends StmtParent, @expr {
Element getParent() { exprparents(underlyingElement(this), _, unresolveElement(result)) }
/** Gets the location of this expression. */
override Location getLocation() { exprs(underlyingElement(this), _, result) }
override Location getLocation() {
result = this.getExprLocationOverride()
or
not exists(this.getExprLocationOverride()) and
result = this.getDbLocation()
}
/**
* Gets a location for this expression that's more accurate than
* `getDbLocation()`, if any.
*/
private Location getExprLocationOverride() {
// Base case: the parent has a better location than `this`.
this.getDbLocation() instanceof UnknownExprLocation and
result = this.getParent().(Expr).getDbLocation() and
not result instanceof UnknownLocation
or
// Recursive case: the parent has a location override that's better than
// what `this` has.
this.getDbLocation() instanceof UnknownExprLocation and
result = this.getParent().(Expr).getExprLocationOverride() and
not result instanceof UnknownLocation
}
/** Gets the location of this expressions, raw from the database. */
private Location getDbLocation() { exprs(underlyingElement(this), _, result) }
/** Holds if this is an auxiliary expression generated by the compiler. */
predicate isCompilerGenerated() {

View File

@@ -86,7 +86,7 @@ class Closure extends Class {
result.getName() = "operator()"
}
override string toString() { result = "decltype([...](...){...})" }
override string getDescription() { result = "decltype([...](...){...})" }
}
/**
@@ -99,7 +99,7 @@ class Closure extends Class {
* ```
*/
class LambdaCapture extends Locatable, @lambdacapture {
override string toString() { result = getField().toString() }
override string toString() { result = getField().getName() }
override string getCanonicalQLClass() { result = "LambdaCapture" }

View File

@@ -1,10 +1,12 @@
import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.DataFlow2
private import semmle.code.cpp.ir.dataflow.DataFlow3
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
@@ -31,8 +33,7 @@ private predicate predictableInstruction(Instruction instr) {
* 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.
* library's `returnArgument` predicate.
*/
predicate predictableOnlyFlow(string name) {
name = "strcasestr" or
@@ -41,7 +42,6 @@ predicate predictableOnlyFlow(string name) {
name = "strchrnul" or
name = "strcmp" or
name = "strcspn" or
name = "strlen" or // special case
name = "strncmp" or
name = "strndup" or
name = "strnlen" or
@@ -173,11 +173,34 @@ private predicate hasUpperBoundsCheck(Variable var) {
)
}
private predicate nodeIsBarrierEqualityCandidate(
DataFlow::Node node, Operand access, Variable checkedVar
) {
readsVariable(node.asInstruction(), checkedVar) and
any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true)
}
private predicate nodeIsBarrier(DataFlow::Node node) {
exists(Variable checkedVar |
readsVariable(node.asInstruction(), checkedVar) and
hasUpperBoundsCheck(checkedVar)
)
or
exists(Variable checkedVar, Operand access |
/*
* This node is guarded by a condition that forces the accessed variable
* to equal something else. For example:
* ```
* x = taintsource()
* if (x == 10) {
* taintsink(x); // not considered tainted
* }
* ```
*/
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
readsVariable(access.getDef(), checkedVar)
)
}
private predicate nodeIsBarrierIn(DataFlow::Node node) {

View File

@@ -77,49 +77,48 @@ private module VirtualDispatch {
// Local flow
DataFlow::localFlowStep(src, other) and
allowFromArg = allowOtherFromArg
)
or
// Flow through global variable
exists(StoreInstruction store |
store = src.asInstruction() and
(
exists(Variable var |
var = store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() and
this.flowsFromGlobal(var)
)
or
// Flow from global variable to load.
exists(LoadInstruction load, GlobalOrNamespaceVariable var |
var = src.asVariable() and
other.asInstruction() = load and
// The `allowFromArg` concept doesn't play a role when `src` is a
// global variable, so we just set it to a single arbitrary value for
// performance.
allowFromArg = true
|
// Load directly from the global variable
load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
or
exists(Variable var, FieldAccess a |
var =
store
.getDestinationAddress()
.(FieldAddressInstruction)
.getObjectAddress()
.(VariableAddressInstruction)
.getASTVariable() and
this.flowsFromGlobalUnionField(var, a)
// Load from a field on a global union
exists(FieldAddressInstruction fa |
fa = load.getSourceAddress() and
fa.getObjectAddress().(VariableAddressInstruction).getASTVariable() = var and
fa.getField().getDeclaringType() instanceof Union
)
) and
allowFromArg = true
)
}
private predicate flowsFromGlobal(GlobalOrNamespaceVariable var) {
exists(LoadInstruction load |
this.flowsFrom(DataFlow::instructionNode(load), _) and
load.getSourceAddress().(VariableAddressInstruction).getASTVariable() = var
)
}
private predicate flowsFromGlobalUnionField(Variable var, FieldAccess a) {
a.getTarget().getDeclaringType() instanceof Union and
exists(LoadInstruction load |
this.flowsFrom(DataFlow::instructionNode(load), _) and
load
.getSourceAddress()
.(FieldAddressInstruction)
.getObjectAddress()
.(VariableAddressInstruction)
.getASTVariable() = var
)
or
// Flow from store to global variable. These cases are similar to the
// above but have `StoreInstruction` instead of `LoadInstruction` and
// have the roles swapped between `other` and `src`.
exists(StoreInstruction store, GlobalOrNamespaceVariable var |
var = other.asVariable() and
store = src.asInstruction() and
// Setting `allowFromArg` to `true` like in the base case means we
// treat a store to a global variable like the dispatch itself: flow
// may come from anywhere.
allowFromArg = true
|
// Store directly to the global variable
store.getDestinationAddress().(VariableAddressInstruction).getASTVariable() = var
or
// Store to a field on a global union
exists(FieldAddressInstruction fa |
fa = store.getDestinationAddress() and
fa.getObjectAddress().(VariableAddressInstruction).getASTVariable() = var and
fa.getField().getDeclaringType() instanceof Union
)
)
)
}
}

View File

@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
/** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }

View File

@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
/** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }

View File

@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
/** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }

View File

@@ -66,9 +66,6 @@ abstract class Configuration extends string {
*/
predicate isBarrier(Node node) { none() }
/** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */
deprecated predicate isBarrierEdge(Node node1, Node node2) { none() }
/** Holds if data flow into `node` is prohibited. */
predicate isBarrierIn(Node node) { none() }

View File

@@ -71,9 +71,7 @@ class Node extends TIRDataFlowNode {
* `x.set(taint())` is a partial definition of `x`, and `transfer(&x, taint())` is
* a partial definition of `&x`).
*/
Expr asPartialDefinition() {
result = this.(PartialDefinitionNode).getInstruction().getUnconvertedResultExpression()
}
Expr asPartialDefinition() { result = this.(PartialDefinitionNode).getDefinedExpr() }
/**
* DEPRECATED: See UninitializedNode.
@@ -184,8 +182,6 @@ class ParameterNode extends InstructionNode {
// To avoid making this class abstract, we enumerate its values here
instr instanceof InitializeParameterInstruction
or
instr instanceof InitializeThisInstruction
or
instr instanceof InitializeIndirectionInstruction
}
@@ -198,9 +194,11 @@ class ParameterNode extends InstructionNode {
}
/** An explicit positional parameter, not including `this` or `...`. */
class ExplicitParameterNode extends ParameterNode {
private class ExplicitParameterNode extends ParameterNode {
override InitializeParameterInstruction instr;
ExplicitParameterNode() { exists(instr.getParameter()) }
override predicate isParameterOf(Function f, int pos) {
f.getParameter(pos) = instr.getParameter()
}
@@ -213,7 +211,9 @@ class ExplicitParameterNode extends ParameterNode {
/** An implicit `this` parameter. */
class ThisParameterNode extends ParameterNode {
override InitializeThisInstruction instr;
override InitializeParameterInstruction instr;
ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable }
override predicate isParameterOf(Function f, int pos) {
pos = -1 and instr.getEnclosingFunction() = f
@@ -228,15 +228,16 @@ class ParameterIndirectionNode extends ParameterNode {
override predicate isParameterOf(Function f, int pos) {
exists(int index |
f.getParameter(index) = instr.getParameter() and
f.getParameter(index) = instr.getParameter()
or
index = -1 and
instr.getIRVariable().(IRThisVariable).getEnclosingFunction() = f
|
pos = getArgumentPosOfSideEffect(index)
)
}
/** Gets the `Parameter` associated with this node. */
Parameter getParameter() { result = instr.getParameter() }
override string toString() { result = "*" + this.getParameter().toString() }
override string toString() { result = "*" + instr.getIRVariable().toString() }
}
/**
@@ -290,14 +291,17 @@ abstract class PostUpdateNode extends InstructionNode {
* setY(&x); // a partial definition of the object `x`.
* ```
*/
abstract private class PartialDefinitionNode extends PostUpdateNode, TInstructionNode { }
abstract private class PartialDefinitionNode extends PostUpdateNode, TInstructionNode {
abstract Expr getDefinedExpr();
}
private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
override ChiInstruction instr;
FieldAddressInstruction field;
ExplicitFieldStoreQualifierNode() {
not instr.isResultConflated() and
exists(StoreInstruction store, FieldInstruction field |
exists(StoreInstruction store |
instr.getPartial() = store and field = store.getDestinationAddress()
)
}
@@ -307,6 +311,10 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
// DataFlowImplConsistency::Consistency. However, it's not clear what (if any) implications
// this consistency failure has.
override Node getPreUpdateNode() { result.asInstruction() = instr.getTotal() }
override Expr getDefinedExpr() {
result = field.getObjectAddress().getUnconvertedResultExpression()
}
}
/**
@@ -317,15 +325,18 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode {
*/
private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode {
override StoreInstruction instr;
FieldAddressInstruction field;
ExplicitSingleFieldStoreQualifierNode() {
exists(FieldAddressInstruction field |
field = instr.getDestinationAddress() and
not exists(ChiInstruction chi | chi.getPartial() = instr)
)
field = instr.getDestinationAddress() and
not exists(ChiInstruction chi | chi.getPartial() = instr)
}
override Node getPreUpdateNode() { none() }
override Expr getDefinedExpr() {
result = field.getObjectAddress().getUnconvertedResultExpression()
}
}
/**
@@ -522,9 +533,9 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// for now.
iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom
or
// The next two rules allow flow from partial definitions in setters to succeeding loads in the caller.
// First, we add flow from write side-effects to non-conflated chi instructions through their
// partial operands. Consider the following example:
// Add flow from write side-effects to non-conflated chi instructions through their
// partial operands. From there, a `readStep` will find subsequent reads of that field.
// Consider the following example:
// ```
// void setX(Point* p, int new_x) {
// p->x = new_x;
@@ -534,14 +545,9 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction
// ```
// Here, a `WriteSideEffectInstruction` will provide a new definition for `p->x` after the call to
// `setX`, which will be melded into `p` through a chi instruction.
iTo.getAnOperand().(ChiPartialOperand).getDef() = iFrom.(WriteSideEffectInstruction) and
not iTo.isResultConflated()
or
// Next, we add flow from non-conflated chi instructions to loads (even when they are not precise).
// This ensures that loads of `p->x` gets data flow from the `WriteSideEffectInstruction` above.
exists(ChiInstruction chi | iFrom = chi |
not chi.isResultConflated() and
iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = chi
exists(ChiInstruction chi | chi = iTo |
chi.getPartialOperand().getDef() = iFrom.(WriteSideEffectInstruction) and
not chi.isResultConflated()
)
or
// Flow from stores to structs with a single field to a load of that field.

View File

@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
defaultTaintBarrier(node)
}
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
isSanitizerEdge(node1, node2)
}
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }

View File

@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
defaultTaintBarrier(node)
}
/** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */
deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() }
deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) {
isSanitizerEdge(node1, node2)
}
/** Holds if data flow into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }

View File

@@ -92,11 +92,3 @@ class ChiTotalMemoryAccess extends MemoryAccessKind, TChiTotalMemoryAccess {
class ChiPartialMemoryAccess extends MemoryAccessKind, TChiPartialMemoryAccess {
override string toString() { result = "chi(partial)" }
}
/**
* The operand accesses memory not modeled in SSA. Used only on the result of
* `UnmodeledDefinition` and on the operands of `UnmodeledUse`.
*/
class UnmodeledMemoryAccess extends MemoryAccessKind, TUnmodeledMemoryAccess {
override string toString() { result = "unmodeled" }
}

View File

@@ -60,8 +60,6 @@ private newtype TOpcode =
TThrowValue() or
TReThrow() or
TUnwind() or
TUnmodeledDefinition() or
TUnmodeledUse() or
TAliasedDefinition() or
TInitializeNonLocal() or
TAliasedUse() or
@@ -579,22 +577,6 @@ module Opcode {
final override string toString() { result = "Unwind" }
}
class UnmodeledDefinition extends Opcode, TUnmodeledDefinition {
final override string toString() { result = "UnmodeledDefinition" }
final override MemoryAccessKind getWriteMemoryAccess() {
result instanceof UnmodeledMemoryAccess
}
}
class UnmodeledUse extends Opcode, TUnmodeledUse {
final override string toString() { result = "UnmodeledUse" }
final override predicate hasOperandInternal(OperandTag tag) {
tag instanceof UnmodeledUseOperandTag
}
}
class AliasedDefinition extends Opcode, TAliasedDefinition {
final override string toString() { result = "AliasedDefinition" }

View File

@@ -31,10 +31,14 @@ class IRBlockBase extends TIRBlock {
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock |
funcBlock.getEnclosingFunction() = getEnclosingFunction()
rank[result + 1](IRBlock funcBlock, int sortOverride |
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
// Ensure that the block containing `EnterFunction` always comes first.
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
then sortOverride = 0
else sortOverride = 1
|
funcBlock order by funcBlock.getUniqueId()
funcBlock order by sortOverride, funcBlock.getUniqueId()
)
}

View File

@@ -55,7 +55,6 @@ module InstructionConsistency {
operand.getOperandTag() = tag
) and
operandCount > 1 and
not tag instanceof UnmodeledUseOperandTag and
message =
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
" in function '$@'." and
@@ -150,20 +149,16 @@ module InstructionConsistency {
}
/**
* Holds if a memory operand is connected to a definition with an unmodeled result, other than
* `UnmodeledDefinition` itself.
* Holds if a memory operand is connected to a definition with an unmodeled result.
*/
query predicate memoryOperandDefinitionIsUnmodeled(
Instruction instr, string message, IRFunction func, string funcText
) {
exists(MemoryOperand operand, Instruction def |
operand = instr.getAnOperand() and
not operand instanceof UnmodeledUseOperand and
def = operand.getAnyDef() and
not def.isResultModeled() and
not def instanceof UnmodeledDefinitionInstruction and
message =
"Memory operand definition has unmodeled result, but is not the `UnmodeledDefinition` instruction in function '$@'" and
message = "Memory operand definition has unmodeled result in function '$@'" and
func = instr.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
)
@@ -259,8 +254,6 @@ module InstructionConsistency {
Operand useOperand, string message, IRFunction func, string funcText
) {
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
not useOperand.getUse() instanceof UnmodeledUseInstruction and
not defInstr instanceof UnmodeledDefinitionInstruction and
pointOfEvaluation(useOperand, useBlock, useIndex) and
defInstr = useOperand.getAnyDef() and
(
@@ -309,8 +302,6 @@ module InstructionConsistency {
private predicate shouldBeConflated(Instruction instr) {
isOnAliasedDefinitionChain(instr)
or
instr instanceof UnmodeledDefinitionInstruction
or
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}

View File

@@ -40,16 +40,6 @@ class IRFunction extends TIRFunction {
result.getEnclosingIRFunction() = this
}
pragma[noinline]
final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() {
result.getEnclosingIRFunction() = this
}
pragma[noinline]
final UnmodeledUseInstruction getUnmodeledUseInstruction() {
result.getEnclosingIRFunction() = this
}
/**
* Gets the single return instruction for this function.
*/

View File

@@ -223,6 +223,15 @@ class IREllipsisVariable extends IRTempVariable {
final override string toString() { result = "#ellipsis" }
}
/**
* A temporary variable generated to hold the `this` pointer.
*/
class IRThisVariable extends IRTempVariable {
IRThisVariable() { tag = ThisTempVar() }
final override string toString() { result = "#this" }
}
/**
* A variable generated to represent the contents of a string literal. This variable acts much like
* a read-only global variable.

View File

@@ -320,8 +320,7 @@ class Instruction extends Construction::TInstruction {
/**
* Holds if the result of this instruction is precisely modeled in SSA. Always
* holds for a register result. For a memory result, a modeled result is
* connected to its actual uses. An unmodeled result is connected to the
* `UnmodeledUse` instruction.
* connected to its actual uses. An unmodeled result has no uses.
*
* For example:
* ```
@@ -1230,10 +1229,6 @@ class CatchAnyInstruction extends CatchInstruction {
CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny }
}
class UnmodeledDefinitionInstruction extends Instruction {
UnmodeledDefinitionInstruction() { getOpcode() instanceof Opcode::UnmodeledDefinition }
}
/**
* An instruction that initializes all escaped memory.
*/
@@ -1248,12 +1243,6 @@ class AliasedUseInstruction extends Instruction {
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
}
class UnmodeledUseInstruction extends Instruction {
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
override string getOperandsString() { result = "mu*" }
}
/**
* An instruction representing the choice of one of multiple input values based on control flow.
*

View File

@@ -14,16 +14,8 @@ private newtype TOperand =
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) and
(
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
or
tag instanceof UnmodeledUseOperandTag
)
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
useInstr.getOpcode().hasOperand(tag)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
@@ -31,6 +23,57 @@ private newtype TOperand =
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
/**
* Base class for all register operands. This is a placeholder for the IPA union type that we will
* eventually use for this purpose.
*/
private class RegisterOperandBase extends TRegisterOperand {
/** Gets a textual representation of this element. */
abstract string toString();
}
/**
* Returns the register operand with the specified parameters.
*/
private RegisterOperandBase registerOperand(
Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
) {
result = TRegisterOperand(useInstr, tag, defInstr)
}
/**
* Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
* will eventually use for this purpose.
*/
private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
/** Gets a textual representation of this element. */
abstract string toString();
}
/**
* Returns the non-Phi memory operand with the specified parameters.
*/
private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
result = TNonPhiMemoryOperand(useInstr, tag)
}
/**
* Base class for all Phi operands. This is a placeholder for the IPA union type that we will
* eventually use for this purpose.
*/
private class PhiOperandBase extends TPhiOperand {
abstract string toString();
}
/**
* Returns the Phi operand with the specified parameters.
*/
private PhiOperandBase phiOperand(
Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
) {
result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
}
/**
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
*/
@@ -104,7 +147,17 @@ class Operand extends TOperand {
* For example: `this:r3_5`
*/
final string getDumpString() {
result = getDumpLabel() + getInexactSpecifier() + getAnyDef().getResultId()
result = getDumpLabel() + getInexactSpecifier() + getDefinitionId()
}
/**
* Gets a string containing the identifier of the definition of this use, or `m?` if the
* definition is not modeled in SSA.
*/
private string getDefinitionId() {
result = getAnyDef().getResultId()
or
not exists(getAnyDef()) and result = "m?"
}
/**
@@ -169,8 +222,8 @@ class Operand extends TOperand {
*/
class MemoryOperand extends Operand {
MemoryOperand() {
this = TNonPhiMemoryOperand(_, _, _, _) or
this = TPhiOperand(_, _, _, _)
this instanceof NonPhiMemoryOperandBase or
this instanceof PhiOperandBase
}
/**
@@ -204,18 +257,15 @@ class MemoryOperand extends Operand {
*/
class NonPhiOperand extends Operand {
Instruction useInstr;
Instruction defInstr;
OperandTag tag;
NonPhiOperand() {
this = TRegisterOperand(useInstr, tag, defInstr) or
this = TNonPhiMemoryOperand(useInstr, tag, defInstr, _)
this = registerOperand(useInstr, tag, _) or
this = nonPhiMemoryOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
final override Instruction getAnyDef() { result = defInstr }
final override string getDumpLabel() { result = tag.getLabel() }
final override int getDumpSortOrder() { result = tag.getSortOrder() }
@@ -226,8 +276,15 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
override RegisterOperandTag tag;
Instruction defInstr;
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
final override string toString() { result = tag.toString() }
final override Instruction getAnyDef() { result = defInstr }
final override Overlap getDefinitionOverlap() {
// All register results overlap exactly with their uses.
@@ -235,13 +292,25 @@ class RegisterOperand extends NonPhiOperand, TRegisterOperand {
}
}
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
override MemoryOperandTag tag;
Overlap overlap;
NonPhiMemoryOperand() { this = TNonPhiMemoryOperand(useInstr, tag, defInstr, overlap) }
NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
final override Overlap getDefinitionOverlap() { result = overlap }
final override string toString() { result = tag.toString() }
final override Instruction getAnyDef() {
result = unique(Instruction defInstr | hasDefinition(defInstr, _))
}
final override Overlap getDefinitionOverlap() { hasDefinition(_, result) }
pragma[noinline]
private predicate hasDefinition(Instruction defInstr, Overlap overlap) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not Construction::isInCycle(useInstr) and
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
}
}
class TypedOperand extends NonPhiMemoryOperand {
@@ -258,8 +327,6 @@ class TypedOperand extends NonPhiMemoryOperand {
*/
class AddressOperand extends RegisterOperand {
override AddressOperandTag tag;
override string toString() { result = "Address" }
}
/**
@@ -268,8 +335,6 @@ class AddressOperand extends RegisterOperand {
*/
class BufferSizeOperand extends RegisterOperand {
override BufferSizeOperandTag tag;
override string toString() { result = "BufferSize" }
}
/**
@@ -278,8 +343,6 @@ class BufferSizeOperand extends RegisterOperand {
*/
class LoadOperand extends TypedOperand {
override LoadOperandTag tag;
override string toString() { result = "Load" }
}
/**
@@ -287,8 +350,6 @@ class LoadOperand extends TypedOperand {
*/
class StoreValueOperand extends RegisterOperand {
override StoreValueOperandTag tag;
override string toString() { result = "StoreValue" }
}
/**
@@ -296,8 +357,6 @@ class StoreValueOperand extends RegisterOperand {
*/
class UnaryOperand extends RegisterOperand {
override UnaryOperandTag tag;
override string toString() { result = "Unary" }
}
/**
@@ -305,8 +364,6 @@ class UnaryOperand extends RegisterOperand {
*/
class LeftOperand extends RegisterOperand {
override LeftOperandTag tag;
override string toString() { result = "Left" }
}
/**
@@ -314,8 +371,6 @@ class LeftOperand extends RegisterOperand {
*/
class RightOperand extends RegisterOperand {
override RightOperandTag tag;
override string toString() { result = "Right" }
}
/**
@@ -323,18 +378,6 @@ class RightOperand extends RegisterOperand {
*/
class ConditionOperand extends RegisterOperand {
override ConditionOperandTag tag;
override string toString() { result = "Condition" }
}
/**
* An operand of the special `UnmodeledUse` instruction, representing a value
* whose set of uses is unknown.
*/
class UnmodeledUseOperand extends NonPhiMemoryOperand {
override UnmodeledUseOperandTag tag;
override string toString() { result = "UnmodeledUse" }
}
/**
@@ -342,8 +385,6 @@ class UnmodeledUseOperand extends NonPhiMemoryOperand {
*/
class CallTargetOperand extends RegisterOperand {
override CallTargetOperandTag tag;
override string toString() { result = "CallTarget" }
}
/**
@@ -361,8 +402,6 @@ class ArgumentOperand extends RegisterOperand {
*/
class ThisArgumentOperand extends ArgumentOperand {
override ThisArgumentOperandTag tag;
override string toString() { result = "ThisArgument" }
}
/**
@@ -370,34 +409,27 @@ class ThisArgumentOperand extends ArgumentOperand {
*/
class PositionalArgumentOperand extends ArgumentOperand {
override PositionalArgumentOperandTag tag;
int argIndex;
PositionalArgumentOperand() { argIndex = tag.getArgIndex() }
override string toString() { result = "Arg(" + argIndex + ")" }
/**
* Gets the zero-based index of the argument.
*/
final int getIndex() { result = argIndex }
final int getIndex() { result = tag.getArgIndex() }
}
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
override string toString() { result = "SideEffect" }
}
/**
* An operand of a `PhiInstruction`.
*/
class PhiInputOperand extends MemoryOperand, TPhiOperand {
class PhiInputOperand extends MemoryOperand, PhiOperandBase {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
PhiInputOperand() { this = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap) }
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
override string toString() { result = "Phi" }
@@ -427,8 +459,6 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
class ChiTotalOperand extends NonPhiMemoryOperand {
override ChiTotalOperandTag tag;
override string toString() { result = "ChiTotal" }
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess }
}
@@ -438,7 +468,5 @@ class ChiTotalOperand extends NonPhiMemoryOperand {
class ChiPartialOperand extends NonPhiMemoryOperand {
override ChiPartialOperandTag tag;
override string toString() { result = "ChiPartial" }
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess }
}

View File

@@ -204,7 +204,7 @@ private predicate isArgumentForParameter(CallInstruction ci, Operand operand, In
init.(InitializeParameterInstruction).getParameter() =
f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
init instanceof InitializeThisInstruction and
init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and
init.getEnclosingFunction() = f and
operand instanceof ThisArgumentOperand
) and
@@ -247,6 +247,10 @@ private predicate resultMayReachReturn(Instruction instr) { operandMayReachRetur
private predicate resultEscapesNonReturn(Instruction instr) {
// The result escapes if it has at least one use that escapes.
operandEscapesNonReturn(instr.getAUse())
or
// The result also escapes if it is not modeled in SSA, because we do not know where it might be
// used.
not instr.isResultModeled()
}
/**

View File

@@ -5,7 +5,7 @@ private import AliasAnalysis
private newtype TAllocation =
TVariableAllocation(IRVariable var) or
TIndirectParameterAllocation(IRAutomaticUserVariable var) {
TIndirectParameterAllocation(IRAutomaticVariable var) {
exists(InitializeIndirectionInstruction instr | instr.getIRVariable() = var)
} or
TDynamicAllocation(CallInstruction call) {
@@ -74,7 +74,7 @@ class VariableAllocation extends Allocation, TVariableAllocation {
}
class IndirectParameterAllocation extends Allocation, TIndirectParameterAllocation {
IRAutomaticUserVariable var;
IRAutomaticVariable var;
IndirectParameterAllocation() { this = TIndirectParameterAllocation(var) }
@@ -90,7 +90,7 @@ class IndirectParameterAllocation extends Allocation, TIndirectParameterAllocati
final override string getUniqueId() { result = var.getUniqueId() }
final override IRType getIRType() { result = var.getIRType() }
final override IRType getIRType() { result instanceof IRUnknownType }
final override predicate isReadOnly() { none() }

View File

@@ -67,8 +67,6 @@ private module Cached {
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
instruction instanceof UnmodeledDefinitionInstruction
or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
@@ -127,40 +125,13 @@ private module Cached {
oldInstruction = getOldInstruction(instruction) and
oldOperand = oldInstruction.getAnOperand() and
tag = oldOperand.getOperandTag() and
(
(
if exists(Alias::getOperandMemoryLocation(oldOperand))
then hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
else (
result = instruction.getEnclosingIRFunction().getUnmodeledDefinitionInstruction() and
overlap instanceof MustTotallyOverlap
)
)
or
// Connect any definitions that are not being modeled in SSA to the
// `UnmodeledUse` instruction.
exists(OldInstruction oldDefinition |
instruction instanceof UnmodeledUseInstruction and
tag instanceof UnmodeledUseOperandTag and
oldDefinition = oldOperand.getAnyDef() and
not exists(Alias::getResultMemoryLocation(oldDefinition)) and
result = getNewInstruction(oldDefinition) and
overlap instanceof MustTotallyOverlap
)
)
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
)
or
instruction = Chi(getOldInstruction(result)) and
tag instanceof ChiPartialOperandTag and
overlap instanceof MustExactlyOverlap
or
exists(IRFunction f |
tag instanceof UnmodeledUseOperandTag and
result = f.getUnmodeledDefinitionInstruction() and
instruction = f.getUnmodeledUseInstruction() and
overlap instanceof MustTotallyOverlap
)
or
tag instanceof ChiTotalOperandTag and
result = getChiInstructionTotalOperand(instruction) and
overlap instanceof MustExactlyOverlap
@@ -942,6 +913,9 @@ private module CachedForDebugging {
}
module SSAConsistency {
/**
* Holds if a `MemoryOperand` has more than one `MemoryLocation` assigned by alias analysis.
*/
query predicate multipleOperandMemoryLocations(
OldIR::MemoryOperand operand, string message, OldIR::IRFunction func, string funcText
) {
@@ -954,6 +928,9 @@ module SSAConsistency {
)
}
/**
* Holds if a `MemoryLocation` does not have an associated `VirtualVariable`.
*/
query predicate missingVirtualVariableForMemoryLocation(
Alias::MemoryLocation location, string message, OldIR::IRFunction func, string funcText
) {
@@ -962,4 +939,25 @@ module SSAConsistency {
funcText = Language::getIdentityString(func.getFunction()) and
message = "Memory location has no virtual variable in function '$@'."
}
/**
* Holds if a `MemoryLocation` is a member of more than one `VirtualVariable`.
*/
query predicate multipleVirtualVariablesForMemoryLocation(
Alias::MemoryLocation location, string message, OldIR::IRFunction func, string funcText
) {
exists(int vvarCount |
vvarCount = strictcount(location.getVirtualVariable()) and
vvarCount > 1 and
func = location.getIRFunction() and
funcText = Language::getIdentityString(func.getFunction()) and
message =
"Memory location has " + vvarCount.toString() + " virtual variables in function '$@': (" +
concat(Alias::VirtualVariable vvar |
vvar = location.getVirtualVariable()
|
vvar.toString(), ", "
) + ")."
)
}
}

View File

@@ -15,7 +15,6 @@ private newtype TOperandTag =
TLeftOperand() or
TRightOperand() or
TConditionOperand() or
TUnmodeledUseOperand() or
TCallTargetOperand() or
TThisArgumentOperand() or
TPositionalArgumentOperand(int argIndex) { Language::hasPositionalArgIndex(argIndex) } or
@@ -165,18 +164,6 @@ class ConditionOperandTag extends RegisterOperandTag, TConditionOperand {
ConditionOperandTag conditionOperand() { result = TConditionOperand() }
/**
* An operand of the special `UnmodeledUse` instruction, representing a value
* whose set of uses is unknown.
*/
class UnmodeledUseOperandTag extends MemoryOperandTag, TUnmodeledUseOperand {
final override string toString() { result = "UnmodeledUse" }
final override int getSortOrder() { result = 9 }
}
UnmodeledUseOperandTag unmodeledUseOperand() { result = TUnmodeledUseOperand() }
/**
* The operand representing the target function of an `Call` instruction.
*/
@@ -234,7 +221,9 @@ PositionalArgumentOperandTag positionalArgumentOperand(int argIndex) {
result = TPositionalArgumentOperand(argIndex)
}
class ChiTotalOperandTag extends MemoryOperandTag, TChiTotalOperand {
abstract class ChiOperandTag extends MemoryOperandTag { }
class ChiTotalOperandTag extends ChiOperandTag, TChiTotalOperand {
final override string toString() { result = "ChiTotal" }
final override int getSortOrder() { result = 13 }
@@ -244,7 +233,7 @@ class ChiTotalOperandTag extends MemoryOperandTag, TChiTotalOperand {
ChiTotalOperandTag chiTotalOperand() { result = TChiTotalOperand() }
class ChiPartialOperandTag extends MemoryOperandTag, TChiPartialOperand {
class ChiPartialOperandTag extends ChiOperandTag, TChiPartialOperand {
final override string toString() { result = "ChiPartial" }
final override int getSortOrder() { result = 14 }

View File

@@ -31,10 +31,14 @@ class IRBlockBase extends TIRBlock {
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
) and
this =
rank[result + 1](IRBlock funcBlock |
funcBlock.getEnclosingFunction() = getEnclosingFunction()
rank[result + 1](IRBlock funcBlock, int sortOverride |
funcBlock.getEnclosingFunction() = getEnclosingFunction() and
// Ensure that the block containing `EnterFunction` always comes first.
if funcBlock.getFirstInstruction() instanceof EnterFunctionInstruction
then sortOverride = 0
else sortOverride = 1
|
funcBlock order by funcBlock.getUniqueId()
funcBlock order by sortOverride, funcBlock.getUniqueId()
)
}

View File

@@ -55,7 +55,6 @@ module InstructionConsistency {
operand.getOperandTag() = tag
) and
operandCount > 1 and
not tag instanceof UnmodeledUseOperandTag and
message =
"Instruction has " + operandCount + " operands with tag '" + tag.toString() + "'" +
" in function '$@'." and
@@ -150,20 +149,16 @@ module InstructionConsistency {
}
/**
* Holds if a memory operand is connected to a definition with an unmodeled result, other than
* `UnmodeledDefinition` itself.
* Holds if a memory operand is connected to a definition with an unmodeled result.
*/
query predicate memoryOperandDefinitionIsUnmodeled(
Instruction instr, string message, IRFunction func, string funcText
) {
exists(MemoryOperand operand, Instruction def |
operand = instr.getAnOperand() and
not operand instanceof UnmodeledUseOperand and
def = operand.getAnyDef() and
not def.isResultModeled() and
not def instanceof UnmodeledDefinitionInstruction and
message =
"Memory operand definition has unmodeled result, but is not the `UnmodeledDefinition` instruction in function '$@'" and
message = "Memory operand definition has unmodeled result in function '$@'" and
func = instr.getEnclosingIRFunction() and
funcText = Language::getIdentityString(func.getFunction())
)
@@ -259,8 +254,6 @@ module InstructionConsistency {
Operand useOperand, string message, IRFunction func, string funcText
) {
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
not useOperand.getUse() instanceof UnmodeledUseInstruction and
not defInstr instanceof UnmodeledDefinitionInstruction and
pointOfEvaluation(useOperand, useBlock, useIndex) and
defInstr = useOperand.getAnyDef() and
(
@@ -309,8 +302,6 @@ module InstructionConsistency {
private predicate shouldBeConflated(Instruction instr) {
isOnAliasedDefinitionChain(instr)
or
instr instanceof UnmodeledDefinitionInstruction
or
instr.getOpcode() instanceof Opcode::InitializeNonLocal
}

View File

@@ -40,16 +40,6 @@ class IRFunction extends TIRFunction {
result.getEnclosingIRFunction() = this
}
pragma[noinline]
final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() {
result.getEnclosingIRFunction() = this
}
pragma[noinline]
final UnmodeledUseInstruction getUnmodeledUseInstruction() {
result.getEnclosingIRFunction() = this
}
/**
* Gets the single return instruction for this function.
*/

View File

@@ -223,6 +223,15 @@ class IREllipsisVariable extends IRTempVariable {
final override string toString() { result = "#ellipsis" }
}
/**
* A temporary variable generated to hold the `this` pointer.
*/
class IRThisVariable extends IRTempVariable {
IRThisVariable() { tag = ThisTempVar() }
final override string toString() { result = "#this" }
}
/**
* A variable generated to represent the contents of a string literal. This variable acts much like
* a read-only global variable.

View File

@@ -320,8 +320,7 @@ class Instruction extends Construction::TInstruction {
/**
* Holds if the result of this instruction is precisely modeled in SSA. Always
* holds for a register result. For a memory result, a modeled result is
* connected to its actual uses. An unmodeled result is connected to the
* `UnmodeledUse` instruction.
* connected to its actual uses. An unmodeled result has no uses.
*
* For example:
* ```
@@ -1230,10 +1229,6 @@ class CatchAnyInstruction extends CatchInstruction {
CatchAnyInstruction() { getOpcode() instanceof Opcode::CatchAny }
}
class UnmodeledDefinitionInstruction extends Instruction {
UnmodeledDefinitionInstruction() { getOpcode() instanceof Opcode::UnmodeledDefinition }
}
/**
* An instruction that initializes all escaped memory.
*/
@@ -1248,12 +1243,6 @@ class AliasedUseInstruction extends Instruction {
AliasedUseInstruction() { getOpcode() instanceof Opcode::AliasedUse }
}
class UnmodeledUseInstruction extends Instruction {
UnmodeledUseInstruction() { getOpcode() instanceof Opcode::UnmodeledUse }
override string getOperandsString() { result = "mu*" }
}
/**
* An instruction representing the choice of one of multiple input values based on control flow.
*

View File

@@ -14,16 +14,8 @@ private newtype TOperand =
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) and
(
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
or
tag instanceof UnmodeledUseOperandTag
)
TNonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
useInstr.getOpcode().hasOperand(tag)
} or
TPhiOperand(
PhiInstruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
@@ -31,6 +23,57 @@ private newtype TOperand =
defInstr = Construction::getPhiOperandDefinition(useInstr, predecessorBlock, overlap)
}
/**
* Base class for all register operands. This is a placeholder for the IPA union type that we will
* eventually use for this purpose.
*/
private class RegisterOperandBase extends TRegisterOperand {
/** Gets a textual representation of this element. */
abstract string toString();
}
/**
* Returns the register operand with the specified parameters.
*/
private RegisterOperandBase registerOperand(
Instruction useInstr, RegisterOperandTag tag, Instruction defInstr
) {
result = TRegisterOperand(useInstr, tag, defInstr)
}
/**
* Base class for all non-Phi memory operands. This is a placeholder for the IPA union type that we
* will eventually use for this purpose.
*/
private class NonPhiMemoryOperandBase extends TNonPhiMemoryOperand {
/** Gets a textual representation of this element. */
abstract string toString();
}
/**
* Returns the non-Phi memory operand with the specified parameters.
*/
private NonPhiMemoryOperandBase nonPhiMemoryOperand(Instruction useInstr, MemoryOperandTag tag) {
result = TNonPhiMemoryOperand(useInstr, tag)
}
/**
* Base class for all Phi operands. This is a placeholder for the IPA union type that we will
* eventually use for this purpose.
*/
private class PhiOperandBase extends TPhiOperand {
abstract string toString();
}
/**
* Returns the Phi operand with the specified parameters.
*/
private PhiOperandBase phiOperand(
Instruction useInstr, Instruction defInstr, IRBlock predecessorBlock, Overlap overlap
) {
result = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap)
}
/**
* A source operand of an `Instruction`. The operand represents a value consumed by the instruction.
*/
@@ -104,7 +147,17 @@ class Operand extends TOperand {
* For example: `this:r3_5`
*/
final string getDumpString() {
result = getDumpLabel() + getInexactSpecifier() + getAnyDef().getResultId()
result = getDumpLabel() + getInexactSpecifier() + getDefinitionId()
}
/**
* Gets a string containing the identifier of the definition of this use, or `m?` if the
* definition is not modeled in SSA.
*/
private string getDefinitionId() {
result = getAnyDef().getResultId()
or
not exists(getAnyDef()) and result = "m?"
}
/**
@@ -169,8 +222,8 @@ class Operand extends TOperand {
*/
class MemoryOperand extends Operand {
MemoryOperand() {
this = TNonPhiMemoryOperand(_, _, _, _) or
this = TPhiOperand(_, _, _, _)
this instanceof NonPhiMemoryOperandBase or
this instanceof PhiOperandBase
}
/**
@@ -204,18 +257,15 @@ class MemoryOperand extends Operand {
*/
class NonPhiOperand extends Operand {
Instruction useInstr;
Instruction defInstr;
OperandTag tag;
NonPhiOperand() {
this = TRegisterOperand(useInstr, tag, defInstr) or
this = TNonPhiMemoryOperand(useInstr, tag, defInstr, _)
this = registerOperand(useInstr, tag, _) or
this = nonPhiMemoryOperand(useInstr, tag)
}
final override Instruction getUse() { result = useInstr }
final override Instruction getAnyDef() { result = defInstr }
final override string getDumpLabel() { result = tag.getLabel() }
final override int getDumpSortOrder() { result = tag.getSortOrder() }
@@ -226,8 +276,15 @@ class NonPhiOperand extends Operand {
/**
* An operand that consumes a register (non-memory) result.
*/
class RegisterOperand extends NonPhiOperand, TRegisterOperand {
class RegisterOperand extends NonPhiOperand, RegisterOperandBase {
override RegisterOperandTag tag;
Instruction defInstr;
RegisterOperand() { this = registerOperand(useInstr, tag, defInstr) }
final override string toString() { result = tag.toString() }
final override Instruction getAnyDef() { result = defInstr }
final override Overlap getDefinitionOverlap() {
// All register results overlap exactly with their uses.
@@ -235,13 +292,25 @@ class RegisterOperand extends NonPhiOperand, TRegisterOperand {
}
}
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, TNonPhiMemoryOperand {
class NonPhiMemoryOperand extends NonPhiOperand, MemoryOperand, NonPhiMemoryOperandBase {
override MemoryOperandTag tag;
Overlap overlap;
NonPhiMemoryOperand() { this = TNonPhiMemoryOperand(useInstr, tag, defInstr, overlap) }
NonPhiMemoryOperand() { this = nonPhiMemoryOperand(useInstr, tag) }
final override Overlap getDefinitionOverlap() { result = overlap }
final override string toString() { result = tag.toString() }
final override Instruction getAnyDef() {
result = unique(Instruction defInstr | hasDefinition(defInstr, _))
}
final override Overlap getDefinitionOverlap() { hasDefinition(_, result) }
pragma[noinline]
private predicate hasDefinition(Instruction defInstr, Overlap overlap) {
defInstr = Construction::getMemoryOperandDefinition(useInstr, tag, overlap) and
not Construction::isInCycle(useInstr) and
strictcount(Construction::getMemoryOperandDefinition(useInstr, tag, _)) = 1
}
}
class TypedOperand extends NonPhiMemoryOperand {
@@ -258,8 +327,6 @@ class TypedOperand extends NonPhiMemoryOperand {
*/
class AddressOperand extends RegisterOperand {
override AddressOperandTag tag;
override string toString() { result = "Address" }
}
/**
@@ -268,8 +335,6 @@ class AddressOperand extends RegisterOperand {
*/
class BufferSizeOperand extends RegisterOperand {
override BufferSizeOperandTag tag;
override string toString() { result = "BufferSize" }
}
/**
@@ -278,8 +343,6 @@ class BufferSizeOperand extends RegisterOperand {
*/
class LoadOperand extends TypedOperand {
override LoadOperandTag tag;
override string toString() { result = "Load" }
}
/**
@@ -287,8 +350,6 @@ class LoadOperand extends TypedOperand {
*/
class StoreValueOperand extends RegisterOperand {
override StoreValueOperandTag tag;
override string toString() { result = "StoreValue" }
}
/**
@@ -296,8 +357,6 @@ class StoreValueOperand extends RegisterOperand {
*/
class UnaryOperand extends RegisterOperand {
override UnaryOperandTag tag;
override string toString() { result = "Unary" }
}
/**
@@ -305,8 +364,6 @@ class UnaryOperand extends RegisterOperand {
*/
class LeftOperand extends RegisterOperand {
override LeftOperandTag tag;
override string toString() { result = "Left" }
}
/**
@@ -314,8 +371,6 @@ class LeftOperand extends RegisterOperand {
*/
class RightOperand extends RegisterOperand {
override RightOperandTag tag;
override string toString() { result = "Right" }
}
/**
@@ -323,18 +378,6 @@ class RightOperand extends RegisterOperand {
*/
class ConditionOperand extends RegisterOperand {
override ConditionOperandTag tag;
override string toString() { result = "Condition" }
}
/**
* An operand of the special `UnmodeledUse` instruction, representing a value
* whose set of uses is unknown.
*/
class UnmodeledUseOperand extends NonPhiMemoryOperand {
override UnmodeledUseOperandTag tag;
override string toString() { result = "UnmodeledUse" }
}
/**
@@ -342,8 +385,6 @@ class UnmodeledUseOperand extends NonPhiMemoryOperand {
*/
class CallTargetOperand extends RegisterOperand {
override CallTargetOperandTag tag;
override string toString() { result = "CallTarget" }
}
/**
@@ -361,8 +402,6 @@ class ArgumentOperand extends RegisterOperand {
*/
class ThisArgumentOperand extends ArgumentOperand {
override ThisArgumentOperandTag tag;
override string toString() { result = "ThisArgument" }
}
/**
@@ -370,34 +409,27 @@ class ThisArgumentOperand extends ArgumentOperand {
*/
class PositionalArgumentOperand extends ArgumentOperand {
override PositionalArgumentOperandTag tag;
int argIndex;
PositionalArgumentOperand() { argIndex = tag.getArgIndex() }
override string toString() { result = "Arg(" + argIndex + ")" }
/**
* Gets the zero-based index of the argument.
*/
final int getIndex() { result = argIndex }
final int getIndex() { result = tag.getArgIndex() }
}
class SideEffectOperand extends TypedOperand {
override SideEffectOperandTag tag;
override string toString() { result = "SideEffect" }
}
/**
* An operand of a `PhiInstruction`.
*/
class PhiInputOperand extends MemoryOperand, TPhiOperand {
class PhiInputOperand extends MemoryOperand, PhiOperandBase {
PhiInstruction useInstr;
Instruction defInstr;
IRBlock predecessorBlock;
Overlap overlap;
PhiInputOperand() { this = TPhiOperand(useInstr, defInstr, predecessorBlock, overlap) }
PhiInputOperand() { this = phiOperand(useInstr, defInstr, predecessorBlock, overlap) }
override string toString() { result = "Phi" }
@@ -427,8 +459,6 @@ class PhiInputOperand extends MemoryOperand, TPhiOperand {
class ChiTotalOperand extends NonPhiMemoryOperand {
override ChiTotalOperandTag tag;
override string toString() { result = "ChiTotal" }
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiTotalMemoryAccess }
}
@@ -438,7 +468,5 @@ class ChiTotalOperand extends NonPhiMemoryOperand {
class ChiPartialOperand extends NonPhiMemoryOperand {
override ChiPartialOperandTag tag;
override string toString() { result = "ChiPartial" }
final override MemoryAccessKind getMemoryAccess() { result instanceof ChiPartialMemoryAccess }
}

View File

@@ -35,6 +35,11 @@ private module Cached {
getTranslatedFunction(func).hasUserVariable(var, type)
}
cached
predicate hasThisVariable(Function func, CppType type) {
type = getTypeForGLValue(getTranslatedFunction(func).getThisType())
}
cached
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) {
exists(TranslatedElement element |
@@ -63,8 +68,6 @@ private module Cached {
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
instruction instanceof UnmodeledDefinitionInstruction
or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
@@ -91,17 +94,14 @@ private module Cached {
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
result =
getInstructionTranslatedElement(instruction)
.getInstructionOperand(getInstructionTag(instruction), tag)
.getInstructionRegisterOperand(getInstructionTag(instruction), tag)
}
cached
Instruction getMemoryOperandDefinition(
Instruction instruction, MemoryOperandTag tag, Overlap overlap
) {
result =
getInstructionTranslatedElement(instruction)
.getInstructionOperand(getInstructionTag(instruction), tag) and
overlap instanceof MustTotallyOverlap
none()
}
/** Gets a non-phi instruction that defines an operand of `instr`. */
@@ -144,12 +144,13 @@ private module Cached {
CppType getInstructionOperandType(Instruction instruction, TypedOperandTag tag) {
// For all `LoadInstruction`s, the operand type of the `LoadOperand` is the same as
// the result type of the load.
tag instanceof LoadOperandTag and
result = instruction.(LoadInstruction).getResultLanguageType()
or
not instruction instanceof LoadInstruction and
result =
getInstructionTranslatedElement(instruction)
.getInstructionOperandType(getInstructionTag(instruction), tag)
.getInstructionMemoryOperandType(getInstructionTag(instruction), tag)
}
cached

Some files were not shown because too many files have changed in this diff Show More