Merge branch 'master' into js/membershiptest

This commit is contained in:
Esben Sparre Andreasen
2020-06-02 08:54:44 +02:00
committed by GitHub
617 changed files with 16819 additions and 10196 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,12 @@
import cpp
import semmle.code.cpp.security.Security
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
private import semmle.code.cpp.ir.dataflow.DataFlow2
private import semmle.code.cpp.ir.dataflow.DataFlow3
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
private import semmle.code.cpp.controlflow.IRGuards
private import semmle.code.cpp.models.interfaces.Taint
private import semmle.code.cpp.models.interfaces.DataFlow
@@ -31,8 +33,7 @@ private predicate predictableInstruction(Instruction instr) {
* Note that the list itself is not very principled; it consists of all the
* functions listed in the old security library's [default] `isPureFunction`
* that have more than one argument, but are not in the old taint tracking
* library's `returnArgument` predicate. In addition, `strlen` is included
* because it's also a special case in flow to return values.
* library's `returnArgument` predicate.
*/
predicate predictableOnlyFlow(string name) {
name = "strcasestr" or
@@ -41,7 +42,6 @@ predicate predictableOnlyFlow(string name) {
name = "strchrnul" or
name = "strcmp" or
name = "strcspn" or
name = "strlen" or // special case
name = "strncmp" or
name = "strndup" or
name = "strnlen" or
@@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -71,9 +71,7 @@ class Node extends TIRDataFlowNode {
* `x.set(taint())` is a partial definition of `x`, and `transfer(&x, taint())` is
* a partial definition of `&x`).
*/
Expr asPartialDefinition() {
result = this.(PartialDefinitionNode).getInstruction().getUnconvertedResultExpression()
}
Expr asPartialDefinition() { result = this.(PartialDefinitionNode).getDefinedExpr() }
/**
* DEPRECATED: See UninitializedNode.
@@ -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.

View File

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

View File

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

View File

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

View File

@@ -60,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" }

View File

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

View File

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

View File

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

View File

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

View File

@@ -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?"
}
/**

View File

@@ -204,7 +204,7 @@ private predicate isArgumentForParameter(CallInstruction ci, Operand operand, In
init.(InitializeParameterInstruction).getParameter() =
f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
init instanceof InitializeThisInstruction and
init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and
init.getEnclosingFunction() = f and
operand instanceof ThisArgumentOperand
) and

View File

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

View File

@@ -67,8 +67,6 @@ private module Cached {
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
instruction instanceof UnmodeledDefinitionInstruction
or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
@@ -127,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(), ", "
) + ")."
)
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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?"
}
/**

View File

@@ -35,6 +35,11 @@ private module Cached {
getTranslatedFunction(func).hasUserVariable(var, type)
}
cached
predicate hasThisVariable(Function func, CppType type) {
type = getTypeForGLValue(getTranslatedFunction(func).getThisType())
}
cached
predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) {
exists(TranslatedElement element |
@@ -63,8 +68,6 @@ private module Cached {
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
instruction instanceof UnmodeledDefinitionInstruction
or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
@@ -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()
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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?"
}
/**

View File

@@ -204,7 +204,7 @@ private predicate isArgumentForParameter(CallInstruction ci, Operand operand, In
init.(InitializeParameterInstruction).getParameter() =
f.getParameter(operand.(PositionalArgumentOperand).getIndex())
or
init instanceof InitializeThisInstruction and
init.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable and
init.getEnclosingFunction() = f and
operand instanceof ThisArgumentOperand
) and

View File

@@ -67,8 +67,6 @@ private module Cached {
cached
predicate hasConflatedMemoryResult(Instruction instruction) {
instruction instanceof UnmodeledDefinitionInstruction
or
instruction instanceof AliasedDefinitionInstruction
or
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
@@ -127,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(), ", "
) + ")."
)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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