mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge branch 'master' into js/membershiptest
This commit is contained in:
9
.devcontainer/devcontainer.json
Normal file
9
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"extensions": [
|
||||
"github.vscode-codeql",
|
||||
"slevesque.vscode-zipexplorer"
|
||||
],
|
||||
"settings": {
|
||||
"codeQL.experimentalBqrsParsing": true
|
||||
}
|
||||
}
|
||||
@@ -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?
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ The following changes in version 1.25 affect C/C++ analysis in all applications.
|
||||
|
||||
## 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
|
||||
@@ -39,3 +40,5 @@ The following changes in version 1.25 affect C/C++ analysis in all applications.
|
||||
}
|
||||
};
|
||||
```
|
||||
* 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.
|
||||
|
||||
@@ -18,10 +18,13 @@ 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
|
||||
|
||||
@@ -3,12 +3,25 @@
|
||||
## 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.
|
||||
@@ -20,24 +33,54 @@
|
||||
| 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** |
|
||||
|--------------------------------|------------------------------|---------------------------------------------------------------------------|
|
||||
| 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. |
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. |
|
||||
| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | Less results | This query now recognizes additional safe patterns of doing URL redirects. |
|
||||
| Client-side cross-site scripting (`js/xss`) | Less results | This query now recognizes additional safe strings based on URLs. |
|
||||
| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. |
|
||||
| Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| 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. |
|
||||
| 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".
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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))
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1 +1,7 @@
|
||||
/**
|
||||
* DEPRECATED: use `import cpp` instead of `import default`.
|
||||
*
|
||||
* Provides classes and predicates for working with C/C++ code.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
65
cpp/ql/src/external/CodeDuplication.qll
vendored
65
cpp/ql/src/external/CodeDuplication.qll
vendored
@@ -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)
|
||||
}
|
||||
|
||||
21
cpp/ql/src/external/DefectFilter.qll
vendored
21
cpp/ql/src/external/DefectFilter.qll
vendored
@@ -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() + ":" +
|
||||
|
||||
35
cpp/ql/src/external/ExternalArtifact.qll
vendored
35
cpp/ql/src/external/ExternalArtifact.qll
vendored
@@ -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) }
|
||||
}
|
||||
|
||||
29
cpp/ql/src/external/MetricFilter.qll
vendored
29
cpp/ql/src/external/MetricFilter.qll
vendored
@@ -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() + ":" +
|
||||
|
||||
92
cpp/ql/src/external/VCS.qll
vendored
92
cpp/ql/src/external/VCS.qll
vendored
@@ -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 }
|
||||
20
cpp/ql/src/external/tests/DefectFromSVN.ql
vendored
20
cpp/ql/src/external/tests/DefectFromSVN.ql
vendored
@@ -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."
|
||||
17
cpp/ql/src/external/tests/MetricFromSVN.ql
vendored
17
cpp/ql/src/external/tests/MetricFromSVN.ql
vendored
@@ -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
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
@@ -1 +1,7 @@
|
||||
/**
|
||||
* DEPRECATED: Objective C is no longer supported.
|
||||
*
|
||||
* Import `cpp` instead of `objc`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -98,7 +98,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.
|
||||
|
||||
@@ -261,18 +261,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 +304,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 +321,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 +335,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
|
||||
|
||||
@@ -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)" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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") }
|
||||
|
||||
@@ -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() }
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides predicates for detecting whether an expression dereferences a pointer.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import Nullness
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with null values and checks for nullness.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import DefinitionsAndUses
|
||||
|
||||
|
||||
@@ -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`. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
/**
|
||||
* Provides a library for working with local (intra-procedural) control-flow
|
||||
* reachability involving stack variables.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/**
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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" }
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -170,11 +170,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) {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -162,11 +160,7 @@ class ExprNode extends InstructionNode {
|
||||
* as `x` in `f(x)` and implicit parameters such as `this` in `x.f()`
|
||||
*/
|
||||
class ParameterNode extends InstructionNode {
|
||||
ParameterNode() {
|
||||
instr instanceof InitializeParameterInstruction
|
||||
or
|
||||
instr instanceof InitializeThisInstruction
|
||||
}
|
||||
override InitializeParameterInstruction instr;
|
||||
|
||||
/**
|
||||
* Holds if this node is the parameter of `c` at the specified (zero-based)
|
||||
@@ -180,7 +174,7 @@ class ParameterNode extends InstructionNode {
|
||||
* flow graph.
|
||||
*/
|
||||
private class ExplicitParameterNode extends ParameterNode {
|
||||
override InitializeParameterInstruction instr;
|
||||
ExplicitParameterNode() { exists(instr.getParameter()) }
|
||||
|
||||
override predicate isParameterOf(Function f, int i) { f.getParameter(i) = instr.getParameter() }
|
||||
|
||||
@@ -191,7 +185,7 @@ private class ExplicitParameterNode extends ParameterNode {
|
||||
}
|
||||
|
||||
private class ThisParameterNode extends ParameterNode {
|
||||
override InitializeThisInstruction instr;
|
||||
ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable }
|
||||
|
||||
override predicate isParameterOf(Function f, int i) {
|
||||
i = -1 and instr.getEnclosingFunction() = f
|
||||
@@ -251,14 +245,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()
|
||||
)
|
||||
}
|
||||
@@ -268,6 +265,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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -278,15 +279,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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -458,9 +462,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;
|
||||
@@ -470,14 +474,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.
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
|
||||
@@ -60,7 +60,6 @@ private newtype TOpcode =
|
||||
TThrowValue() or
|
||||
TReThrow() or
|
||||
TUnwind() or
|
||||
TUnmodeledDefinition() or
|
||||
TAliasedDefinition() or
|
||||
TInitializeNonLocal() or
|
||||
TAliasedUse() or
|
||||
@@ -578,14 +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 AliasedDefinition extends Opcode, TAliasedDefinition {
|
||||
final override string toString() { result = "AliasedDefinition" }
|
||||
|
||||
|
||||
@@ -149,8 +149,7 @@ 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
|
||||
@@ -159,9 +158,7 @@ module InstructionConsistency {
|
||||
operand = instr.getAnOperand() 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())
|
||||
)
|
||||
@@ -257,7 +254,6 @@ module InstructionConsistency {
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
not defInstr instanceof UnmodeledDefinitionInstruction and
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
@@ -306,8 +302,6 @@ module InstructionConsistency {
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,6 @@ class IRFunction extends TIRFunction {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single return instruction for this function.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1229,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.
|
||||
*/
|
||||
|
||||
@@ -147,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?"
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() }
|
||||
|
||||
|
||||
@@ -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,14 +125,7 @@ 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
|
||||
)
|
||||
)
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = Chi(getOldInstruction(result)) and
|
||||
@@ -922,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
|
||||
) {
|
||||
@@ -934,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
|
||||
) {
|
||||
@@ -942,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(), ", "
|
||||
) + ")."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,8 +149,7 @@ 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
|
||||
@@ -159,9 +158,7 @@ module InstructionConsistency {
|
||||
operand = instr.getAnOperand() 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())
|
||||
)
|
||||
@@ -257,7 +254,6 @@ module InstructionConsistency {
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
not defInstr instanceof UnmodeledDefinitionInstruction and
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
@@ -306,8 +302,6 @@ module InstructionConsistency {
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,6 @@ class IRFunction extends TIRFunction {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single return instruction for this function.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1229,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.
|
||||
*/
|
||||
|
||||
@@ -147,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?"
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
@@ -98,17 +101,6 @@ private module Cached {
|
||||
Instruction getMemoryOperandDefinition(
|
||||
Instruction instruction, MemoryOperandTag tag, Overlap overlap
|
||||
) {
|
||||
exists(TranslatedElement translatedElement, TranslatedFunction translatedFunc |
|
||||
translatedElement = getInstructionTranslatedElement(instruction) and
|
||||
exists(getInstructionOperandType(instruction, tag)) and
|
||||
translatedFunc = getTranslatedFunction(instruction.getEnclosingFunction()) and
|
||||
result = translatedFunc.getUnmodeledDefinitionInstruction() and
|
||||
overlap instanceof MustTotallyOverlap
|
||||
)
|
||||
or
|
||||
// Without the code below, the optimizer will realize that raw IR never contains Chi operands,
|
||||
// and report an error that `ChiTotalOperand` and `ChiPartialOperand` are infeasible.
|
||||
(tag instanceof ChiTotalOperandTag or tag instanceof ChiPartialOperandTag) and
|
||||
none()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ private import cpp
|
||||
|
||||
newtype TInstructionTag =
|
||||
OnlyInstructionTag() or // Single instruction (not including implicit Load)
|
||||
InitializeThisTag() or
|
||||
InitializerVariableAddressTag() or
|
||||
InitializerLoadStringTag() or
|
||||
InitializerStoreTag() or
|
||||
@@ -28,7 +27,6 @@ newtype TInstructionTag =
|
||||
ReturnValueAddressTag() or
|
||||
ReturnTag() or
|
||||
ExitFunctionTag() or
|
||||
UnmodeledDefinitionTag() or
|
||||
AliasedDefinitionTag() or
|
||||
InitializeNonLocalTag() or
|
||||
AliasedUseTag() or
|
||||
@@ -71,7 +69,9 @@ newtype TInstructionTag =
|
||||
VarArgsMoveNextTag() or
|
||||
VarArgsVAListStoreTag() or
|
||||
AsmTag() or
|
||||
AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) }
|
||||
AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) } or
|
||||
ThisAddressTag() or
|
||||
ThisLoadTag()
|
||||
|
||||
class InstructionTag extends TInstructionTag {
|
||||
final string toString() { result = "Tag" }
|
||||
@@ -126,8 +126,6 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
or
|
||||
tag = ExitFunctionTag() and result = "ExitFunc"
|
||||
or
|
||||
tag = UnmodeledDefinitionTag() and result = "UnmodeledDef"
|
||||
or
|
||||
tag = AliasedDefinitionTag() and result = "AliasedDef"
|
||||
or
|
||||
tag = InitializeNonLocalTag() and result = "InitNonLocal"
|
||||
|
||||
@@ -108,11 +108,6 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
result = getArgument(argTag.getArgIndex()).getResult()
|
||||
)
|
||||
)
|
||||
or
|
||||
tag = CallSideEffectTag() and
|
||||
hasSideEffect() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
}
|
||||
|
||||
final override CppType getInstructionMemoryOperandType(
|
||||
|
||||
@@ -400,6 +400,9 @@ newtype TTranslatedElement =
|
||||
TTranslatedConstructorInitList(Function func) { translateFunction(func) } or
|
||||
// A destructor destruction list
|
||||
TTranslatedDestructorDestructionList(Function func) { translateFunction(func) } or
|
||||
TTranslatedThisParameter(Function func) {
|
||||
translateFunction(func) and func.isMember() and not func.isStatic()
|
||||
} or
|
||||
// A function parameter
|
||||
TTranslatedParameter(Parameter param) {
|
||||
exists(Function func |
|
||||
|
||||
@@ -664,31 +664,40 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr {
|
||||
final override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::CopyValue and
|
||||
tag = ThisAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getTypeForGLValue(any(UnknownType t))
|
||||
or
|
||||
tag = ThisLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getResultType()
|
||||
}
|
||||
|
||||
final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
|
||||
final override Instruction getResult() { result = getInstruction(ThisLoadTag()) }
|
||||
|
||||
final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
final override Instruction getFirstInstruction() { result = getInstruction(ThisAddressTag()) }
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
tag = OnlyInstructionTag() and
|
||||
tag = ThisAddressTag() and
|
||||
result = getInstruction(ThisLoadTag())
|
||||
or
|
||||
kind instanceof GotoEdge and
|
||||
tag = ThisLoadTag() and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
|
||||
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getInitializeThisInstruction()
|
||||
tag = ThisLoadTag() and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ThisAddressTag())
|
||||
}
|
||||
|
||||
private Instruction getInitializeThisInstruction() {
|
||||
result = getTranslatedFunction(expr.getEnclosingFunction()).getInitializeThisInstruction()
|
||||
override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = ThisAddressTag() and
|
||||
result = this.getEnclosingFunction().getThisVariable()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1689,7 +1698,8 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
|
||||
else
|
||||
if index = 1 and expr.hasAlignedAllocation()
|
||||
then result = getTranslatedExpr(expr.getAlignmentArgument())
|
||||
else result = getTranslatedExpr(expr.getAllocatorCall().getArgument(index))
|
||||
else
|
||||
result = getTranslatedExpr(expr.getAllocatorCall().getArgument(index).getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,15 +73,15 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
final override Function getFunction() { result = func }
|
||||
|
||||
final override TranslatedElement getChild(int id) {
|
||||
id = -4 and result = getReadEffects()
|
||||
id = -5 and result = getReadEffects()
|
||||
or
|
||||
id = -3 and result = getConstructorInitList()
|
||||
id = -4 and result = getConstructorInitList()
|
||||
or
|
||||
id = -2 and result = getBody()
|
||||
id = -3 and result = getBody()
|
||||
or
|
||||
id = -1 and result = getDestructorDestructionList()
|
||||
id = -2 and result = getDestructorDestructionList()
|
||||
or
|
||||
id >= 0 and result = getParameter(id)
|
||||
id >= -1 and result = getParameter(id)
|
||||
}
|
||||
|
||||
final private TranslatedConstructorInitList getConstructorInitList() {
|
||||
@@ -97,6 +97,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
final private TranslatedReadEffects getReadEffects() { result = getTranslatedReadEffects(func) }
|
||||
|
||||
final private TranslatedParameter getParameter(int index) {
|
||||
result = getTranslatedThisParameter(func) and
|
||||
index = -1
|
||||
or
|
||||
result = getTranslatedParameter(func.getParameter(index))
|
||||
or
|
||||
index = getEllipsisParameterIndexForFunction(func) and
|
||||
@@ -114,26 +117,16 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
tag = AliasedDefinitionTag() and
|
||||
result = getInstruction(InitializeNonLocalTag())
|
||||
or
|
||||
tag = InitializeNonLocalTag() and
|
||||
result = getInstruction(UnmodeledDefinitionTag())
|
||||
or
|
||||
(
|
||||
tag = UnmodeledDefinitionTag() and
|
||||
tag = InitializeNonLocalTag() and
|
||||
if exists(getThisType())
|
||||
then result = getInstruction(InitializeThisTag())
|
||||
then result = getParameter(-1).getFirstInstruction()
|
||||
else
|
||||
if exists(getParameter(0))
|
||||
then result = getParameter(0).getFirstInstruction()
|
||||
else result = getBody().getFirstInstruction()
|
||||
)
|
||||
or
|
||||
(
|
||||
tag = InitializeThisTag() and
|
||||
if exists(getParameter(0))
|
||||
then result = getParameter(0).getFirstInstruction()
|
||||
else result = getConstructorInitList().getFirstInstruction()
|
||||
)
|
||||
or
|
||||
tag = ReturnValueAddressTag() and
|
||||
result = getInstruction(ReturnTag())
|
||||
or
|
||||
@@ -179,10 +172,6 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
opcode instanceof Opcode::EnterFunction and
|
||||
resultType = getVoidType()
|
||||
or
|
||||
tag = UnmodeledDefinitionTag() and
|
||||
opcode instanceof Opcode::UnmodeledDefinition and
|
||||
resultType = getUnknownType()
|
||||
or
|
||||
tag = AliasedDefinitionTag() and
|
||||
opcode instanceof Opcode::AliasedDefinition and
|
||||
resultType = getUnknownType()
|
||||
@@ -191,10 +180,6 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
opcode instanceof Opcode::InitializeNonLocal and
|
||||
resultType = getUnknownType()
|
||||
or
|
||||
tag = InitializeThisTag() and
|
||||
opcode instanceof Opcode::InitializeThis and
|
||||
resultType = getTypeForGLValue(getThisType())
|
||||
or
|
||||
tag = ReturnValueAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getTypeForGLValue(getReturnType()) and
|
||||
@@ -235,10 +220,8 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = ReturnTag() and
|
||||
hasReturnValue() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ReturnValueAddressTag())
|
||||
)
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(ReturnValueAddressTag())
|
||||
}
|
||||
|
||||
final override CppType getInstructionMemoryOperandType(
|
||||
@@ -271,6 +254,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
tag = EllipsisTempVar() and
|
||||
func.isVarargs() and
|
||||
type = getEllipsisVariablePRValueType()
|
||||
or
|
||||
tag = ThisTempVar() and
|
||||
type = getTypeForGLValue(getThisType())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,23 +279,23 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
*/
|
||||
final IREllipsisVariable getEllipsisVariable() { result.getEnclosingFunction() = func }
|
||||
|
||||
/**
|
||||
* Gets the variable that represents the `this` pointer for this function, if any.
|
||||
*/
|
||||
final IRThisVariable getThisVariable() { result = getIRTempVariable(func, ThisTempVar()) }
|
||||
|
||||
/**
|
||||
* Holds if the function has a non-`void` return type.
|
||||
*/
|
||||
final predicate hasReturnValue() { hasReturnValue(func) }
|
||||
|
||||
/**
|
||||
* Gets the single `UnmodeledDefinition` instruction for this function.
|
||||
*/
|
||||
final Instruction getUnmodeledDefinitionInstruction() {
|
||||
result = getInstruction(UnmodeledDefinitionTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single `InitializeThis` instruction for this function. Holds only
|
||||
* if the function is an instance member function, constructor, or destructor.
|
||||
*/
|
||||
final Instruction getInitializeThisInstruction() { result = getInstruction(InitializeThisTag()) }
|
||||
final Instruction getInitializeThisInstruction() {
|
||||
result = getTranslatedThisParameter(func).getInstruction(InitializerStoreTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type pointed to by the `this` pointer for this function (i.e. `*this`).
|
||||
@@ -350,6 +336,11 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
final Type getReturnType() { result = func.getType() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedThisParameter` for function `func`, if one exists.
|
||||
*/
|
||||
TranslatedThisParameter getTranslatedThisParameter(Function func) { result.getFunction() = func }
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedPositionalParameter` that represents parameter `param`.
|
||||
*/
|
||||
@@ -364,8 +355,9 @@ TranslatedEllipsisParameter getTranslatedEllipsisParameter(Function func) {
|
||||
|
||||
/**
|
||||
* The IR translation of a parameter to a function. This can be either a user-declared parameter
|
||||
* (`TranslatedPositionParameter`) or the synthesized parameter used to represent a `...` in a
|
||||
* varargs function (`TranslatedEllipsisParameter`).
|
||||
* (`TranslatedPositionParameter`), the synthesized parameter used to represent `this`, or the
|
||||
* synthesized parameter used to represent a `...` in a varargs function
|
||||
* (`TranslatedEllipsisParameter`).
|
||||
*/
|
||||
abstract class TranslatedParameter extends TranslatedElement {
|
||||
final override TranslatedElement getChild(int id) { none() }
|
||||
@@ -412,7 +404,7 @@ abstract class TranslatedParameter extends TranslatedElement {
|
||||
hasIndirection() and
|
||||
tag = InitializerIndirectStoreTag() and
|
||||
opcode instanceof Opcode::InitializeIndirection and
|
||||
resultType = getUnknownType()
|
||||
resultType = getInitializationResultType()
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
@@ -449,9 +441,43 @@ abstract class TranslatedParameter extends TranslatedElement {
|
||||
|
||||
abstract CppType getPRValueType();
|
||||
|
||||
abstract CppType getInitializationResultType();
|
||||
|
||||
abstract IRAutomaticVariable getIRVariable();
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the synthesized parameter used to represent the `...` in a varargs
|
||||
* function.
|
||||
*/
|
||||
class TranslatedThisParameter extends TranslatedParameter, TTranslatedThisParameter {
|
||||
Function func;
|
||||
|
||||
TranslatedThisParameter() { this = TTranslatedThisParameter(func) }
|
||||
|
||||
final override string toString() { result = "this" }
|
||||
|
||||
final override Locatable getAST() { result = func }
|
||||
|
||||
final override Function getFunction() { result = func }
|
||||
|
||||
final override predicate hasIndirection() { any() }
|
||||
|
||||
final override CppType getGLValueType() { result = getTypeForGLValue(any(UnknownType t)) }
|
||||
|
||||
final override CppType getPRValueType() {
|
||||
result = getTypeForGLValue(getTranslatedFunction(func).getThisType())
|
||||
}
|
||||
|
||||
final override CppType getInitializationResultType() {
|
||||
result = getTypeForPRValue(getTranslatedFunction(func).getThisType())
|
||||
}
|
||||
|
||||
final override IRThisVariable getIRVariable() {
|
||||
result = getTranslatedFunction(func).getThisVariable()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a function parameter, including the
|
||||
* initialization of that parameter with the incoming argument.
|
||||
@@ -482,6 +508,8 @@ class TranslatedPositionalParameter extends TranslatedParameter, TTranslatedPara
|
||||
|
||||
final override CppType getPRValueType() { result = getTypeForPRValue(getVariableType(param)) }
|
||||
|
||||
final override CppType getInitializationResultType() { result = getUnknownType() }
|
||||
|
||||
final override IRAutomaticUserVariable getIRVariable() {
|
||||
result = getIRUserVariable(getFunction(), param)
|
||||
}
|
||||
@@ -508,6 +536,8 @@ class TranslatedEllipsisParameter extends TranslatedParameter, TTranslatedEllips
|
||||
|
||||
final override CppType getPRValueType() { result = getEllipsisVariablePRValueType() }
|
||||
|
||||
final override CppType getInitializationResultType() { result = getUnknownType() }
|
||||
|
||||
final override IREllipsisVariable getIRVariable() {
|
||||
result = getTranslatedFunction(func).getEllipsisVariable()
|
||||
}
|
||||
|
||||
@@ -149,8 +149,7 @@ 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
|
||||
@@ -159,9 +158,7 @@ module InstructionConsistency {
|
||||
operand = instr.getAnOperand() 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())
|
||||
)
|
||||
@@ -257,7 +254,6 @@ module InstructionConsistency {
|
||||
Operand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(IRBlock useBlock, int useIndex, Instruction defInstr, IRBlock defBlock, int defIndex |
|
||||
not defInstr instanceof UnmodeledDefinitionInstruction and
|
||||
pointOfEvaluation(useOperand, useBlock, useIndex) and
|
||||
defInstr = useOperand.getAnyDef() and
|
||||
(
|
||||
@@ -306,8 +302,6 @@ module InstructionConsistency {
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
|
||||
@@ -40,11 +40,6 @@ class IRFunction extends TIRFunction {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
final UnmodeledDefinitionInstruction getUnmodeledDefinitionInstruction() {
|
||||
result.getEnclosingIRFunction() = this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the single return instruction for this function.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1229,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.
|
||||
*/
|
||||
|
||||
@@ -147,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?"
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,14 +125,7 @@ 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
|
||||
)
|
||||
)
|
||||
hasMemoryOperandDefinition(oldInstruction, oldOperand, overlap, result)
|
||||
)
|
||||
or
|
||||
instruction = Chi(getOldInstruction(result)) and
|
||||
@@ -922,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
|
||||
) {
|
||||
@@ -934,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
|
||||
) {
|
||||
@@ -942,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(), ", "
|
||||
) + ")."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,7 +362,7 @@ CppType getTypeForPRValueOrUnknown(Type type) {
|
||||
/**
|
||||
* Gets the `CppType` that represents a glvalue of type `type`.
|
||||
*/
|
||||
CppType getTypeForGLValue(Type type) { result.hasType(type, true) }
|
||||
CppGLValueAddressType getTypeForGLValue(Type type) { result.hasType(type, true) }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a prvalue of type `int`.
|
||||
|
||||
@@ -3,7 +3,8 @@ newtype TTempVariableTag =
|
||||
ReturnValueTempVar() or
|
||||
ThrowTempVar() or
|
||||
LambdaTempVar() or
|
||||
EllipsisTempVar()
|
||||
EllipsisTempVar() or
|
||||
ThisTempVar()
|
||||
|
||||
string getTempVariableTagId(TTempVariableTag tag) {
|
||||
tag = ConditionValueTempVar() and result = "CondVal"
|
||||
@@ -15,4 +16,6 @@ string getTempVariableTagId(TTempVariableTag tag) {
|
||||
tag = LambdaTempVar() and result = "Lambda"
|
||||
or
|
||||
tag = EllipsisTempVar() and result = "Ellipsis"
|
||||
or
|
||||
tag = ThisTempVar() and result = "This"
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ class Printf extends FormattingFunction, AliasFunction {
|
||||
|
||||
override int getFormatParameterIndex() { result = 0 }
|
||||
|
||||
override predicate isWideCharDefault() {
|
||||
deprecated override predicate isWideCharDefault() {
|
||||
hasGlobalOrStdName("wprintf") or
|
||||
hasGlobalName("wprintf_s")
|
||||
}
|
||||
@@ -47,7 +47,7 @@ class Fprintf extends FormattingFunction {
|
||||
|
||||
override int getFormatParameterIndex() { result = 1 }
|
||||
|
||||
override predicate isWideCharDefault() { hasGlobalOrStdName("fwprintf") }
|
||||
deprecated override predicate isWideCharDefault() { hasGlobalOrStdName("fwprintf") }
|
||||
|
||||
override int getOutputParameterIndex() { result = 0 }
|
||||
}
|
||||
@@ -70,7 +70,7 @@ class Sprintf extends FormattingFunction {
|
||||
not exists(getDefinition().getFile().getRelativePath())
|
||||
}
|
||||
|
||||
override predicate isWideCharDefault() {
|
||||
deprecated override predicate isWideCharDefault() {
|
||||
getParameter(getFormatParameterIndex())
|
||||
.getType()
|
||||
.getUnspecifiedType()
|
||||
@@ -136,7 +136,7 @@ class Snprintf extends FormattingFunction {
|
||||
else result = getFirstFormatArgumentIndex() - 1
|
||||
}
|
||||
|
||||
override predicate isWideCharDefault() {
|
||||
deprecated override predicate isWideCharDefault() {
|
||||
getParameter(getFormatParameterIndex())
|
||||
.getType()
|
||||
.getUnspecifiedType()
|
||||
@@ -201,7 +201,7 @@ class StringCchPrintf extends FormattingFunction {
|
||||
if getName().matches("%Ex") then result = 5 else result = 2
|
||||
}
|
||||
|
||||
override predicate isWideCharDefault() {
|
||||
deprecated override predicate isWideCharDefault() {
|
||||
getParameter(getFormatParameterIndex())
|
||||
.getType()
|
||||
.getUnspecifiedType()
|
||||
|
||||
@@ -20,9 +20,7 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
|
||||
name = "strpbrk" or
|
||||
name = "strcmp" or
|
||||
name = "strcspn" or
|
||||
name = "strlen" or
|
||||
name = "strncmp" or
|
||||
name = "strnlen" or
|
||||
name = "strrchr" or
|
||||
name = "strspn" or
|
||||
name = "strtod" or
|
||||
@@ -30,16 +28,7 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
|
||||
name = "strtol" or
|
||||
name = "strtoll" or
|
||||
name = "strtoq" or
|
||||
name = "strtoul" or
|
||||
name = "wcslen"
|
||||
)
|
||||
or
|
||||
hasGlobalName(name) and
|
||||
(
|
||||
name = "_mbslen" or
|
||||
name = "_mbslen_l" or
|
||||
name = "_mbstrlen" or
|
||||
name = "_mbstrlen_l"
|
||||
name = "strtoul"
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -90,6 +79,52 @@ class PureStrFunction extends AliasFunction, ArrayFunction, TaintFunction, SideE
|
||||
}
|
||||
}
|
||||
|
||||
class StrLenFunction extends AliasFunction, ArrayFunction, SideEffectFunction {
|
||||
StrLenFunction() {
|
||||
exists(string name |
|
||||
hasGlobalOrStdName(name) and
|
||||
(
|
||||
name = "strlen" or
|
||||
name = "strnlen" or
|
||||
name = "wcslen"
|
||||
)
|
||||
or
|
||||
hasGlobalName(name) and
|
||||
(
|
||||
name = "_mbslen" or
|
||||
name = "_mbslen_l" or
|
||||
name = "_mbstrlen" or
|
||||
name = "_mbstrlen_l"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int bufParam) {
|
||||
getParameter(bufParam).getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) {
|
||||
getParameter(bufParam).getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int i) {
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int i) { none() }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int i) { none() }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
getParameter(i).getUnspecifiedType() instanceof PointerType and
|
||||
buffer = true
|
||||
}
|
||||
}
|
||||
|
||||
class PureFunction extends TaintFunction, SideEffectFunction {
|
||||
PureFunction() {
|
||||
exists(string name |
|
||||
|
||||
@@ -10,10 +10,7 @@ class Strftime extends TaintFunction, ArrayFunction {
|
||||
input.isParameterDeref(2) or
|
||||
input.isParameterDeref(3)
|
||||
) and
|
||||
(
|
||||
output.isParameterDeref(0) or
|
||||
output.isReturnValue()
|
||||
)
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam = 2 }
|
||||
|
||||
@@ -15,6 +15,9 @@ import semmle.code.cpp.models.Models
|
||||
* A library function for which a taint-tracking library should propagate taint
|
||||
* from a parameter or qualifier to an output buffer, return value, or qualifier.
|
||||
*
|
||||
* An expression is tainted if it could be influenced by an attacker to have
|
||||
* an unusual value.
|
||||
*
|
||||
* Note that this does not include direct copying of values; that is covered by
|
||||
* DataFlowModel.qll. If a value is sometimes copied in full, and sometimes
|
||||
* altered (for example copying a string with `strncpy`), this is also considered
|
||||
|
||||
@@ -278,8 +278,6 @@ private predicate unknownSign(Instruction i) {
|
||||
// non-positive, non-zero}, which would mean that the representation of the sign of an unknown
|
||||
// value would be the empty set.
|
||||
(
|
||||
i instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
i instanceof UninitializedInstruction
|
||||
or
|
||||
i instanceof InitializeParameterInstruction
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user