mirror of
https://github.com/github/codeql.git
synced 2026-07-20 18:58:36 +02:00
Merge branch 'master' into python-objectapi-to-valueapi-iterreturnsnonself
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -14,6 +14,9 @@
|
||||
.vs/*
|
||||
!.vs/VSWorkspaceSettings.json
|
||||
|
||||
# Byte-compiled python files
|
||||
*.pyc
|
||||
|
||||
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
|
||||
/codeql/
|
||||
.vscode/settings.json
|
||||
|
||||
@@ -1,62 +1,65 @@
|
||||
# Contributing to CodeQL
|
||||
|
||||
We welcome contributions to our standard library and standard checks. Got an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request!
|
||||
We welcome contributions to our CodeQL libraries and queries. Got an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request!
|
||||
|
||||
Before we accept your pull request, we require that you have agreed to our Contributor License Agreement, this is not something that you need to do before you submit your pull request, but until you've done so, we will be unable to accept your contribution.
|
||||
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [Writing CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||
|
||||
## Adding a new query
|
||||
|
||||
If you have an idea for a query that you would like to share with other Semmle users, please open a pull request to add it to this repository.
|
||||
Follow the steps below to help other users understand what your query does, and to ensure that your query is consistent with the other Semmle queries.
|
||||
## Submitting a new experimental query
|
||||
|
||||
1. **Consult the documentation for query writers**
|
||||
If you have an idea for a query that you would like to share with other CodeQL users, please open a pull request to add it to this repository. New queries start out in a `<language>/ql/src/experimental` directory, to which they can be merged when they meet the following requirements.
|
||||
|
||||
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [Writing CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||
1. **Directory structure**
|
||||
|
||||
2. **Format your code correctly**
|
||||
There are five language-specific query directories in this repository:
|
||||
|
||||
All of Semmle's standard queries and libraries are uniformly formatted for clarity and consistency, so we strongly recommend that all contributions follow the same formatting guidelines. If you use CodeQL for VS Code, you can autoformat your query in the [Editor](https://help.semmle.com/codeql/codeql-for-vscode/reference/editor.html#autoformatting). For more information, see the [CodeQL style guide](https://github.com/Semmle/ql/blob/master/docs/ql-style-guide.md).
|
||||
* C/C++: `cpp/ql/src`
|
||||
* C#: `csharp/ql/src`
|
||||
* Java: `java/ql/src`
|
||||
* JavaScript: `javascript/ql/src`
|
||||
* Python: `python/ql/src`
|
||||
|
||||
3. **Make sure your query has the correct metadata**
|
||||
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
|
||||
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/Semmle/ql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
|
||||
- The structure of an `experimental` subdirectory mirrors the structure of its parent directory.
|
||||
- Select or create an appropriate directory in `experimental` based on the existing directory structure of `experimental` or its parent directory.
|
||||
|
||||
Query metadata is used by Semmle's analysis to identify your query and make sure the query results are displayed properly.
|
||||
The most important metadata to include are the `@name`, `@description`, and the `@kind`.
|
||||
Other metadata properties (`@precision`, `@severity`, and `@tags`) are usually added after the query has been reviewed by Semmle staff.
|
||||
For more information on writing query metadata, see the [Query metadata style guide](https://github.com/Semmle/ql/blob/master/docs/query-metadata-style-guide.md).
|
||||
2. **Query metadata**
|
||||
|
||||
4. **Make sure the `select` statement is compatible with the query type**
|
||||
- The query `@id` must conform to all the requirements in the [guide on query metadata](docs/query-metadata-style-guide.md#query-id-id). In particular, it must not clash with any other queries in the repository, and it must start with the appropriate language-specific prefix.
|
||||
- The query must have a `@name` and `@description` to explain its purpose.
|
||||
- The query must have a `@kind` and `@problem.severity` as required by CodeQL tools.
|
||||
|
||||
The `select` statement of your query must be compatible with the query type (determined by the `@kind` metadata property) for alert or path results to be displayed correctly in LGTM and CodeQL for VS Code.
|
||||
For more information on `select` statement format, see [Introduction to query files](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
|
||||
For details, see the [guide on query metadata](docs/query-metadata-style-guide.md).
|
||||
|
||||
5. **Save your query in a `.ql` file in the correct language directory in this repository**
|
||||
Make sure the `select` statement is compatible with the query `@kind`. See [Introduction to query files](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
|
||||
|
||||
There are five language-specific directories in this repository:
|
||||
|
||||
* C/C++: `ql/cpp/ql/src`
|
||||
* C#: `ql/csharp/ql/src`
|
||||
* Java: `ql/java/ql/src`
|
||||
* JavaScript: `ql/javascript/ql/src`
|
||||
* Python: `ql/python/ql/src`
|
||||
3. **Formatting**
|
||||
|
||||
Each language-specific directory contains further subdirectories that group queries based on their `@tags` properties or purpose. Select the appropriate subdirectory for your new query, or create a new one if necessary.
|
||||
- The queries and libraries must be [autoformatted](https://help.semmle.com/codeql/codeql-for-vscode/reference/editor.html#autoformatting).
|
||||
|
||||
6. **Write a query help file**
|
||||
4. **Compilation**
|
||||
|
||||
Query help files explain the purpose of your query to other users. Write your query help in a `.qhelp` file and save it in the same directory as your new query.
|
||||
For more information on writing query help, see the [Query help style guide](https://github.com/Semmle/ql/blob/master/docs/query-help-style-guide.md).
|
||||
- Compilation of the query and any associated libraries and tests must be resilient to future development of the [supported](docs/supported-queries.md) libraries. This means that the functionality cannot use internal libraries, cannot depend on the output of `getAQlClass`, and cannot make use of regexp matching on `toString`.
|
||||
- The query and any associated libraries and tests must not cause any compiler warnings to be emitted (such as use of deprecated functionality or missing `override` annotations).
|
||||
|
||||
7. **Maintain backwards compatibility**
|
||||
5. **Results**
|
||||
|
||||
The standard CodeQL libraries must evolve in a backwards compatible manner. If any backwards incompatible changes need to be made, the existing API must first be marked as deprecated. This is done by adding a `deprecated` annotation along with a QLDoc reference to the replacement API. Only after at least one full release cycle has elapsed may the old API be removed.
|
||||
- The query must have at least one true positive result on some revision of a real project.
|
||||
|
||||
In addition to contributions to our standard queries and libraries, we also welcome contributions of a more experimental nature, which do not need to fulfill all the requirements listed above. See the guidelines for [experimental queries and libraries](docs/experimental.md) for details.
|
||||
6. **Contributor License Agreement**
|
||||
|
||||
- The contributor can satisfy the [CLA](#contributor-license-agreement).
|
||||
|
||||
Experimental queries and libraries may not be actively maintained as the [supported](docs/supported-queries.md) libraries evolve. They may also be changed in backwards-incompatible ways or may be removed entirely in the future without deprecation warnings.
|
||||
|
||||
After the experimental query is merged, we welcome pull requests to improve it. Before a query can be moved out of the `experimental` subdirectory, it must satisfy [the requirements for being a supported query](docs/supported-queries.md).
|
||||
|
||||
## Using your personal data
|
||||
|
||||
If you contribute to this project, we will record your name and email
|
||||
address (as provided by you with your contributions) as part of the code
|
||||
repositories, which might be made public. We might also use this information
|
||||
repositories, which are public. We might also use this information
|
||||
to contact you in relation to your contributions, as well as in the
|
||||
normal course of software development. We also store records of your
|
||||
CLA agreements. Under GDPR legislation, we do this
|
||||
|
||||
@@ -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 products that [Semmle](https://semmle.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.
|
||||
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
@@ -13,4 +13,4 @@ We welcome contributions to our standard library and standard checks. Do you hav
|
||||
|
||||
## License
|
||||
|
||||
The code in this repository is licensed under [Apache License 2.0](LICENSE) by [Semmle](https://semmle.com).
|
||||
The code in this repository is licensed under [Apache License 2.0](LICENSE) by [GitHub](https://github.com).
|
||||
|
||||
@@ -18,12 +18,15 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| Memory is never freed (`cpp/memory-never-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| Memory may not be freed (`cpp/memory-may-not-be-freed`) | More true positive results | This query now identifies a wider variety of buffer allocations using the `semmle.code.cpp.models.interfaces.Allocation` library. |
|
||||
| Mismatching new/free or malloc/delete (`cpp/new-free-mismatch`) | Fewer false positive results | Fixed false positive results in template code. |
|
||||
| Missing return statement (`cpp/missing-return`) | Fewer false positive results | Functions containing `asm` statements are no longer highlighted by this query. |
|
||||
| Missing return statement (`cpp/missing-return`) | More accurate locations | Locations reported by this query are now more accurate in some cases. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | More correct results | String arguments to formatting functions are now (usually) expected to be null terminated strings. |
|
||||
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | | This query is no longer run on LGTM. |
|
||||
| No space for zero terminator (`cpp/no-space-for-terminator`) | Fewer false positive results | This query has been modified to be more conservative when identifying which pointers point to null-terminated strings. This approach produces fewer, more accurate results. |
|
||||
| Overloaded assignment does not return 'this' (`cpp/assignment-does-not-return-this`) | Fewer false positive results | This query no longer reports incorrect results in template classes. |
|
||||
| Unsafe array for days of the year (`cpp/leap-year/unsafe-array-for-days-of-the-year`) | | This query is no longer run on LGTM. |
|
||||
| Unsigned comparison to zero (`cpp/unsigned-comparison-zero`) | More correct results | This query now also looks for comparisons of the form `0 <= x`. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
@@ -42,6 +45,7 @@ The following changes in version 1.24 affect C/C++ analysis in all applications.
|
||||
* The `LocalScopeVariableReachability` library is deprecated in favor of
|
||||
`StackVariableReachability`. The functionality is the same.
|
||||
* The models library models `strlen` in more detail, and includes common variations such as `wcslen`.
|
||||
* The models library models `gets` and similar functions.
|
||||
* The taint tracking library (`semmle.code.cpp.dataflow.TaintTracking`) has had
|
||||
the following improvements:
|
||||
* The library now models data flow through `strdup` and similar functions.
|
||||
|
||||
@@ -21,6 +21,7 @@ The following changes in version 1.24 affect C# analysis in all applications.
|
||||
| Potentially dangerous use of non-short-circuit logic (`cs/non-short-circuit`) | Fewer false positive results | Results have been removed when the expression contains an `out` parameter. |
|
||||
| Dereferenced variable may be null (`cs/dereferenced-value-may-be-null`) | More results | Results are reported from parameters with a default value of `null`. |
|
||||
| Useless assignment to local variable (`cs/useless-assignment-to-local`) | Fewer false positive results | Results have been removed when the value assigned is an (implicitly or explicitly) cast default-like value. For example, `var s = (string)null` and `string s = default`. |
|
||||
| XPath injection (`cs/xml/xpath-injection`) | More results | The query now recognizes calls to methods on `System.Xml.XPath.XPathNavigator` objects. |
|
||||
|
||||
## Removal of old queries
|
||||
|
||||
|
||||
@@ -6,16 +6,18 @@
|
||||
|
||||
* Alert suppression can now be done with single-line block comments (`/* ... */`) as well as line comments (`// ...`).
|
||||
|
||||
* Imports with the `.js` extension can now be resolved to a TypeScript file,
|
||||
* Resolution of imports has improved, leading to more results from the security queries:
|
||||
- Imports with the `.js` extension can now be resolved to a TypeScript file,
|
||||
when the import refers to a file generated by TypeScript.
|
||||
- Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
|
||||
- Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
|
||||
|
||||
* Imports that rely on path-mappings from a `tsconfig.json` file can now be resolved.
|
||||
* The analysis of sanitizers has improved, leading to more accurate results from the security queries.
|
||||
In particular:
|
||||
- Sanitizer guards now act across function boundaries in more cases.
|
||||
- Sanitizers can now better distinguish between a tainted value and an object _containing_ a tainted value.
|
||||
|
||||
* Export declarations of the form `export * as ns from "x"` are now analyzed more precisely.
|
||||
|
||||
* The analysis of sanitizer guards has improved, leading to fewer false-positive results from the security queries.
|
||||
|
||||
* The call graph construction has been improved, leading to more results from the security queries:
|
||||
* Call graph construction has been improved, leading to more results from the security queries:
|
||||
- Calls can now be resolved to indirectly-defined class members in more cases.
|
||||
- Calls through partial invocations such as `.bind` can now be resolved in more cases.
|
||||
|
||||
@@ -23,7 +25,9 @@
|
||||
|
||||
* Support for the following frameworks and libraries has been improved:
|
||||
- [Electron](https://electronjs.org/)
|
||||
- [fstream](https://www.npmjs.com/package/fstream)
|
||||
- [Handlebars](https://www.npmjs.com/package/handlebars)
|
||||
- [jsonfile](https://www.npmjs.com/package/jsonfile)
|
||||
- [Koa](https://www.npmjs.com/package/koa)
|
||||
- [Node.js](https://nodejs.org/)
|
||||
- [Socket.IO](https://socket.io/)
|
||||
@@ -32,11 +36,23 @@
|
||||
- [for-in](https://www.npmjs.com/package/for-in)
|
||||
- [for-own](https://www.npmjs.com/package/for-own)
|
||||
- [http2](https://nodejs.org/api/http2.html)
|
||||
- [jQuery](https://jquery.com/)
|
||||
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
|
||||
- [mongodb](https://www.npmjs.com/package/mongodb)
|
||||
- [ncp](https://www.npmjs.com/package/ncp)
|
||||
- [node-dir](https://www.npmjs.com/package/node-dir)
|
||||
- [path-exists](https://www.npmjs.com/package/path-exists)
|
||||
- [pg](https://www.npmjs.com/package/pg)
|
||||
- [react](https://www.npmjs.com/package/react)
|
||||
- [recursive-readdir](https://www.npmjs.com/package/recursive-readdir)
|
||||
- [request](https://www.npmjs.com/package/request)
|
||||
- [rimraf](https://www.npmjs.com/package/rimraf)
|
||||
- [send](https://www.npmjs.com/package/send)
|
||||
- [SockJS](https://www.npmjs.com/package/sockjs)
|
||||
- [SockJS-client](https://www.npmjs.com/package/sockjs-client)
|
||||
- [typeahead.js](https://www.npmjs.com/package/typeahead.js)
|
||||
- [vinyl-fs](https://www.npmjs.com/package/vinyl-fs)
|
||||
- [write-file-atomic](https://www.npmjs.com/package/write-file-atomic)
|
||||
- [ws](https://github.com/websockets/ws)
|
||||
|
||||
## New queries
|
||||
@@ -67,8 +83,16 @@
|
||||
| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional ways of constructing arguments to `cmd.exe` and `/bin/sh`. |
|
||||
| Syntax error (`js/syntax-error`) | Lower severity | This results of this query are now displayed with lower severity. |
|
||||
| Use of password hash with insufficient computational effort (`js/insufficient-password-hash`) | Fewer false positive results | This query now recognizes additional cases that do not require secure hashing. |
|
||||
| Useless regular-expression character escape (`js/useless-regexp-character-escape`) | Fewer false positive results | This query now distinguishes escapes in strings and regular expression literals. |
|
||||
| Identical operands (`js/redundant-operation`) | Fewer results | This query now recognizes cases where the operands change a value using ++/-- expressions. |
|
||||
| Superfluous trailing arguments (`js/superfluous-trailing-arguments`) | Fewer results | This query now recognizes cases where a function uses the `Function.arguments` value to process a variable number of parameters. |
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The predicates `RegExpTerm.getSuccessor` and `RegExpTerm.getPredecessor` have been changed to reflect textual, not operational, matching order. This only makes a difference in lookbehind assertions, which are operationally matched backwards. Previously, `getSuccessor` would mimick this, so in an assertion `(?<=ab)` the term `b` would be considered the predecessor, not the successor, of `a`. Textually, however, `a` is still matched before `b`, and this is the order we now follow.
|
||||
* An extensible model of the `EventEmitter` pattern has been implemented.
|
||||
* Taint-tracking configurations now interact differently with the `data` flow label, which may affect queries
|
||||
that combine taint-tracking and flow labels.
|
||||
- Sources added by the 1-argument `isSource` predicate are associated with the `taint` label now, instead of the `data` label.
|
||||
- Sanitizers now only block the `taint` label. As a result, sanitizers no longer block the flow of tainted values wrapped inside a property of an object.
|
||||
To retain the old behavior, instead use a barrier, or block the `data` flow label using a labeled sanitizer.
|
||||
|
||||
@@ -4,6 +4,8 @@ The following changes in version 1.24 affect Python analysis in all applications
|
||||
|
||||
## General improvements
|
||||
|
||||
Support for Django version 2.x and 3.x
|
||||
|
||||
## New queries
|
||||
|
||||
| **Query** | **Tags** | **Purpose** |
|
||||
@@ -13,6 +15,7 @@ The following changes in version 1.24 affect Python analysis in all applications
|
||||
|
||||
| **Query** | **Expected impact** | **Change** |
|
||||
|----------------------------|------------------------|------------------------------------------------------------------|
|
||||
| Uncontrolled command line (`py/command-line-injection`) | More results | We now model the `fabric` and `invoke` pacakges for command execution. |
|
||||
|
||||
### Web framework support
|
||||
|
||||
|
||||
@@ -39,6 +39,12 @@
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll"
|
||||
],
|
||||
"DataFlow Java/C++/C# Consistency checks": [
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll",
|
||||
"csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll"
|
||||
],
|
||||
"C++ SubBasicBlocks": [
|
||||
"cpp/ql/src/semmle/code/cpp/controlflow/SubBasicBlocks.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/SubBasicBlocks.qll"
|
||||
|
||||
140
config/sync-files.py
Normal file
140
config/sync-files.py
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Due to various technical limitations, we sometimes have files that need to be
|
||||
# kept identical in the repository. This script loads a database of such
|
||||
# files and can perform two functions: check whether they are still identical,
|
||||
# and overwrite the others with a master copy if needed.
|
||||
|
||||
import hashlib
|
||||
import shutil
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
path = os.path
|
||||
|
||||
file_groups = {}
|
||||
|
||||
def add_prefix(prefix, relative):
|
||||
result = path.join(prefix, relative)
|
||||
if path.commonprefix((path.realpath(result), path.realpath(prefix))) != \
|
||||
path.realpath(prefix):
|
||||
raise Exception("Path {} is not below {}".format(
|
||||
result, prefix))
|
||||
return result
|
||||
|
||||
def load_if_exists(prefix, json_file_relative):
|
||||
json_file_name = path.join(prefix, json_file_relative)
|
||||
if path.isfile(json_file_name):
|
||||
print("Loading file groups from", json_file_name)
|
||||
with open(json_file_name, 'r', encoding='utf-8') as fp:
|
||||
raw_groups = json.load(fp)
|
||||
prefixed_groups = {
|
||||
name: [
|
||||
add_prefix(prefix, relative)
|
||||
for relative in relatives
|
||||
]
|
||||
for name, relatives in raw_groups.items()
|
||||
}
|
||||
file_groups.update(prefixed_groups)
|
||||
|
||||
# Generates a list of C# test files that should be in sync
|
||||
def csharp_test_files():
|
||||
test_file_re = re.compile('.*(Bad|Good)[0-9]*\\.cs$')
|
||||
csharp_doc_files = {
|
||||
file:os.path.join(root, file)
|
||||
for root, dirs, files in os.walk("csharp/ql/src")
|
||||
for file in files
|
||||
if test_file_re.match(file)
|
||||
}
|
||||
return {
|
||||
"C# test '" + file + "'" : [os.path.join(root, file), csharp_doc_files[file]]
|
||||
for root, dirs, files in os.walk("csharp/ql/test")
|
||||
for file in files
|
||||
if file in csharp_doc_files
|
||||
}
|
||||
|
||||
def file_checksum(filename):
|
||||
with open(filename, 'rb') as file_handle:
|
||||
return hashlib.sha1(file_handle.read()).hexdigest()
|
||||
|
||||
def check_group(group_name, files, master_file_picker, emit_error):
|
||||
checksums = {file_checksum(f) for f in files}
|
||||
|
||||
if len(checksums) == 1:
|
||||
return
|
||||
|
||||
master_file = master_file_picker(files)
|
||||
if master_file is None:
|
||||
emit_error(__file__, 0,
|
||||
"Files from group '"+ group_name +"' not in sync.")
|
||||
emit_error(__file__, 0,
|
||||
"Run this script with a file-name argument among the "
|
||||
"following to overwrite the remaining files with the contents "
|
||||
"of that file or run with the --latest switch to update each "
|
||||
"group of files from the most recently modified file in the group.")
|
||||
for filename in files:
|
||||
emit_error(__file__, 0, " " + filename)
|
||||
else:
|
||||
print(" Syncing others from", master_file)
|
||||
for filename in files:
|
||||
if filename == master_file:
|
||||
continue
|
||||
print(" " + filename)
|
||||
os.replace(filename, filename + '~')
|
||||
shutil.copy(master_file, filename)
|
||||
print(" Backups written with '~' appended to file names")
|
||||
|
||||
def chdir_repo_root():
|
||||
root_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..')
|
||||
os.chdir(root_path)
|
||||
|
||||
def choose_master_file(master_file, files):
|
||||
if master_file in files:
|
||||
return master_file
|
||||
else:
|
||||
return None
|
||||
|
||||
def choose_latest_file(files):
|
||||
latest_time = None
|
||||
latest_file = None
|
||||
for filename in files:
|
||||
file_time = os.path.getmtime(filename)
|
||||
if (latest_time is None) or (latest_time < file_time):
|
||||
latest_time = file_time
|
||||
latest_file = filename
|
||||
return latest_file
|
||||
|
||||
local_error_count = 0
|
||||
def emit_local_error(path, line, error):
|
||||
print('ERROR: ' + path + ':' + line + " - " + error)
|
||||
global local_error_count
|
||||
local_error_count += 1
|
||||
|
||||
# This function is invoked directly by a CI script, which passes a different error-handling
|
||||
# callback.
|
||||
def sync_identical_files(emit_error):
|
||||
if len(sys.argv) == 1:
|
||||
master_file_picker = lambda files: None
|
||||
elif len(sys.argv) == 2:
|
||||
if sys.argv[1] == "--latest":
|
||||
master_file_picker = choose_latest_file
|
||||
elif os.path.isfile(sys.argv[1]):
|
||||
master_file_picker = lambda files: choose_master_file(sys.argv[1], files)
|
||||
else:
|
||||
raise Exception("File not found")
|
||||
else:
|
||||
raise Exception("Bad command line or file not found")
|
||||
chdir_repo_root()
|
||||
load_if_exists('.', 'config/identical-files.json')
|
||||
file_groups.update(csharp_test_files())
|
||||
for group_name, files in file_groups.items():
|
||||
check_group(group_name, files, master_file_picker, emit_error)
|
||||
|
||||
def main():
|
||||
sync_identical_files(emit_local_error)
|
||||
if local_error_count > 0:
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -12,6 +12,7 @@ import semmle.code.cpp.dataflow.DataFlow
|
||||
*/
|
||||
predicate allocExpr(Expr alloc, string kind) {
|
||||
isAllocationExpr(alloc) and
|
||||
not alloc.isFromUninstantiatedTemplate(_) and
|
||||
(
|
||||
alloc instanceof FunctionCall and
|
||||
kind = "malloc"
|
||||
|
||||
@@ -19,15 +19,19 @@ class ConstantZero extends Expr {
|
||||
* Holds if `candidate` is an expression such that if it's unsigned then we
|
||||
* want an alert at `ge`.
|
||||
*/
|
||||
private predicate lookForUnsignedAt(GEExpr ge, Expr candidate) {
|
||||
// Base case: `candidate >= 0`
|
||||
ge.getRightOperand() instanceof ConstantZero and
|
||||
candidate = ge.getLeftOperand().getFullyConverted() and
|
||||
// left operand was a signed or unsigned IntegralType before conversions
|
||||
private predicate lookForUnsignedAt(RelationalOperation ge, Expr candidate) {
|
||||
// Base case: `candidate >= 0` (or `0 <= candidate`)
|
||||
(
|
||||
ge instanceof GEExpr or
|
||||
ge instanceof LEExpr
|
||||
) and
|
||||
ge.getLesserOperand() instanceof ConstantZero and
|
||||
candidate = ge.getGreaterOperand().getFullyConverted() and
|
||||
// left/greater operand was a signed or unsigned IntegralType before conversions
|
||||
// (not a pointer, checking a pointer >= 0 is an entirely different mistake)
|
||||
// (not an enum, as the fully converted type of an enum is compiler dependent
|
||||
// so checking an enum >= 0 is always reasonable)
|
||||
ge.getLeftOperand().getUnderlyingType() instanceof IntegralType
|
||||
ge.getGreaterOperand().getUnderlyingType() instanceof IntegralType
|
||||
or
|
||||
// Recursive case: `...(largerType)candidate >= 0`
|
||||
exists(Conversion conversion |
|
||||
@@ -37,7 +41,7 @@ private predicate lookForUnsignedAt(GEExpr ge, Expr candidate) {
|
||||
)
|
||||
}
|
||||
|
||||
class UnsignedGEZero extends GEExpr {
|
||||
class UnsignedGEZero extends ComparisonOperation {
|
||||
UnsignedGEZero() {
|
||||
exists(Expr ue |
|
||||
lookForUnsignedAt(this, ue) and
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
#include <stdlib.h>
|
||||
#include <sys/param.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
|
||||
void callSetuidAndCheck(int uid) {
|
||||
if (setuid(uid) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void callSetgidAndCheck(int gid) {
|
||||
if (setgid(gid) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Correct ways to drop priv.
|
||||
|
||||
void correctDropPrivInline() {
|
||||
if (setgroups(0, NULL)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (setgid(-2) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (setuid(-2) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void correctDropPrivInScope() {
|
||||
{
|
||||
if (setgroups(0, NULL)) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (setgid(-2) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (setuid(-2) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void correctOrderForInitgroups() {
|
||||
struct passwd *pw = getpwuid(0);
|
||||
if (pw) {
|
||||
if (initgroups(pw->pw_name, -2)) {
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
// Unhandled.
|
||||
}
|
||||
int rc = setuid(-2);
|
||||
if (rc) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void correctDropPrivInScopeParent() {
|
||||
{
|
||||
callSetgidAndCheck(-2);
|
||||
}
|
||||
correctOrderForInitgroups();
|
||||
}
|
||||
|
||||
void incorrectNoReturnCodeCheck() {
|
||||
int user = -2;
|
||||
if (user) {
|
||||
if (user) {
|
||||
int rc = setgid(user);
|
||||
(void)rc;
|
||||
initgroups("nobody", user);
|
||||
}
|
||||
if (user) {
|
||||
setuid(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void correctDropPrivInFunctionCall() {
|
||||
if (setgroups(0, NULL)) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
callSetgidAndCheck(-2);
|
||||
callSetuidAndCheck(-2);
|
||||
}
|
||||
|
||||
/// Incorrect, out of order gid and uid.
|
||||
/// Calling uid before gid will fail.
|
||||
|
||||
void incorrectDropPrivOutOfOrderInline() {
|
||||
if (setuid(-2) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (setgid(-2) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void incorrectDropPrivOutOfOrderInScope() {
|
||||
{
|
||||
if (setuid(-2) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
setgid(-2);
|
||||
}
|
||||
|
||||
void incorrectDropPrivOutOfOrderWithFunction() {
|
||||
callSetuidAndCheck(-2);
|
||||
|
||||
if (setgid(-2) != 0) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void incorrectDropPrivOutOfOrderWithFunction2() {
|
||||
callSetuidAndCheck(-2);
|
||||
callSetgidAndCheck(-2);
|
||||
}
|
||||
|
||||
void incorrectDropPrivNoCheck() {
|
||||
setgid(-2);
|
||||
setuid(-2);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The code attempts to drop privilege in an incorrect order by
|
||||
erroneous dropping user privilege before groups. This has security
|
||||
impact if the return codes are not checked.</p>
|
||||
|
||||
<p>False positives include code performing negative checks, making
|
||||
sure that setgid or setgroups does not work, meaning permissions are
|
||||
dropped. Additionally, other forms of sandboxing may be present removing
|
||||
any residual risk, for example a dedicated user namespace.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>Set the new group ID, then set the target user's intended groups by
|
||||
dropping previous supplemental source groups and initializing target
|
||||
groups, and finally set the target user.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates out of order calls.</p>
|
||||
<sample src="PrivilegeDroppingOutoforder.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/POS37-C.+Ensure+that+privilege+relinquishment+is+successful">POS37-C. Ensure that privilege relinquishment is successful</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @name LinuxPrivilegeDroppingOutoforder
|
||||
* @description A syscall commonly associated with privilege dropping is being called out of order.
|
||||
* Normally a process drops group ID and sets supplimental groups for the target user
|
||||
* before setting the target user ID. This can have security impact if the return code
|
||||
* from these methods is not checked.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @id cpp/drop-linux-privileges-outoforder
|
||||
* @tags security
|
||||
* external/cwe/cwe-273
|
||||
* @precision medium
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate argumentMayBeRoot(Expr e) {
|
||||
e.getValue() = "0" or
|
||||
e.(VariableAccess).getTarget().getName().toLowerCase().matches("%root%")
|
||||
}
|
||||
|
||||
class SetuidLikeFunctionCall extends FunctionCall {
|
||||
SetuidLikeFunctionCall() {
|
||||
(getTarget().hasGlobalName("setuid") or getTarget().hasGlobalName("setresuid")) and
|
||||
// setuid/setresuid with the root user are false positives.
|
||||
not argumentMayBeRoot(getArgument(0))
|
||||
}
|
||||
}
|
||||
|
||||
class SetuidLikeWrapperCall extends FunctionCall {
|
||||
SetuidLikeFunctionCall baseCall;
|
||||
|
||||
SetuidLikeWrapperCall() {
|
||||
this = baseCall
|
||||
or
|
||||
exists(SetuidLikeWrapperCall fc |
|
||||
this.getTarget() = fc.getEnclosingFunction() and
|
||||
baseCall = fc.getBaseCall()
|
||||
)
|
||||
}
|
||||
|
||||
SetuidLikeFunctionCall getBaseCall() { result = baseCall }
|
||||
}
|
||||
|
||||
class CallBeforeSetuidFunctionCall extends FunctionCall {
|
||||
CallBeforeSetuidFunctionCall() {
|
||||
(
|
||||
getTarget().hasGlobalName("setgid") or
|
||||
getTarget().hasGlobalName("setresgid") or
|
||||
// Compatibility may require skipping initgroups and setgroups return checks.
|
||||
// A stricter best practice is to check the result and errnor for EPERM.
|
||||
getTarget().hasGlobalName("initgroups") or
|
||||
getTarget().hasGlobalName("setgroups")
|
||||
) and
|
||||
// setgid/setresgid/etc with the root group are false positives.
|
||||
not argumentMayBeRoot(getArgument(0))
|
||||
}
|
||||
}
|
||||
|
||||
class CallBeforeSetuidWrapperCall extends FunctionCall {
|
||||
CallBeforeSetuidFunctionCall baseCall;
|
||||
|
||||
CallBeforeSetuidWrapperCall() {
|
||||
this = baseCall
|
||||
or
|
||||
exists(CallBeforeSetuidWrapperCall fc |
|
||||
this.getTarget() = fc.getEnclosingFunction() and
|
||||
baseCall = fc.getBaseCall()
|
||||
)
|
||||
}
|
||||
|
||||
CallBeforeSetuidFunctionCall getBaseCall() { result = baseCall }
|
||||
}
|
||||
|
||||
predicate setuidBeforeSetgid(
|
||||
SetuidLikeWrapperCall setuidWrapper, CallBeforeSetuidWrapperCall setgidWrapper
|
||||
) {
|
||||
setgidWrapper.getAPredecessor+() = setuidWrapper
|
||||
}
|
||||
|
||||
predicate isAccessed(FunctionCall fc) {
|
||||
exists(Variable v | v.getAnAssignedValue() = fc)
|
||||
or
|
||||
exists(Operation c | fc = c.getAChild() | c.isCondition())
|
||||
or
|
||||
// ignore pattern where result is intentionally ignored by a cast to void.
|
||||
fc.hasExplicitConversion()
|
||||
}
|
||||
|
||||
from Function func, CallBeforeSetuidFunctionCall fc, SetuidLikeFunctionCall setuid
|
||||
where
|
||||
setuidBeforeSetgid(setuid, fc) and
|
||||
// Require the call return code to be used in a condition or assigned.
|
||||
// This introduces false negatives where the return is checked but then
|
||||
// errno == EPERM allows execution to continue.
|
||||
not isAccessed(fc) and
|
||||
func = fc.getEnclosingFunction()
|
||||
select fc,
|
||||
"This function is called within " + func + ", and potentially after " +
|
||||
"$@, and may not succeed. Be sure to check the return code and errno, otherwise permissions " +
|
||||
"may not be dropped.", setuid, setuid.getTarget().getName()
|
||||
@@ -30,7 +30,13 @@ predicate functionsMissingReturnStmt(Function f, ControlFlowNode blame) {
|
||||
) and
|
||||
exists(ReturnStmt s |
|
||||
f.getAPredecessor() = s and
|
||||
blame = s.getAPredecessor()
|
||||
(
|
||||
blame = s.getAPredecessor() and
|
||||
count(blame.getASuccessor()) = 1
|
||||
or
|
||||
blame = s and
|
||||
exists(ControlFlowNode pred | pred = s.getAPredecessor() | count(pred.getASuccessor()) != 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -133,10 +133,16 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
*/
|
||||
Type getUnspecifiedType() { result = getType().getUnspecifiedType() }
|
||||
|
||||
/** Gets the nth parameter of this function. */
|
||||
/**
|
||||
* Gets the nth parameter of this function. There is no result for the
|
||||
* implicit `this` parameter, and there is no `...` varargs pseudo-parameter.
|
||||
*/
|
||||
Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) }
|
||||
|
||||
/** Gets a parameter of this function. */
|
||||
/**
|
||||
* Gets a parameter of this function. There is no result for the implicit
|
||||
* `this` parameter, and there is no `...` varargs pseudo-parameter.
|
||||
*/
|
||||
Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) }
|
||||
|
||||
/**
|
||||
|
||||
@@ -366,6 +366,59 @@ class LocalVariable extends LocalScopeVariable, @localvariable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable whose contents always have static storage duration. This can be a
|
||||
* global variable, a namespace variable, a static local variable, or a static
|
||||
* member variable.
|
||||
*/
|
||||
class StaticStorageDurationVariable extends Variable {
|
||||
StaticStorageDurationVariable() {
|
||||
this instanceof GlobalOrNamespaceVariable
|
||||
or
|
||||
this.(LocalVariable).isStatic()
|
||||
or
|
||||
this.(MemberVariable).isStatic()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the initializer for this variable is evaluated at runtime.
|
||||
*/
|
||||
predicate hasDynamicInitialization() {
|
||||
runtimeExprInStaticInitializer(this.getInitializer().getExpr())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is an expression in a static initializer that must be evaluated
|
||||
* at run time. This predicate computes "is non-const" instead of "is const"
|
||||
* since computing "is const" for an aggregate literal with many children would
|
||||
* either involve recursion through `forall` on those children or an iteration
|
||||
* through the rank numbers of the children, both of which can be slow.
|
||||
*/
|
||||
private predicate runtimeExprInStaticInitializer(Expr e) {
|
||||
inStaticInitializer(e) and
|
||||
if e instanceof AggregateLiteral // in sync with the cast in `inStaticInitializer`
|
||||
then runtimeExprInStaticInitializer(e.getAChild())
|
||||
else not e.getFullyConverted().isConstant()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is the initializer of a `StaticStorageDurationVariable`, either
|
||||
* directly or below some top-level `AggregateLiteral`s.
|
||||
*/
|
||||
private predicate inStaticInitializer(Expr e) {
|
||||
exists(StaticStorageDurationVariable var | e = var.getInitializer().getExpr())
|
||||
or
|
||||
// The cast to `AggregateLiteral` ensures we only compute what'll later be
|
||||
// needed by `runtimeExprInStaticInitializer`.
|
||||
inStaticInitializer(e.getParent().(AggregateLiteral))
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ local variable declared as `static`.
|
||||
*/
|
||||
class StaticLocalVariable extends LocalVariable, StaticStorageDurationVariable { }
|
||||
|
||||
/**
|
||||
* A C/C++ variable which has global scope or namespace scope. For example the
|
||||
* variables `a` and `b` in the following code:
|
||||
|
||||
@@ -441,10 +441,9 @@ private Node getControlOrderChildSparse(Node n, int i) {
|
||||
* thus should not have control flow computed.
|
||||
*/
|
||||
private predicate skipInitializer(Initializer init) {
|
||||
exists(LocalVariable local |
|
||||
exists(StaticLocalVariable local |
|
||||
init = local.getInitializer() and
|
||||
local.isStatic() and
|
||||
not runtimeExprInStaticInitializer(init.getExpr())
|
||||
not local.hasDynamicInitialization()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -243,7 +243,7 @@ private module Cached {
|
||||
* - Types are checked using the `compatibleTypes()` relation.
|
||||
*/
|
||||
cached
|
||||
module Final {
|
||||
private module Final {
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps, not taking call contexts into account.
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Provides consistency queries for checking invariants in the language-specific
|
||||
* data-flow classes and predicates.
|
||||
*/
|
||||
|
||||
private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Public
|
||||
private import tainttracking1.TaintTrackingParameter::Private
|
||||
private import tainttracking1.TaintTrackingParameter::Public
|
||||
|
||||
module Consistency {
|
||||
private class RelevantNode extends Node {
|
||||
RelevantNode() {
|
||||
this instanceof ArgumentNode or
|
||||
this instanceof ParameterNode or
|
||||
this instanceof ReturnNode or
|
||||
this = getAnOutNode(_, _) or
|
||||
simpleLocalFlowStep(this, _) or
|
||||
simpleLocalFlowStep(_, this) or
|
||||
jumpStep(this, _) or
|
||||
jumpStep(_, this) or
|
||||
storeStep(this, _, _) or
|
||||
storeStep(_, _, this) or
|
||||
readStep(this, _, _) or
|
||||
readStep(_, _, this) or
|
||||
defaultAdditionalTaintStep(this, _) or
|
||||
defaultAdditionalTaintStep(_, this)
|
||||
}
|
||||
}
|
||||
|
||||
query predicate uniqueEnclosingCallable(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(n.getEnclosingCallable()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueTypeBound(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(n.getTypeBound()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one type bound but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueTypeRepr(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(getErasedRepr(n.getTypeBound())) and
|
||||
c != 1 and
|
||||
msg = "Node should have one type representation but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueNodeLocation(Node n, string msg) {
|
||||
exists(int c |
|
||||
c =
|
||||
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
c != 1 and
|
||||
msg = "Node should have one location but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingLocation(string msg) {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueNodeToString(Node n, string msg) {
|
||||
exists(int c |
|
||||
c = count(n.toString()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one toString but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingToString(string msg) {
|
||||
exists(int c |
|
||||
c = strictcount(Node n | not exists(n.toString())) and
|
||||
msg = "Nodes without toString: " + c
|
||||
)
|
||||
}
|
||||
|
||||
query predicate parameterCallable(ParameterNode p, string msg) {
|
||||
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
|
||||
msg = "Callable mismatch for parameter."
|
||||
}
|
||||
|
||||
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
|
||||
simpleLocalFlowStep(n1, n2) and
|
||||
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) }
|
||||
|
||||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
|
||||
t = typeRepr() and
|
||||
not compatibleTypes(t, t) and
|
||||
msg = "Type compatibility predicate is not reflexive."
|
||||
}
|
||||
|
||||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
|
||||
isUnreachableInCall(n, call) and
|
||||
exists(DataFlowCallable c |
|
||||
c = n.getEnclosingCallable() and
|
||||
not viableCallable(call) = c
|
||||
) and
|
||||
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
|
||||
}
|
||||
|
||||
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
|
||||
(
|
||||
n = getAnOutNode(call, _) and
|
||||
msg = "OutNode and call does not share enclosing callable."
|
||||
or
|
||||
n.(ArgumentNode).argumentOf(call, _) and
|
||||
msg = "ArgumentNode and call does not share enclosing callable."
|
||||
) and
|
||||
n.getEnclosingCallable() != call.getEnclosingCallable()
|
||||
}
|
||||
|
||||
query predicate postIsNotPre(PostUpdateNode n, string msg) {
|
||||
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
|
||||
}
|
||||
|
||||
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
|
||||
exists(int c |
|
||||
c = count(n.getPreUpdateNode()) and
|
||||
c != 1 and
|
||||
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniquePostUpdate(Node n, string msg) {
|
||||
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
|
||||
msg = "Node has multiple PostUpdateNodes."
|
||||
}
|
||||
|
||||
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
|
||||
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
|
||||
msg = "PostUpdateNode does not share callable with its pre-update node."
|
||||
}
|
||||
|
||||
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
|
||||
|
||||
query predicate reverseRead(Node n, string msg) {
|
||||
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
|
||||
msg = "Origin of readStep is missing a PostUpdateNode."
|
||||
}
|
||||
|
||||
query predicate storeIsPostUpdate(Node n, string msg) {
|
||||
storeStep(_, _, n) and
|
||||
not n instanceof PostUpdateNode and
|
||||
msg = "Store targets should be PostUpdateNodes."
|
||||
}
|
||||
|
||||
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
|
||||
not hasPost(n) and
|
||||
not isImmutableOrUnobservable(n) and
|
||||
msg = "ArgumentNode is missing PostUpdateNode."
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -293,3 +293,27 @@ class DataFlowCall extends Expr {
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
/**
|
||||
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
|
||||
* modified or its modification cannot be observed, for example if it is a
|
||||
* freshly created object that is not saved in a variable.
|
||||
*
|
||||
* This predicate is only used for consistency checks.
|
||||
*/
|
||||
predicate isImmutableOrUnobservable(Node n) {
|
||||
// Is the null pointer (or something that's not really a pointer)
|
||||
exists(n.asExpr().getValue())
|
||||
or
|
||||
// Isn't a pointer or is a pointer to const
|
||||
forall(DerivedType dt | dt = n.asExpr().getActualType() |
|
||||
dt.getBaseType().isConst()
|
||||
or
|
||||
dt.getBaseType() instanceof RoutineType
|
||||
)
|
||||
or
|
||||
// Isn't something we can track
|
||||
n.asExpr() instanceof Call
|
||||
// The above list of cases isn't exhaustive, but it narrows down the
|
||||
// consistency alerts enough that most of them are interesting.
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ class UnaryMinusExpr extends UnaryArithmeticOperation, @arithnegexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "UnaryMinusExpr" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -30,7 +30,7 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unaryplusexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "UnaryPlusExpr" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,7 +109,7 @@ class PrefixIncrExpr extends IncrementOperation, PrefixCrementOperation, @preinc
|
||||
|
||||
override string getCanonicalQLClass() { result = "PrefixIncrExpr" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,7 +125,7 @@ class PrefixDecrExpr extends DecrementOperation, PrefixCrementOperation, @predec
|
||||
|
||||
override string getCanonicalQLClass() { result = "PrefixDecrExpr" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,7 +141,7 @@ class PostfixIncrExpr extends IncrementOperation, PostfixCrementOperation, @post
|
||||
|
||||
override string getCanonicalQLClass() { result = "PostfixIncrExpr" }
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
override int getPrecedence() { result = 17 }
|
||||
|
||||
override string toString() { result = "... " + getOperator() }
|
||||
}
|
||||
@@ -159,7 +159,7 @@ class PostfixDecrExpr extends DecrementOperation, PostfixCrementOperation, @post
|
||||
|
||||
override string getCanonicalQLClass() { result = "PostfixDecrExpr" }
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
override int getPrecedence() { result = 17 }
|
||||
|
||||
override string toString() { result = "... " + getOperator() }
|
||||
}
|
||||
@@ -210,7 +210,7 @@ class AddExpr extends BinaryArithmeticOperation, @addexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "AddExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -224,7 +224,7 @@ class SubExpr extends BinaryArithmeticOperation, @subexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "SubExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,7 +238,7 @@ class MulExpr extends BinaryArithmeticOperation, @mulexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "MulExpr" }
|
||||
|
||||
override int getPrecedence() { result = 13 }
|
||||
override int getPrecedence() { result = 14 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +252,7 @@ class DivExpr extends BinaryArithmeticOperation, @divexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "DivExpr" }
|
||||
|
||||
override int getPrecedence() { result = 13 }
|
||||
override int getPrecedence() { result = 14 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,7 +266,7 @@ class RemExpr extends BinaryArithmeticOperation, @remexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "RemExpr" }
|
||||
|
||||
override int getPrecedence() { result = 13 }
|
||||
override int getPrecedence() { result = 14 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,7 +283,7 @@ class ImaginaryMulExpr extends BinaryArithmeticOperation, @jmulexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryMulExpr" }
|
||||
|
||||
override int getPrecedence() { result = 13 }
|
||||
override int getPrecedence() { result = 14 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -300,7 +300,7 @@ class ImaginaryDivExpr extends BinaryArithmeticOperation, @jdivexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryDivExpr" }
|
||||
|
||||
override int getPrecedence() { result = 13 }
|
||||
override int getPrecedence() { result = 14 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -318,7 +318,7 @@ class RealImaginaryAddExpr extends BinaryArithmeticOperation, @fjaddexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "RealImaginaryAddExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,7 +336,7 @@ class ImaginaryRealAddExpr extends BinaryArithmeticOperation, @jfaddexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryRealAddExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -354,7 +354,7 @@ class RealImaginarySubExpr extends BinaryArithmeticOperation, @fjsubexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "RealImaginarySubExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,7 +372,7 @@ class ImaginaryRealSubExpr extends BinaryArithmeticOperation, @jfsubexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "ImaginaryRealSubExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -416,7 +416,7 @@ class PointerAddExpr extends PointerArithmeticOperation, @paddexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "PointerAddExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,7 +431,7 @@ class PointerSubExpr extends PointerArithmeticOperation, @psubexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "PointerSubExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -446,5 +446,5 @@ class PointerDiffExpr extends PointerArithmeticOperation, @pdiffexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "PointerDiffExpr" }
|
||||
|
||||
override int getPrecedence() { result = 12 }
|
||||
override int getPrecedence() { result = 13 }
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ class UnaryBitwiseOperation extends UnaryOperation, @un_bitwise_op_expr { }
|
||||
class ComplementExpr extends UnaryBitwiseOperation, @complementexpr {
|
||||
override string getOperator() { result = "~" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
override string getCanonicalQLClass() { result = "ComplementExpr" }
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class BinaryBitwiseOperation extends BinaryOperation, @bin_bitwise_op_expr { }
|
||||
class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr {
|
||||
override string getOperator() { result = "<<" }
|
||||
|
||||
override int getPrecedence() { result = 11 }
|
||||
override int getPrecedence() { result = 12 }
|
||||
|
||||
override string getCanonicalQLClass() { result = "LShiftExpr" }
|
||||
}
|
||||
@@ -47,7 +47,7 @@ class LShiftExpr extends BinaryBitwiseOperation, @lshiftexpr {
|
||||
class RShiftExpr extends BinaryBitwiseOperation, @rshiftexpr {
|
||||
override string getOperator() { result = ">>" }
|
||||
|
||||
override int getPrecedence() { result = 11 }
|
||||
override int getPrecedence() { result = 12 }
|
||||
|
||||
override string getCanonicalQLClass() { result = "RShiftExpr" }
|
||||
}
|
||||
|
||||
@@ -8,6 +8,22 @@ abstract class BuiltInOperation extends Expr {
|
||||
override string getCanonicalQLClass() { result = "BuiltInOperation" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ built-in operation that is used to support functions with variable numbers of arguments.
|
||||
* This includes `va_start`, `va_end`, `va_copy`, and `va_arg`.
|
||||
*/
|
||||
class VarArgsExpr extends BuiltInOperation {
|
||||
VarArgsExpr() {
|
||||
this instanceof BuiltInVarArgsStart
|
||||
or
|
||||
this instanceof BuiltInVarArgsEnd
|
||||
or
|
||||
this instanceof BuiltInVarArg
|
||||
or
|
||||
this instanceof BuiltInVarArgCopy
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A C/C++ `__builtin_va_start` built-in operation (used by some
|
||||
* implementations of `va_start`).
|
||||
@@ -20,6 +36,16 @@ class BuiltInVarArgsStart extends BuiltInOperation, @vastartexpr {
|
||||
override string toString() { result = "__builtin_va_start" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "BuiltInVarArgsStart" }
|
||||
|
||||
/**
|
||||
* Gets the `va_list` argument.
|
||||
*/
|
||||
final Expr getVAList() { result = getChild(0) }
|
||||
|
||||
/**
|
||||
* Gets the argument that specifies the last named parameter before the ellipsis.
|
||||
*/
|
||||
final VariableAccess getLastNamedParameter() { result = getChild(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,6 +61,11 @@ class BuiltInVarArgsEnd extends BuiltInOperation, @vaendexpr {
|
||||
override string toString() { result = "__builtin_va_end" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "BuiltInVarArgsEnd" }
|
||||
|
||||
/**
|
||||
* Gets the `va_list` argument.
|
||||
*/
|
||||
final Expr getVAList() { result = getChild(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,6 +79,11 @@ class BuiltInVarArg extends BuiltInOperation, @vaargexpr {
|
||||
override string toString() { result = "__builtin_va_arg" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "BuiltInVarArg" }
|
||||
|
||||
/**
|
||||
* Gets the `va_list` argument.
|
||||
*/
|
||||
final Expr getVAList() { result = getChild(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +99,16 @@ class BuiltInVarArgCopy extends BuiltInOperation, @vacopyexpr {
|
||||
override string toString() { result = "__builtin_va_copy" }
|
||||
|
||||
override string getCanonicalQLClass() { result = "BuiltInVarArgCopy" }
|
||||
|
||||
/**
|
||||
* Gets the destination `va_list` argument.
|
||||
*/
|
||||
final Expr getDestinationVAList() { result = getChild(0) }
|
||||
|
||||
/**
|
||||
* Gets the the source `va_list` argument.
|
||||
*/
|
||||
final Expr getSourceVAList() { result = getChild(1) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -74,7 +74,7 @@ abstract class Call extends Expr, NameQualifiableElement {
|
||||
*/
|
||||
abstract Function getTarget();
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
override int getPrecedence() { result = 17 }
|
||||
|
||||
override string toString() { none() }
|
||||
|
||||
@@ -196,7 +196,7 @@ class FunctionCall extends Call, @funbindexpr {
|
||||
* constructor calls, this predicate instead gets the `Class` of the constructor
|
||||
* being called.
|
||||
*/
|
||||
private Type getTargetType() { result = Call.super.getType().stripType() }
|
||||
Type getTargetType() { result = Call.super.getType().stripType() }
|
||||
|
||||
/**
|
||||
* Gets the expected return type of the function called by this call.
|
||||
|
||||
@@ -84,7 +84,7 @@ class CStyleCast extends Cast, @c_style_cast {
|
||||
|
||||
override string getCanonicalQLClass() { result = "CStyleCast" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +103,7 @@ class StaticCast extends Cast, @static_cast {
|
||||
|
||||
override string getCanonicalQLClass() { result = "StaticCast" }
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
override int getPrecedence() { result = 17 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,7 +121,7 @@ class ConstCast extends Cast, @const_cast {
|
||||
|
||||
override string getCanonicalQLClass() { result = "ConstCast" }
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
override int getPrecedence() { result = 17 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,7 +139,7 @@ class ReinterpretCast extends Cast, @reinterpret_cast {
|
||||
|
||||
override string getCanonicalQLClass() { result = "ReinterpretCast" }
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
override int getPrecedence() { result = 17 }
|
||||
}
|
||||
|
||||
private predicate isArithmeticOrEnum(Type type) {
|
||||
@@ -608,7 +608,7 @@ class PrvalueAdjustmentConversion extends Cast {
|
||||
class DynamicCast extends Cast, @dynamic_cast {
|
||||
override string toString() { result = "dynamic_cast<" + this.getType().getName() + ">..." }
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
override int getPrecedence() { result = 17 }
|
||||
|
||||
override string getCanonicalQLClass() { result = "DynamicCast" }
|
||||
|
||||
@@ -631,7 +631,7 @@ class UuidofOperator extends Expr, @uuidof {
|
||||
else result = "__uuidof(0)"
|
||||
}
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
/** Gets the contained type. */
|
||||
Type getTypeOperand() { uuidof_bind(underlyingElement(this), unresolveElement(result)) }
|
||||
@@ -669,7 +669,7 @@ class TypeidOperator extends Expr, @type_id {
|
||||
|
||||
override string toString() { result = "typeid ..." }
|
||||
|
||||
override int getPrecedence() { result = 16 }
|
||||
override int getPrecedence() { result = 17 }
|
||||
|
||||
override predicate mayBeImpure() { this.getExpr().mayBeImpure() }
|
||||
|
||||
@@ -700,7 +700,7 @@ class SizeofPackOperator extends Expr, @sizeof_pack {
|
||||
* A C/C++ sizeof expression.
|
||||
*/
|
||||
abstract class SizeofOperator extends Expr, @runtime_sizeof {
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -763,7 +763,7 @@ class SizeofTypeOperator extends SizeofOperator {
|
||||
* A C++11 `alignof` expression.
|
||||
*/
|
||||
abstract class AlignofOperator extends Expr, @runtime_alignof {
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ import semmle.code.cpp.Element
|
||||
private import semmle.code.cpp.Enclosing
|
||||
private import semmle.code.cpp.internal.ResolveClass
|
||||
private import semmle.code.cpp.internal.AddressConstantExpression
|
||||
private import semmle.code.cpp.models.implementations.Allocation
|
||||
|
||||
/**
|
||||
* A C/C++ expression.
|
||||
@@ -642,7 +643,7 @@ class AddressOfExpr extends UnaryOperation, @address_of {
|
||||
|
||||
override string getOperator() { result = "&" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
override predicate mayBeImpure() { this.getOperand().mayBeImpure() }
|
||||
|
||||
@@ -664,7 +665,7 @@ class ReferenceToExpr extends Conversion, @reference_to {
|
||||
|
||||
override string getCanonicalQLClass() { result = "ReferenceToExpr" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -687,7 +688,7 @@ class PointerDereferenceExpr extends UnaryOperation, @indirect {
|
||||
|
||||
override string getOperator() { result = "*" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
override predicate mayBeImpure() {
|
||||
this.getChild(0).mayBeImpure() or
|
||||
@@ -721,7 +722,7 @@ class ReferenceDereferenceExpr extends Conversion, @ref_indirect {
|
||||
* A C++ `new` or `new[]` expression.
|
||||
*/
|
||||
class NewOrNewArrayExpr extends Expr, @any_new_expr {
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
/**
|
||||
* Gets the `operator new` or `operator new[]` that allocates storage.
|
||||
@@ -804,8 +805,10 @@ class NewOrNewArrayExpr extends Expr, @any_new_expr {
|
||||
* call the constructor of `T` but will not allocate memory.
|
||||
*/
|
||||
Expr getPlacementPointer() {
|
||||
isStandardPlacementNewAllocator(this.getAllocator()) and
|
||||
result = this.getAllocatorCall().getArgument(1)
|
||||
result =
|
||||
this
|
||||
.getAllocatorCall()
|
||||
.getArgument(this.getAllocator().(OperatorNewAllocationFunction).getPlacementArgument())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -898,7 +901,7 @@ class DeleteExpr extends Expr, @delete_expr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "DeleteExpr" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
/**
|
||||
* Gets the compile-time type of the object being deleted.
|
||||
@@ -972,7 +975,7 @@ class DeleteArrayExpr extends Expr, @delete_array_expr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "DeleteArrayExpr" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
|
||||
/**
|
||||
* Gets the element type of the array being deleted.
|
||||
@@ -1194,12 +1197,6 @@ private predicate convparents(Expr child, int idx, Element parent) {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isStandardPlacementNewAllocator(Function operatorNew) {
|
||||
operatorNew.getName().matches("operator new%") and
|
||||
operatorNew.getNumberOfParameters() = 2 and
|
||||
operatorNew.getParameter(1).getType() instanceof VoidPointerType
|
||||
}
|
||||
|
||||
// Pulled out for performance. See QL-796.
|
||||
private predicate hasNoConversions(Expr e) { not e.hasConversion() }
|
||||
|
||||
@@ -1216,3 +1213,18 @@ private predicate constantTemplateLiteral(Expr e) {
|
||||
or
|
||||
constantTemplateLiteral(e.(Cast).getExpr())
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ three-way comparison operation, also known as the _spaceship
|
||||
* operation_. This is specific to C++20 and later.
|
||||
* ```
|
||||
* auto c = (a <=> b);
|
||||
* ```
|
||||
*/
|
||||
class SpaceshipExpr extends BinaryOperation, @spaceshipexpr {
|
||||
override string getCanonicalQLClass() { result = "SpaceshipExpr" }
|
||||
|
||||
override int getPrecedence() { result = 11 }
|
||||
|
||||
override string getOperator() { result = "<=>" }
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class NotExpr extends UnaryLogicalOperation, @notexpr {
|
||||
|
||||
override string getCanonicalQLClass() { result = "NotExpr" }
|
||||
|
||||
override int getPrecedence() { result = 15 }
|
||||
override int getPrecedence() { result = 16 }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,7 +60,14 @@ private DataFlow::Node getNodeForSource(Expr source) {
|
||||
(
|
||||
result = DataFlow::exprNode(source)
|
||||
or
|
||||
result = DataFlow::definitionByReferenceNode(source)
|
||||
// Some of the sources in `isUserInput` are intended to match the value of
|
||||
// an expression, while others (those modeled below) are intended to match
|
||||
// the taint that propagates out of an argument, like the `char *` argument
|
||||
// to `gets`. It's impossible here to tell which is which, but the "access
|
||||
// to argv" source is definitely not intended to match an output argument,
|
||||
// and it causes false positives if we let it.
|
||||
result = DataFlow::definitionByReferenceNode(source) and
|
||||
not argv(source.(VariableAccess).getTarget())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -76,6 +83,8 @@ private class DefaultTaintTrackingCfg extends DataFlow::Configuration {
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
|
||||
@@ -96,6 +105,8 @@ private class ToGlobalVarTaintTrackingCfg extends DataFlow::Configuration {
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
|
||||
@@ -119,6 +130,8 @@ private class FromGlobalVarTaintTrackingCfg extends DataFlow2::Configuration {
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node node) { nodeIsBarrier(node) }
|
||||
|
||||
override predicate isBarrierIn(DataFlow::Node node) { nodeIsBarrierIn(node) }
|
||||
}
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
@@ -153,6 +166,11 @@ private predicate nodeIsBarrier(DataFlow::Node node) {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||
// don't use dataflow into taint sources, as this leads to duplicate results.
|
||||
node = getNodeForSource(any(Expr e))
|
||||
}
|
||||
|
||||
private predicate instructionTaintStep(Instruction i1, Instruction i2) {
|
||||
// Expressions computed from tainted data are also tainted
|
||||
exists(CallInstruction call, int argIndex | call = i2 |
|
||||
@@ -172,7 +190,7 @@ private predicate instructionTaintStep(Instruction i1, Instruction i2) {
|
||||
i2.(UnaryInstruction).getUnary() = i1
|
||||
or
|
||||
i2.(ChiInstruction).getPartial() = i1 and
|
||||
not isChiForAllAliasedMemory(i2)
|
||||
not i2.isResultConflated()
|
||||
or
|
||||
exists(BinaryInstruction bin |
|
||||
bin = i2 and
|
||||
@@ -265,19 +283,6 @@ private predicate modelTaintToParameter(Function f, int parameterIn, int paramet
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `chi` is on the chain of chi-instructions for all aliased memory.
|
||||
* Taint should not pass through these instructions since they tend to mix up
|
||||
* unrelated objects.
|
||||
*/
|
||||
private predicate isChiForAllAliasedMemory(Instruction instr) {
|
||||
instr.(ChiInstruction).getTotal() instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
isChiForAllAliasedMemory(instr.(ChiInstruction).getTotal())
|
||||
or
|
||||
isChiForAllAliasedMemory(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
|
||||
}
|
||||
|
||||
private predicate modelTaintToReturnValue(Function f, int parameterIn) {
|
||||
// Taint flow from parameter to return value
|
||||
exists(FunctionInput modelIn, FunctionOutput modelOut |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -243,7 +243,7 @@ private module Cached {
|
||||
* - Types are checked using the `compatibleTypes()` relation.
|
||||
*/
|
||||
cached
|
||||
module Final {
|
||||
private module Final {
|
||||
/**
|
||||
* Holds if `p` can flow to `node` in the same callable using only
|
||||
* value-preserving steps, not taking call contexts into account.
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Provides consistency queries for checking invariants in the language-specific
|
||||
* data-flow classes and predicates.
|
||||
*/
|
||||
|
||||
private import DataFlowImplSpecific::Private
|
||||
private import DataFlowImplSpecific::Public
|
||||
private import tainttracking1.TaintTrackingParameter::Private
|
||||
private import tainttracking1.TaintTrackingParameter::Public
|
||||
|
||||
module Consistency {
|
||||
private class RelevantNode extends Node {
|
||||
RelevantNode() {
|
||||
this instanceof ArgumentNode or
|
||||
this instanceof ParameterNode or
|
||||
this instanceof ReturnNode or
|
||||
this = getAnOutNode(_, _) or
|
||||
simpleLocalFlowStep(this, _) or
|
||||
simpleLocalFlowStep(_, this) or
|
||||
jumpStep(this, _) or
|
||||
jumpStep(_, this) or
|
||||
storeStep(this, _, _) or
|
||||
storeStep(_, _, this) or
|
||||
readStep(this, _, _) or
|
||||
readStep(_, _, this) or
|
||||
defaultAdditionalTaintStep(this, _) or
|
||||
defaultAdditionalTaintStep(_, this)
|
||||
}
|
||||
}
|
||||
|
||||
query predicate uniqueEnclosingCallable(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(n.getEnclosingCallable()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueTypeBound(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(n.getTypeBound()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one type bound but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueTypeRepr(Node n, string msg) {
|
||||
exists(int c |
|
||||
n instanceof RelevantNode and
|
||||
c = count(getErasedRepr(n.getTypeBound())) and
|
||||
c != 1 and
|
||||
msg = "Node should have one type representation but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueNodeLocation(Node n, string msg) {
|
||||
exists(int c |
|
||||
c =
|
||||
count(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
c != 1 and
|
||||
msg = "Node should have one location but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingLocation(string msg) {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniqueNodeToString(Node n, string msg) {
|
||||
exists(int c |
|
||||
c = count(n.toString()) and
|
||||
c != 1 and
|
||||
msg = "Node should have one toString but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate missingToString(string msg) {
|
||||
exists(int c |
|
||||
c = strictcount(Node n | not exists(n.toString())) and
|
||||
msg = "Nodes without toString: " + c
|
||||
)
|
||||
}
|
||||
|
||||
query predicate parameterCallable(ParameterNode p, string msg) {
|
||||
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
|
||||
msg = "Callable mismatch for parameter."
|
||||
}
|
||||
|
||||
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
|
||||
simpleLocalFlowStep(n1, n2) and
|
||||
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
private DataFlowType typeRepr() { result = getErasedRepr(any(Node n).getTypeBound()) }
|
||||
|
||||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
|
||||
t = typeRepr() and
|
||||
not compatibleTypes(t, t) and
|
||||
msg = "Type compatibility predicate is not reflexive."
|
||||
}
|
||||
|
||||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
|
||||
isUnreachableInCall(n, call) and
|
||||
exists(DataFlowCallable c |
|
||||
c = n.getEnclosingCallable() and
|
||||
not viableCallable(call) = c
|
||||
) and
|
||||
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
|
||||
}
|
||||
|
||||
query predicate localCallNodes(DataFlowCall call, Node n, string msg) {
|
||||
(
|
||||
n = getAnOutNode(call, _) and
|
||||
msg = "OutNode and call does not share enclosing callable."
|
||||
or
|
||||
n.(ArgumentNode).argumentOf(call, _) and
|
||||
msg = "ArgumentNode and call does not share enclosing callable."
|
||||
) and
|
||||
n.getEnclosingCallable() != call.getEnclosingCallable()
|
||||
}
|
||||
|
||||
query predicate postIsNotPre(PostUpdateNode n, string msg) {
|
||||
n.getPreUpdateNode() = n and msg = "PostUpdateNode should not equal its pre-update node."
|
||||
}
|
||||
|
||||
query predicate postHasUniquePre(PostUpdateNode n, string msg) {
|
||||
exists(int c |
|
||||
c = count(n.getPreUpdateNode()) and
|
||||
c != 1 and
|
||||
msg = "PostUpdateNode should have one pre-update node but has " + c + "."
|
||||
)
|
||||
}
|
||||
|
||||
query predicate uniquePostUpdate(Node n, string msg) {
|
||||
1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and
|
||||
msg = "Node has multiple PostUpdateNodes."
|
||||
}
|
||||
|
||||
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
|
||||
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
|
||||
msg = "PostUpdateNode does not share callable with its pre-update node."
|
||||
}
|
||||
|
||||
private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) }
|
||||
|
||||
query predicate reverseRead(Node n, string msg) {
|
||||
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
|
||||
msg = "Origin of readStep is missing a PostUpdateNode."
|
||||
}
|
||||
|
||||
query predicate storeIsPostUpdate(Node n, string msg) {
|
||||
storeStep(_, _, n) and
|
||||
not n instanceof PostUpdateNode and
|
||||
msg = "Store targets should be PostUpdateNodes."
|
||||
}
|
||||
|
||||
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
|
||||
not hasPost(n) and
|
||||
not isImmutableOrUnobservable(n) and
|
||||
msg = "ArgumentNode is missing PostUpdateNode."
|
||||
}
|
||||
}
|
||||
@@ -202,3 +202,16 @@ class DataFlowCall extends CallInstruction {
|
||||
predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation
|
||||
|
||||
int accessPathLimit() { result = 5 }
|
||||
|
||||
/**
|
||||
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
|
||||
* modified or its modification cannot be observed, for example if it is a
|
||||
* freshly created object that is not saved in a variable.
|
||||
*
|
||||
* This predicate is only used for consistency checks.
|
||||
*/
|
||||
predicate isImmutableOrUnobservable(Node n) {
|
||||
// The rules for whether an IR argument gets a post-update node are too
|
||||
// complex to model here.
|
||||
any()
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
/**
|
||||
* Provides an implementation of global (interprocedural) taint tracking.
|
||||
* This file re-exports the local (intraprocedural) taint-tracking analysis
|
||||
* from `TaintTrackingParameter::Public` and adds a global analysis, mainly
|
||||
* exposed through the `Configuration` class. For some languages, this file
|
||||
* exists in several identical copies, allowing queries to use multiple
|
||||
* `Configuration` classes that depend on each other without introducing
|
||||
* mutual recursion among those configurations.
|
||||
*/
|
||||
|
||||
import TaintTrackingParameter::Public
|
||||
private import TaintTrackingParameter::Private
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ class IRConfiguration extends TIRConfiguration {
|
||||
* Holds if IR should be created for function `func`. By default, holds for all functions.
|
||||
*/
|
||||
predicate shouldCreateIRForFunction(Language::Function func) { any() }
|
||||
|
||||
predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() }
|
||||
}
|
||||
|
||||
private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration()
|
||||
|
||||
@@ -70,7 +70,7 @@ private newtype TOpcode =
|
||||
TVarArgsStart() or
|
||||
TVarArgsEnd() or
|
||||
TVarArg() or
|
||||
TVarArgCopy() or
|
||||
TNextVarArg() or
|
||||
TCallSideEffect() or
|
||||
TCallReadSideEffect() or
|
||||
TIndirectReadSideEffect() or
|
||||
@@ -629,20 +629,20 @@ module Opcode {
|
||||
final override string toString() { result = "BuiltIn" }
|
||||
}
|
||||
|
||||
class VarArgsStart extends BuiltInOperationOpcode, TVarArgsStart {
|
||||
class VarArgsStart extends UnaryOpcode, TVarArgsStart {
|
||||
final override string toString() { result = "VarArgsStart" }
|
||||
}
|
||||
|
||||
class VarArgsEnd extends BuiltInOperationOpcode, TVarArgsEnd {
|
||||
class VarArgsEnd extends UnaryOpcode, TVarArgsEnd {
|
||||
final override string toString() { result = "VarArgsEnd" }
|
||||
}
|
||||
|
||||
class VarArg extends BuiltInOperationOpcode, TVarArg {
|
||||
class VarArg extends UnaryOpcode, TVarArg {
|
||||
final override string toString() { result = "VarArg" }
|
||||
}
|
||||
|
||||
class VarArgCopy extends BuiltInOperationOpcode, TVarArgCopy {
|
||||
final override string toString() { result = "VarArgCopy" }
|
||||
class NextVarArg extends UnaryOpcode, TNextVarArg {
|
||||
final override string toString() { result = "NextVarArg" }
|
||||
}
|
||||
|
||||
class CallSideEffect extends WriteSideEffectOpcode, EscapedWriteOpcode, MayWriteOpcode,
|
||||
|
||||
@@ -27,6 +27,9 @@ class IRBlockBase extends TIRBlock {
|
||||
* by debugging and printing code only.
|
||||
*/
|
||||
int getDisplayIndex() {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock |
|
||||
funcBlock.getEnclosingFunction() = getEnclosingFunction()
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRTypeSanity // module is in IRType.qll
|
||||
module InstructionSanity {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
/**
|
||||
@@ -272,4 +273,48 @@ module InstructionSanity {
|
||||
func = switchInstr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
|
||||
* memory.
|
||||
*/
|
||||
private predicate isOnAliasedDefinitionChain(Instruction instr) {
|
||||
instr instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
|
||||
}
|
||||
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(Instruction instr) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated()
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(Instruction instr) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
overlap instanceof MayPartiallyOverlap and
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
|
||||
IRVariable() {
|
||||
this = TIRUserVariable(_, _, func) or
|
||||
this = TIRTempVariable(func, _, _, _) or
|
||||
this = TIRStringLiteral(func, _, _, _)
|
||||
this = TIRStringLiteral(func, _, _, _) or
|
||||
this = TIRDynamicInitializationFlag(func, _, _)
|
||||
}
|
||||
|
||||
string toString() { none() }
|
||||
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
|
||||
|
||||
IRGeneratedVariable() {
|
||||
this = TIRTempVariable(func, ast, _, type) or
|
||||
this = TIRStringLiteral(func, ast, type, _)
|
||||
this = TIRStringLiteral(func, ast, type, _) or
|
||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||
}
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
@@ -208,7 +210,17 @@ class IRReturnVariable extends IRTempVariable {
|
||||
class IRThrowVariable extends IRTempVariable {
|
||||
IRThrowVariable() { tag = ThrowTempVar() }
|
||||
|
||||
override string getBaseString() { result = "#throw" }
|
||||
final override string getBaseString() { result = "#throw" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||
}
|
||||
|
||||
override string getBaseString() { result = "#string" }
|
||||
final override string getBaseString() { result = "#string" }
|
||||
|
||||
final Language::StringLiteral getLiteral() { result = literal }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable generated to track whether a specific non-stack variable has been initialized. This is
|
||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||
* fields in C#.
|
||||
*/
|
||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
Language::Variable var;
|
||||
|
||||
IRDynamicInitializationFlag() {
|
||||
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
|
||||
}
|
||||
|
||||
final override string toString() { result = var.toString() + "#init" }
|
||||
|
||||
final Language::Variable getVariable() { result = var }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ private import Imports::OperandTag
|
||||
* `File` and line number. Used for assigning register names when printing IR.
|
||||
*/
|
||||
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction())
|
||||
) and
|
||||
exists(Language::Location location |
|
||||
irFunc = result.getEnclosingIRFunction() and
|
||||
location = result.getLocation() and
|
||||
@@ -39,6 +42,12 @@ class Instruction extends Construction::TInstruction {
|
||||
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
|
||||
}
|
||||
|
||||
private predicate shouldGenerateDumpStrings() {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the operation of this instruction. This includes
|
||||
* the opcode and the immediate value, if any. For example:
|
||||
@@ -46,6 +55,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* VariableAddress[x]
|
||||
*/
|
||||
final string getOperationString() {
|
||||
shouldGenerateDumpStrings() and
|
||||
if exists(getImmediateString())
|
||||
then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]"
|
||||
else result = getOperationPrefix() + getOpcode().toString()
|
||||
@@ -57,10 +67,12 @@ class Instruction extends Construction::TInstruction {
|
||||
string getImmediateString() { none() }
|
||||
|
||||
private string getOperationPrefix() {
|
||||
shouldGenerateDumpStrings() and
|
||||
if this instanceof SideEffectInstruction then result = "^" else result = ""
|
||||
}
|
||||
|
||||
private string getResultPrefix() {
|
||||
shouldGenerateDumpStrings() and
|
||||
if getResultIRType() instanceof IRVoidType
|
||||
then result = "v"
|
||||
else
|
||||
@@ -74,6 +86,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* used by debugging and printing code only.
|
||||
*/
|
||||
int getDisplayIndexInBlock() {
|
||||
shouldGenerateDumpStrings() and
|
||||
exists(IRBlock block |
|
||||
this = block.getInstruction(result)
|
||||
or
|
||||
@@ -87,6 +100,7 @@ class Instruction extends Construction::TInstruction {
|
||||
}
|
||||
|
||||
private int getLineRank() {
|
||||
shouldGenerateDumpStrings() and
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr =
|
||||
@@ -105,6 +119,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `r1_1`
|
||||
*/
|
||||
string getResultId() {
|
||||
shouldGenerateDumpStrings() and
|
||||
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
|
||||
}
|
||||
|
||||
@@ -116,6 +131,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `r1_1(int*)`
|
||||
*/
|
||||
final string getResultString() {
|
||||
shouldGenerateDumpStrings() and
|
||||
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
|
||||
}
|
||||
|
||||
@@ -126,6 +142,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `func:r3_4, this:r3_5`
|
||||
*/
|
||||
string getOperandsString() {
|
||||
shouldGenerateDumpStrings() and
|
||||
result =
|
||||
concat(Operand operand |
|
||||
operand = getAnOperand()
|
||||
@@ -321,6 +338,17 @@ class Instruction extends Construction::TInstruction {
|
||||
Construction::hasModeledMemoryResult(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is an instruction with a memory result that represents a
|
||||
* conflation of more than one memory allocation.
|
||||
*
|
||||
* This happens in practice when dereferencing a pointer that cannot be
|
||||
* tracked back to a single local allocation. Such memory is instead modeled
|
||||
* as originating on the `AliasedDefinitionInstruction` at the entry of the
|
||||
* function.
|
||||
*/
|
||||
final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) }
|
||||
|
||||
/**
|
||||
* Gets the successor of this instruction along the control flow edge
|
||||
* specified by `kind`.
|
||||
|
||||
@@ -384,6 +384,8 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
override string toString() { result = "SideEffect" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,19 +18,19 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
|
||||
predicate shouldPrintFunction(Language::Function func) { any() }
|
||||
}
|
||||
|
||||
private predicate shouldPrintFunction(Language::Function func) {
|
||||
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
|
||||
}
|
||||
|
||||
/**
|
||||
* Override of `IRConfiguration` to only create IR for the functions that are to be dumped.
|
||||
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
|
||||
*/
|
||||
private class FilteredIRConfiguration extends IRConfiguration {
|
||||
override predicate shouldCreateIRForFunction(Language::Function func) {
|
||||
override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
|
||||
shouldPrintFunction(func)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate shouldPrintFunction(Language::Function func) {
|
||||
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
|
||||
}
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
|
||||
}
|
||||
|
||||
@@ -354,6 +354,7 @@ class AllAliasedMemory extends TAllAliasedMemory, MemoryLocation {
|
||||
final override predicate isMayAccess() { isMayAccess = true }
|
||||
}
|
||||
|
||||
/** A virtual variable that groups all escaped memory within a function. */
|
||||
class AliasedVirtualVariable extends AllAliasedMemory, VirtualVariable {
|
||||
AliasedVirtualVariable() { not isMayAccess() }
|
||||
}
|
||||
@@ -429,10 +430,18 @@ private Overlap getExtentOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
use instanceof EntireAllocationMemoryLocation and
|
||||
result instanceof MustExactlyOverlap
|
||||
or
|
||||
// EntireAllocationMemoryLocation totally overlaps any location within the same virtual
|
||||
// variable.
|
||||
not use instanceof EntireAllocationMemoryLocation and
|
||||
result instanceof MustTotallyOverlap
|
||||
if def.getAllocation() = use.getAllocation()
|
||||
then
|
||||
// EntireAllocationMemoryLocation totally overlaps any location within
|
||||
// the same allocation.
|
||||
result instanceof MustTotallyOverlap
|
||||
else (
|
||||
// There is no overlap with a location that's known to belong to a
|
||||
// different allocation, but all other locations may partially overlap.
|
||||
not exists(use.getAllocation()) and
|
||||
result instanceof MayPartiallyOverlap
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(VariableMemoryLocation defVariableLocation |
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SSAConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
|
||||
@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
|
||||
@@ -65,6 +65,29 @@ private module Cached {
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
)
|
||||
or
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
|
||||
exists(OldInstruction oldInstruction, OldIR::RegisterOperand oldOperand |
|
||||
@@ -84,14 +107,15 @@ private module Cached {
|
||||
oldOperand instanceof OldIR::NonPhiMemoryOperand and
|
||||
exists(
|
||||
OldBlock useBlock, int useRank, Alias::MemoryLocation useLocation,
|
||||
Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset
|
||||
Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset,
|
||||
Alias::MemoryLocation actualDefLocation
|
||||
|
|
||||
useLocation = Alias::getOperandMemoryLocation(oldOperand) and
|
||||
hasUseAtRank(useLocation, useBlock, useRank, oldInstruction) and
|
||||
definitionReachesUse(useLocation, defBlock, defRank, useBlock, useRank) and
|
||||
hasDefinitionAtRank(useLocation, defLocation, defBlock, defRank, defOffset) and
|
||||
instr = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, _) and
|
||||
overlap = Alias::getOverlap(defLocation, useLocation)
|
||||
instr = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ newtype TIRVariable =
|
||||
) {
|
||||
Construction::hasTempVariable(func, ast, tag, type)
|
||||
} or
|
||||
TIRDynamicInitializationFlag(
|
||||
Language::Function func, Language::Variable var, Language::LanguageType type
|
||||
) {
|
||||
Construction::hasDynamicInitializationFlag(func, var, type)
|
||||
} or
|
||||
TIRStringLiteral(
|
||||
Language::Function func, Language::AST ast, Language::LanguageType type,
|
||||
Language::StringLiteral literal
|
||||
|
||||
@@ -27,6 +27,9 @@ class IRBlockBase extends TIRBlock {
|
||||
* by debugging and printing code only.
|
||||
*/
|
||||
int getDisplayIndex() {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock |
|
||||
funcBlock.getEnclosingFunction() = getEnclosingFunction()
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRTypeSanity // module is in IRType.qll
|
||||
module InstructionSanity {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
/**
|
||||
@@ -272,4 +273,48 @@ module InstructionSanity {
|
||||
func = switchInstr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
|
||||
* memory.
|
||||
*/
|
||||
private predicate isOnAliasedDefinitionChain(Instruction instr) {
|
||||
instr instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
|
||||
}
|
||||
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(Instruction instr) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated()
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(Instruction instr) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
overlap instanceof MayPartiallyOverlap and
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
|
||||
IRVariable() {
|
||||
this = TIRUserVariable(_, _, func) or
|
||||
this = TIRTempVariable(func, _, _, _) or
|
||||
this = TIRStringLiteral(func, _, _, _)
|
||||
this = TIRStringLiteral(func, _, _, _) or
|
||||
this = TIRDynamicInitializationFlag(func, _, _)
|
||||
}
|
||||
|
||||
string toString() { none() }
|
||||
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
|
||||
|
||||
IRGeneratedVariable() {
|
||||
this = TIRTempVariable(func, ast, _, type) or
|
||||
this = TIRStringLiteral(func, ast, type, _)
|
||||
this = TIRStringLiteral(func, ast, type, _) or
|
||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||
}
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
@@ -208,7 +210,17 @@ class IRReturnVariable extends IRTempVariable {
|
||||
class IRThrowVariable extends IRTempVariable {
|
||||
IRThrowVariable() { tag = ThrowTempVar() }
|
||||
|
||||
override string getBaseString() { result = "#throw" }
|
||||
final override string getBaseString() { result = "#throw" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||
}
|
||||
|
||||
override string getBaseString() { result = "#string" }
|
||||
final override string getBaseString() { result = "#string" }
|
||||
|
||||
final Language::StringLiteral getLiteral() { result = literal }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable generated to track whether a specific non-stack variable has been initialized. This is
|
||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||
* fields in C#.
|
||||
*/
|
||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
Language::Variable var;
|
||||
|
||||
IRDynamicInitializationFlag() {
|
||||
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
|
||||
}
|
||||
|
||||
final override string toString() { result = var.toString() + "#init" }
|
||||
|
||||
final Language::Variable getVariable() { result = var }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ private import Imports::OperandTag
|
||||
* `File` and line number. Used for assigning register names when printing IR.
|
||||
*/
|
||||
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction())
|
||||
) and
|
||||
exists(Language::Location location |
|
||||
irFunc = result.getEnclosingIRFunction() and
|
||||
location = result.getLocation() and
|
||||
@@ -39,6 +42,12 @@ class Instruction extends Construction::TInstruction {
|
||||
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
|
||||
}
|
||||
|
||||
private predicate shouldGenerateDumpStrings() {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the operation of this instruction. This includes
|
||||
* the opcode and the immediate value, if any. For example:
|
||||
@@ -46,6 +55,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* VariableAddress[x]
|
||||
*/
|
||||
final string getOperationString() {
|
||||
shouldGenerateDumpStrings() and
|
||||
if exists(getImmediateString())
|
||||
then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]"
|
||||
else result = getOperationPrefix() + getOpcode().toString()
|
||||
@@ -57,10 +67,12 @@ class Instruction extends Construction::TInstruction {
|
||||
string getImmediateString() { none() }
|
||||
|
||||
private string getOperationPrefix() {
|
||||
shouldGenerateDumpStrings() and
|
||||
if this instanceof SideEffectInstruction then result = "^" else result = ""
|
||||
}
|
||||
|
||||
private string getResultPrefix() {
|
||||
shouldGenerateDumpStrings() and
|
||||
if getResultIRType() instanceof IRVoidType
|
||||
then result = "v"
|
||||
else
|
||||
@@ -74,6 +86,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* used by debugging and printing code only.
|
||||
*/
|
||||
int getDisplayIndexInBlock() {
|
||||
shouldGenerateDumpStrings() and
|
||||
exists(IRBlock block |
|
||||
this = block.getInstruction(result)
|
||||
or
|
||||
@@ -87,6 +100,7 @@ class Instruction extends Construction::TInstruction {
|
||||
}
|
||||
|
||||
private int getLineRank() {
|
||||
shouldGenerateDumpStrings() and
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr =
|
||||
@@ -105,6 +119,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `r1_1`
|
||||
*/
|
||||
string getResultId() {
|
||||
shouldGenerateDumpStrings() and
|
||||
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
|
||||
}
|
||||
|
||||
@@ -116,6 +131,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `r1_1(int*)`
|
||||
*/
|
||||
final string getResultString() {
|
||||
shouldGenerateDumpStrings() and
|
||||
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
|
||||
}
|
||||
|
||||
@@ -126,6 +142,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `func:r3_4, this:r3_5`
|
||||
*/
|
||||
string getOperandsString() {
|
||||
shouldGenerateDumpStrings() and
|
||||
result =
|
||||
concat(Operand operand |
|
||||
operand = getAnOperand()
|
||||
@@ -321,6 +338,17 @@ class Instruction extends Construction::TInstruction {
|
||||
Construction::hasModeledMemoryResult(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is an instruction with a memory result that represents a
|
||||
* conflation of more than one memory allocation.
|
||||
*
|
||||
* This happens in practice when dereferencing a pointer that cannot be
|
||||
* tracked back to a single local allocation. Such memory is instead modeled
|
||||
* as originating on the `AliasedDefinitionInstruction` at the entry of the
|
||||
* function.
|
||||
*/
|
||||
final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) }
|
||||
|
||||
/**
|
||||
* Gets the successor of this instruction along the control flow edge
|
||||
* specified by `kind`.
|
||||
|
||||
@@ -384,6 +384,8 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
override string toString() { result = "SideEffect" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,19 +18,19 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
|
||||
predicate shouldPrintFunction(Language::Function func) { any() }
|
||||
}
|
||||
|
||||
private predicate shouldPrintFunction(Language::Function func) {
|
||||
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
|
||||
}
|
||||
|
||||
/**
|
||||
* Override of `IRConfiguration` to only create IR for the functions that are to be dumped.
|
||||
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
|
||||
*/
|
||||
private class FilteredIRConfiguration extends IRConfiguration {
|
||||
override predicate shouldCreateIRForFunction(Language::Function func) {
|
||||
override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
|
||||
shouldPrintFunction(func)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate shouldPrintFunction(Language::Function func) {
|
||||
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
|
||||
}
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
|
||||
}
|
||||
|
||||
@@ -51,9 +51,25 @@ private module Cached {
|
||||
getTypeForPRValue(literal.getType()) = type
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasDynamicInitializationFlag(Function func, StaticLocalVariable var, CppType type) {
|
||||
var.getFunction() = func and
|
||||
var.hasDynamicInitialization() and
|
||||
type = getBoolType()
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasModeledMemoryResult(Instruction instruction) { none() }
|
||||
|
||||
cached
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
cached
|
||||
Expr getInstructionConvertedResultExpression(Instruction instruction) {
|
||||
exists(TranslatedExpr translatedExpr |
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import IRConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
|
||||
@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
|
||||
@@ -8,6 +8,11 @@ newtype TInstructionTag =
|
||||
InitializerStoreTag() or
|
||||
InitializerIndirectAddressTag() or
|
||||
InitializerIndirectStoreTag() or
|
||||
DynamicInitializationFlagAddressTag() or
|
||||
DynamicInitializationFlagLoadTag() or
|
||||
DynamicInitializationConditionalBranchTag() or
|
||||
DynamicInitializationFlagConstantTag() or
|
||||
DynamicInitializationFlagStoreTag() or
|
||||
ZeroPadStringConstantTag() or
|
||||
ZeroPadStringElementIndexTag() or
|
||||
ZeroPadStringElementAddressTag() or
|
||||
@@ -59,6 +64,13 @@ newtype TInstructionTag =
|
||||
InitializerElementAddressTag() or
|
||||
InitializerElementDefaultValueTag() or
|
||||
InitializerElementDefaultValueStoreTag() or
|
||||
VarArgsStartEllipsisAddressTag() or
|
||||
VarArgsStartTag() or
|
||||
VarArgsVAListLoadTag() or
|
||||
VarArgsArgAddressTag() or
|
||||
VarArgsArgLoadTag() or
|
||||
VarArgsMoveNextTag() or
|
||||
VarArgsVAListStoreTag() or
|
||||
AsmTag() or
|
||||
AsmInputTag(int elementIndex) { exists(AsmStmt asm | exists(asm.getChild(elementIndex))) }
|
||||
|
||||
@@ -183,7 +195,31 @@ string getInstructionTagId(TInstructionTag tag) {
|
||||
or
|
||||
tag = InitializerElementDefaultValueStoreTag() and result = "InitElemDefValStore"
|
||||
or
|
||||
tag = VarArgsStartEllipsisAddressTag() and result = "VarArgsStartEllipsisAddr"
|
||||
or
|
||||
tag = VarArgsStartTag() and result = "VarArgsStart"
|
||||
or
|
||||
tag = VarArgsVAListLoadTag() and result = "VarArgsVAListLoad"
|
||||
or
|
||||
tag = VarArgsArgAddressTag() and result = "VarArgsArgAddr"
|
||||
or
|
||||
tag = VarArgsArgLoadTag() and result = "VaArgsArgLoad"
|
||||
or
|
||||
tag = VarArgsMoveNextTag() and result = "VarArgsMoveNext"
|
||||
or
|
||||
tag = VarArgsVAListStoreTag() and result = "VarArgsVAListStore"
|
||||
or
|
||||
tag = AsmTag() and result = "Asm"
|
||||
or
|
||||
exists(int index | tag = AsmInputTag(index) and result = "AsmInputTag(" + index + ")")
|
||||
or
|
||||
tag = DynamicInitializationFlagAddressTag() and result = "DynInitFlagAddr"
|
||||
or
|
||||
tag = DynamicInitializationFlagLoadTag() and result = "DynInitFlagLoad"
|
||||
or
|
||||
tag = DynamicInitializationConditionalBranchTag() and result = "DynInitCondBranch"
|
||||
or
|
||||
tag = DynamicInitializationFlagConstantTag() and result = "DynInitFlagConst"
|
||||
or
|
||||
tag = DynamicInitializationFlagStoreTag() and result = "DynInitFlagStore"
|
||||
}
|
||||
|
||||
@@ -206,7 +206,44 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
|
||||
predicate hasPreciseSideEffect() { exists(getSideEffects()) }
|
||||
|
||||
TranslatedSideEffects getSideEffects() { result.getCall() = expr }
|
||||
final TranslatedSideEffects getSideEffects() { result.getExpr() = expr }
|
||||
}
|
||||
|
||||
abstract class TranslatedSideEffects extends TranslatedElement {
|
||||
abstract Expr getExpr();
|
||||
|
||||
final override Locatable getAST() { result = getExpr() }
|
||||
|
||||
final override Function getFunction() { result = getExpr().getEnclosingFunction() }
|
||||
|
||||
override TranslatedElement getChild(int i) {
|
||||
result =
|
||||
rank[i + 1](TranslatedSideEffect tse, int isWrite, int index |
|
||||
(
|
||||
tse.getCall() = getExpr() and
|
||||
tse.getArgumentIndex() = index and
|
||||
if tse.isWrite() then isWrite = 1 else isWrite = 0
|
||||
)
|
||||
|
|
||||
tse order by isWrite, index
|
||||
)
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement te) {
|
||||
exists(int i |
|
||||
getChild(i) = te and
|
||||
if exists(getChild(i + 1))
|
||||
then result = getChild(i + 1).getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedFunction` containing this expression.
|
||||
*/
|
||||
final TranslatedFunction getEnclosingFunction() {
|
||||
result = getTranslatedFunction(getExpr().getEnclosingFunction())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -308,56 +345,27 @@ class TranslatedStructorCall extends TranslatedFunctionCall {
|
||||
override predicate hasQualifier() { any() }
|
||||
}
|
||||
|
||||
class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
|
||||
Call expr;
|
||||
class TranslatedAllocationSideEffects extends TranslatedSideEffects,
|
||||
TTranslatedAllocationSideEffects {
|
||||
AllocationExpr expr;
|
||||
|
||||
TranslatedSideEffects() { this = TTranslatedSideEffects(expr) }
|
||||
TranslatedAllocationSideEffects() { this = TTranslatedAllocationSideEffects(expr) }
|
||||
|
||||
override string toString() { result = "(side effects for " + expr.toString() + ")" }
|
||||
final override AllocationExpr getExpr() { result = expr }
|
||||
|
||||
override Locatable getAST() { result = expr }
|
||||
override string toString() { result = "(allocation side effects for " + expr.toString() + ")" }
|
||||
|
||||
Call getCall() { result = expr }
|
||||
|
||||
override TranslatedElement getChild(int i) {
|
||||
result =
|
||||
rank[i + 1](TranslatedSideEffect tse, int isWrite, int index |
|
||||
(
|
||||
tse.getCall() = getCall() and
|
||||
tse.getArgumentIndex() = index and
|
||||
if tse.isWrite() then isWrite = 1 else isWrite = 0
|
||||
)
|
||||
|
|
||||
tse order by isWrite, index
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement te) {
|
||||
exists(int i |
|
||||
getChild(i) = te and
|
||||
if exists(getChild(i + 1))
|
||||
then result = getChild(i + 1).getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
}
|
||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
||||
expr.getTarget() instanceof AllocationFunction and
|
||||
opcode instanceof Opcode::InitializeDynamicAllocation and
|
||||
tag = OnlyInstructionTag() and
|
||||
type = getUnknownType()
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
if expr.getTarget() instanceof AllocationFunction
|
||||
then result = getInstruction(OnlyInstructionTag())
|
||||
else result = getChild(0).getFirstInstruction()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = OnlyInstructionTag() and
|
||||
kind = gotoEdge() and
|
||||
expr.getTarget() instanceof AllocationFunction and
|
||||
if exists(getChild(0))
|
||||
then result = getChild(0).getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
@@ -371,23 +379,34 @@ class TranslatedSideEffects extends TranslatedElement, TTranslatedSideEffects {
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getTranslatedExpr(expr).getInstruction(CallTag())
|
||||
if expr instanceof NewOrNewArrayExpr
|
||||
then result = getTranslatedAllocatorCall(expr).getInstruction(CallTag())
|
||||
else result = getTranslatedExpr(expr).getInstruction(CallTag())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedFunction` containing this expression.
|
||||
*/
|
||||
final TranslatedFunction getEnclosingFunction() {
|
||||
result = getTranslatedFunction(expr.getEnclosingFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `Function` containing this expression.
|
||||
*/
|
||||
override Function getFunction() { result = expr.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
class TranslatedStructorCallSideEffects extends TranslatedSideEffects {
|
||||
class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSideEffects {
|
||||
Call expr;
|
||||
|
||||
TranslatedCallSideEffects() { this = TTranslatedCallSideEffects(expr) }
|
||||
|
||||
override string toString() { result = "(side effects for " + expr.toString() + ")" }
|
||||
|
||||
override Call getExpr() { result = expr }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { none() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = getChild(0).getFirstInstruction() }
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getTranslatedExpr(expr).getInstruction(CallTag())
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedStructorCallSideEffects extends TranslatedCallSideEffects {
|
||||
TranslatedStructorCallSideEffects() { getParent().(TranslatedStructorCall).hasQualifier() }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType t) {
|
||||
|
||||
@@ -6,6 +6,7 @@ private import semmle.code.cpp.ir.internal.IRUtilities
|
||||
private import InstructionTag
|
||||
private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedFunction
|
||||
private import TranslatedInitialization
|
||||
|
||||
/**
|
||||
@@ -66,17 +67,172 @@ abstract class TranslatedLocalVariableDeclaration extends TranslatedVariableInit
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a local variable declaration within a declaration statement.
|
||||
* The IR translation of a local variable declaration within a declaration statement.
|
||||
*/
|
||||
class TranslatedVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
|
||||
class TranslatedAutoVariableDeclarationEntry extends TranslatedLocalVariableDeclaration,
|
||||
TranslatedDeclarationEntry {
|
||||
LocalVariable var;
|
||||
StackVariable var;
|
||||
|
||||
TranslatedVariableDeclarationEntry() { var = entry.getDeclaration() }
|
||||
TranslatedAutoVariableDeclarationEntry() { var = entry.getDeclaration() }
|
||||
|
||||
override LocalVariable getVariable() { result = var }
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the declaration of a static local variable.
|
||||
* This element generates the logic that determines whether or not the variable has already been
|
||||
* initialized, and if not, invokes the initializer and sets the dynamic initialization flag for the
|
||||
* variable. The actual initialization code is handled in
|
||||
* `TranslatedStaticLocalVariableInitialization`, which is a child of this element.
|
||||
*
|
||||
* The generated code to do the initialization only once is:
|
||||
* ```
|
||||
* Block 1
|
||||
* r1225_1(glval<bool>) = VariableAddress[c#init] :
|
||||
* r1225_2(bool) = Load : &:r1225_1, ~mu1222_4
|
||||
* v1225_3(void) = ConditionalBranch : r1225_2
|
||||
* False -> Block 2
|
||||
* True -> Block 3
|
||||
*
|
||||
* Block 2
|
||||
* r1225_4(glval<int>) = VariableAddress[c] :
|
||||
* <actual initialization of `c`>
|
||||
* r1225_8(bool) = Constant[1] :
|
||||
* mu1225_9(bool) = Store : &:r1225_1, r1225_8
|
||||
* Goto -> Block 3
|
||||
*
|
||||
* Block 3
|
||||
* ```
|
||||
*
|
||||
* Note that the flag variable, `c#init`, is assumed to be zero-initialized at program startup, just
|
||||
* like any other variable with static storage duration.
|
||||
*/
|
||||
class TranslatedStaticLocalVariableDeclarationEntry extends TranslatedDeclarationEntry {
|
||||
StaticLocalVariable var;
|
||||
|
||||
TranslatedStaticLocalVariableDeclarationEntry() { var = entry.getDeclaration() }
|
||||
|
||||
final override TranslatedElement getChild(int id) { id = 0 and result = getInitialization() }
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
||||
tag = DynamicInitializationFlagAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
type = getBoolGLValueType()
|
||||
or
|
||||
tag = DynamicInitializationFlagLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
type = getBoolType()
|
||||
or
|
||||
tag = DynamicInitializationConditionalBranchTag() and
|
||||
opcode instanceof Opcode::ConditionalBranch and
|
||||
type = getVoidType()
|
||||
or
|
||||
tag = DynamicInitializationFlagConstantTag() and
|
||||
opcode instanceof Opcode::Constant and
|
||||
type = getBoolType()
|
||||
or
|
||||
tag = DynamicInitializationFlagStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
type = getBoolType()
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
result = getInstruction(DynamicInitializationFlagAddressTag())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = DynamicInitializationFlagAddressTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getInstruction(DynamicInitializationFlagLoadTag())
|
||||
or
|
||||
tag = DynamicInitializationFlagLoadTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getInstruction(DynamicInitializationConditionalBranchTag())
|
||||
or
|
||||
tag = DynamicInitializationConditionalBranchTag() and
|
||||
(
|
||||
kind instanceof TrueEdge and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
or
|
||||
kind instanceof FalseEdge and
|
||||
result = getInitialization().getFirstInstruction()
|
||||
)
|
||||
or
|
||||
tag = DynamicInitializationFlagConstantTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getInstruction(DynamicInitializationFlagStoreTag())
|
||||
or
|
||||
tag = DynamicInitializationFlagStoreTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getInitialization() and
|
||||
result = getInstruction(DynamicInitializationFlagConstantTag())
|
||||
}
|
||||
|
||||
final override IRDynamicInitializationFlag getInstructionVariable(InstructionTag tag) {
|
||||
tag = DynamicInitializationFlagAddressTag() and
|
||||
result.getVariable() = var
|
||||
}
|
||||
|
||||
final override string getInstructionConstantValue(InstructionTag tag) {
|
||||
tag = DynamicInitializationFlagConstantTag() and result = "1"
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = DynamicInitializationFlagLoadTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(DynamicInitializationFlagAddressTag())
|
||||
or
|
||||
operandTag instanceof LoadOperandTag and
|
||||
result = getTranslatedFunction(var.getFunction()).getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
or
|
||||
tag = DynamicInitializationConditionalBranchTag() and
|
||||
operandTag instanceof ConditionOperandTag and
|
||||
result = getInstruction(DynamicInitializationFlagLoadTag())
|
||||
or
|
||||
tag = DynamicInitializationFlagStoreTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getInstruction(DynamicInitializationFlagAddressTag())
|
||||
or
|
||||
operandTag instanceof StoreValueOperandTag and
|
||||
result = getInstruction(DynamicInitializationFlagConstantTag())
|
||||
)
|
||||
}
|
||||
|
||||
private TranslatedStaticLocalVariableInitialization getInitialization() {
|
||||
result.getVariable() = var
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The initialization of a static local variable. This element will only exist for a static variable
|
||||
* with a dynamic initializer.
|
||||
*/
|
||||
class TranslatedStaticLocalVariableInitialization extends TranslatedElement,
|
||||
TranslatedLocalVariableDeclaration, TTranslatedStaticLocalVariableInitialization {
|
||||
VariableDeclarationEntry entry;
|
||||
StaticLocalVariable var;
|
||||
|
||||
TranslatedStaticLocalVariableInitialization() {
|
||||
this = TTranslatedStaticLocalVariableInitialization(entry) and
|
||||
var = entry.getDeclaration()
|
||||
}
|
||||
|
||||
final override string toString() { result = "init: " + entry.toString() }
|
||||
|
||||
final override Locatable getAST() { result = entry }
|
||||
|
||||
final override LocalVariable getVariable() { result = var }
|
||||
|
||||
final override Function getFunction() { result = var.getFunction() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedRangeBasedForVariableDeclaration` that represents the declaration of
|
||||
* `var`.
|
||||
|
||||
@@ -50,6 +50,14 @@ private predicate ignoreExprAndDescendants(Expr expr) {
|
||||
// constant value.
|
||||
isIRConstant(getRealParent(expr))
|
||||
or
|
||||
// Only translate the initializer of a static local if it uses run-time data.
|
||||
// Otherwise the initializer does not run in function scope.
|
||||
exists(Initializer init, StaticStorageDurationVariable var |
|
||||
init = var.getInitializer() and
|
||||
not var.hasDynamicInitialization() and
|
||||
expr = init.getExpr().getFullyConverted()
|
||||
)
|
||||
or
|
||||
// Ignore descendants of `__assume` expressions, since we translated these to `NoOp`.
|
||||
getRealParent(expr) instanceof AssumeExpr
|
||||
or
|
||||
@@ -75,6 +83,10 @@ private predicate ignoreExprAndDescendants(Expr expr) {
|
||||
exists(DeleteExpr deleteExpr | deleteExpr.getAllocatorCall() = expr)
|
||||
or
|
||||
exists(DeleteArrayExpr deleteArrayExpr | deleteArrayExpr.getAllocatorCall() = expr)
|
||||
or
|
||||
exists(BuiltInVarArgsStart vaStartExpr |
|
||||
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,6 +260,26 @@ predicate hasTranslatedLoad(Expr expr) {
|
||||
not ignoreLoad(expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the specified `DeclarationEntry` needs an IR translation. An IR translation is only
|
||||
* necessary for automatic local variables, or for static local variables with dynamic
|
||||
* initialization.
|
||||
*/
|
||||
private predicate translateDeclarationEntry(DeclarationEntry entry) {
|
||||
exists(DeclStmt declStmt, LocalVariable var |
|
||||
translateStmt(declStmt) and
|
||||
declStmt.getADeclarationEntry() = entry and
|
||||
// Only declarations of local variables need to be translated to IR.
|
||||
var = entry.getDeclaration() and
|
||||
(
|
||||
not var.isStatic()
|
||||
or
|
||||
// Ignore static variables unless they have a dynamic initializer.
|
||||
var.(StaticLocalVariable).hasDynamicInitialization()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
newtype TTranslatedElement =
|
||||
// An expression that is not being consumed as a condition
|
||||
TTranslatedValueExpr(Expr expr) {
|
||||
@@ -374,6 +406,7 @@ newtype TTranslatedElement =
|
||||
translateFunction(func)
|
||||
)
|
||||
} or
|
||||
TTranslatedEllipsisParameter(Function func) { translateFunction(func) and func.isVarargs() } or
|
||||
TTranslatedReadEffects(Function func) { translateFunction(func) } or
|
||||
// The read side effects in a function's return block
|
||||
TTranslatedReadEffect(Parameter param) {
|
||||
@@ -385,13 +418,12 @@ newtype TTranslatedElement =
|
||||
)
|
||||
} or
|
||||
// A local declaration
|
||||
TTranslatedDeclarationEntry(DeclarationEntry entry) {
|
||||
exists(DeclStmt declStmt |
|
||||
translateStmt(declStmt) and
|
||||
declStmt.getADeclarationEntry() = entry and
|
||||
// Only declarations of local variables need to be translated to IR.
|
||||
entry.getDeclaration() instanceof LocalVariable
|
||||
)
|
||||
TTranslatedDeclarationEntry(DeclarationEntry entry) { translateDeclarationEntry(entry) } or
|
||||
// The dynamic initialization of a static local variable. This is a separate object from the
|
||||
// declaration entry.
|
||||
TTranslatedStaticLocalVariableInitialization(DeclarationEntry entry) {
|
||||
translateDeclarationEntry(entry) and
|
||||
entry.getDeclaration() instanceof StaticLocalVariable
|
||||
} or
|
||||
// A compiler-generated variable to implement a range-based for loop. These don't have a
|
||||
// `DeclarationEntry` in the database, so we have to go by the `Variable` itself.
|
||||
@@ -410,11 +442,22 @@ newtype TTranslatedElement =
|
||||
// The declaration/initialization part of a `ConditionDeclExpr`
|
||||
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
|
||||
// The side effects of a `Call`
|
||||
TTranslatedSideEffects(Call expr) {
|
||||
exists(TTranslatedArgumentSideEffect(expr, _, _, _)) or
|
||||
expr instanceof ConstructorCall or
|
||||
expr.getTarget() instanceof AllocationFunction
|
||||
} or // A precise side effect of an argument to a `Call`
|
||||
TTranslatedCallSideEffects(Call expr) {
|
||||
// Exclude allocations such as `malloc` (which happen to also be function calls).
|
||||
// Both `TranslatedCallSideEffects` and `TranslatedAllocationSideEffects` generate
|
||||
// the same side effects for its children as they both extend the `TranslatedSideEffects`
|
||||
// class.
|
||||
// Note: We can separate allocation side effects and call side effects into two
|
||||
// translated elements as no call can be both a `ConstructorCall` and an `AllocationExpr`.
|
||||
not expr instanceof AllocationExpr and
|
||||
(
|
||||
exists(TTranslatedArgumentSideEffect(expr, _, _, _)) or
|
||||
expr instanceof ConstructorCall
|
||||
)
|
||||
} or
|
||||
// The side effects of an allocation, i.e. `new`, `new[]` or `malloc`
|
||||
TTranslatedAllocationSideEffects(AllocationExpr expr) or
|
||||
// A precise side effect of an argument to a `Call`
|
||||
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, boolean isWrite) {
|
||||
(
|
||||
expr = call.getArgument(n).getFullyConverted()
|
||||
|
||||
@@ -1649,6 +1649,11 @@ class TranslatedAllocatorCall extends TTranslatedAllocatorCall, TranslatedDirect
|
||||
|
||||
final override int getNumberOfArguments() {
|
||||
result = expr.getAllocatorCall().getNumberOfArguments()
|
||||
or
|
||||
// Make sure there's a result even when there is no allocator, as otherwise
|
||||
// TranslatedCall::getChild() will not return the side effects for this call.
|
||||
not exists(expr.getAllocatorCall()) and
|
||||
result = 0
|
||||
}
|
||||
|
||||
final override TranslatedExpr getArgument(int index) {
|
||||
@@ -2020,7 +2025,12 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
|
||||
override BuiltInOperation expr;
|
||||
|
||||
TranslatedBuiltInOperation() {
|
||||
not expr instanceof BuiltInOperationBuiltInAddressOf // Handled specially
|
||||
// The following expressions are handled specially.
|
||||
not expr instanceof BuiltInOperationBuiltInAddressOf and
|
||||
not expr instanceof BuiltInVarArgsStart and
|
||||
not expr instanceof BuiltInVarArg and
|
||||
not expr instanceof BuiltInVarArgsEnd and
|
||||
not expr instanceof BuiltInVarArgCopy
|
||||
}
|
||||
|
||||
final override Instruction getResult() { result = getInstruction(OnlyInstructionTag()) }
|
||||
@@ -2075,39 +2085,318 @@ class TranslatedBuiltInOperation extends TranslatedNonConstantExpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a `BuiltInVarArgsStart` expression.
|
||||
* Holds if the expression `expr` is one of the `va_list` operands to a `va_*` macro.
|
||||
*/
|
||||
class TranslatedVarArgsStart extends TranslatedBuiltInOperation {
|
||||
override BuiltInVarArgsStart expr;
|
||||
|
||||
final override Opcode getOpcode() { result instanceof Opcode::VarArgsStart }
|
||||
private predicate isVAListExpr(Expr expr) {
|
||||
exists(VarArgsExpr parent, Expr originalExpr |
|
||||
(
|
||||
originalExpr = parent.(BuiltInVarArgsStart).getVAList()
|
||||
or
|
||||
originalExpr = parent.(BuiltInVarArgsEnd).getVAList()
|
||||
or
|
||||
originalExpr = parent.(BuiltInVarArg).getVAList()
|
||||
or
|
||||
originalExpr = parent.(BuiltInVarArgCopy).getSourceVAList()
|
||||
or
|
||||
originalExpr = parent.(BuiltInVarArgCopy).getDestinationVAList()
|
||||
) and
|
||||
expr = originalExpr.getFullyConverted()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a `BuiltInVarArgsEnd` expression.
|
||||
* Gets the type of the `va_list` being accessed by `expr`, where `expr` is a `va_list` operand of a
|
||||
* `va_*` macro.
|
||||
*
|
||||
* In the Unix ABI, `va_list` is declared as `typedef struct __va_list_tag va_list[1];`. When used
|
||||
* as the type of a local variable, this gets an implicit array-to-pointer conversion, so that the
|
||||
* actual argument to the `va_*` macro is a prvalue of type `__va_list_tag*`. When used as the type
|
||||
* of a function parameter, the parameter's type decays to `__va_list_tag*`, so that the argument
|
||||
* to the `va_*` macro is still a prvalue of type `__va_list_tag*`, with no implicit conversion
|
||||
* necessary. In either case, we treat `__va_list_tag` as the representative type of the `va_list`.
|
||||
*
|
||||
* In the Windows ABI, `va_list` is declared as a pointer type (usually `char*`). Whether used as
|
||||
* the type of a local variable or of a parameter, this means that the argument to the `va_*` macro
|
||||
* is always an _lvalue_ of type `char*`. We treat `char*` as the representative type of the
|
||||
* `va_list`.
|
||||
*/
|
||||
class TranslatedVarArgsEnd extends TranslatedBuiltInOperation {
|
||||
override BuiltInVarArgsEnd expr;
|
||||
private Type getVAListType(Expr expr) {
|
||||
isVAListExpr(expr) and
|
||||
if expr.isPRValueCategory()
|
||||
then
|
||||
// In the Unix ABI, this will be a prvalue of type `__va_list_tag*`. We want the `__va_list_tag`
|
||||
// type.
|
||||
result = expr.getType().getUnderlyingType().(PointerType).getBaseType()
|
||||
else
|
||||
// In the Windows ABI, this will be an lvalue of some pointer type. We want that pointer type.
|
||||
result = expr.getType()
|
||||
}
|
||||
|
||||
final override Opcode getOpcode() { result instanceof Opcode::VarArgsEnd }
|
||||
/**
|
||||
* The IR translation of a `BuiltInVarArgsStart` expression.
|
||||
*/
|
||||
class TranslatedVarArgsStart extends TranslatedNonConstantExpr {
|
||||
override BuiltInVarArgsStart expr;
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = VarArgsStartEllipsisAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getEllipsisVariableGLValueType()
|
||||
or
|
||||
tag = VarArgsStartTag() and
|
||||
opcode instanceof Opcode::VarArgsStart and
|
||||
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
|
||||
or
|
||||
tag = VarArgsVAListStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
result = getInstruction(VarArgsStartEllipsisAddressTag())
|
||||
}
|
||||
|
||||
final override Instruction getResult() { none() }
|
||||
|
||||
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
|
||||
|
||||
private TranslatedExpr getVAList() {
|
||||
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = VarArgsStartEllipsisAddressTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getInstruction(VarArgsStartTag())
|
||||
or
|
||||
tag = VarArgsStartTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getVAList().getFirstInstruction()
|
||||
or
|
||||
tag = VarArgsVAListStoreTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getVAList() and
|
||||
result = getInstruction(VarArgsVAListStoreTag())
|
||||
}
|
||||
|
||||
final override IRVariable getInstructionVariable(InstructionTag tag) {
|
||||
tag = VarArgsStartEllipsisAddressTag() and
|
||||
result = getEnclosingFunction().getEllipsisVariable()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = VarArgsStartTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getInstruction(VarArgsStartEllipsisAddressTag())
|
||||
or
|
||||
tag = VarArgsVAListStoreTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and result = getVAList().getResult()
|
||||
or
|
||||
operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsStartTag())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a `BuiltInVarArg` expression.
|
||||
*/
|
||||
class TranslatedVarArg extends TranslatedBuiltInOperation {
|
||||
class TranslatedVarArg extends TranslatedNonConstantExpr {
|
||||
override BuiltInVarArg expr;
|
||||
|
||||
final override Opcode getOpcode() { result instanceof Opcode::VarArg }
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = VarArgsVAListLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
|
||||
or
|
||||
tag = VarArgsArgAddressTag() and
|
||||
opcode instanceof Opcode::VarArg and
|
||||
resultType = getResultType()
|
||||
or
|
||||
tag = VarArgsMoveNextTag() and
|
||||
opcode instanceof Opcode::NextVarArg and
|
||||
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
|
||||
or
|
||||
tag = VarArgsVAListStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getTypeForPRValue(getVAListType(expr.getVAList().getFullyConverted()))
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() { result = getVAList().getFirstInstruction() }
|
||||
|
||||
final override Instruction getResult() { result = getInstruction(VarArgsArgAddressTag()) }
|
||||
|
||||
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
|
||||
|
||||
private TranslatedExpr getVAList() {
|
||||
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = VarArgsVAListLoadTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getInstruction(VarArgsArgAddressTag())
|
||||
or
|
||||
tag = VarArgsArgAddressTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getInstruction(VarArgsMoveNextTag())
|
||||
or
|
||||
tag = VarArgsMoveNextTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getInstruction(VarArgsVAListStoreTag())
|
||||
or
|
||||
tag = VarArgsVAListStoreTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getVAList() and
|
||||
result = getInstruction(VarArgsVAListLoadTag())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = VarArgsVAListLoadTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getVAList().getResult()
|
||||
or
|
||||
operandTag instanceof LoadOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
or
|
||||
tag = VarArgsArgAddressTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getInstruction(VarArgsVAListLoadTag())
|
||||
or
|
||||
tag = VarArgsMoveNextTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getInstruction(VarArgsVAListLoadTag())
|
||||
or
|
||||
tag = VarArgsVAListStoreTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and result = getVAList().getResult()
|
||||
or
|
||||
operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsMoveNextTag())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a `BuiltInVarArgsEnd` expression.
|
||||
*/
|
||||
class TranslatedVarArgsEnd extends TranslatedNonConstantExpr {
|
||||
override BuiltInVarArgsEnd expr;
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::VarArgsEnd and
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() { result = getVAList().getFirstInstruction() }
|
||||
|
||||
final override Instruction getResult() { none() }
|
||||
|
||||
final override TranslatedElement getChild(int id) { id = 0 and result = getVAList() }
|
||||
|
||||
private TranslatedExpr getVAList() {
|
||||
result = getTranslatedExpr(expr.getVAList().getFullyConverted())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = OnlyInstructionTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getVAList() and
|
||||
result = getInstruction(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = getVAList().getResult()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a `BuiltInVarArgCopy` expression.
|
||||
*/
|
||||
class TranslatedVarArgCopy extends TranslatedBuiltInOperation {
|
||||
class TranslatedVarArgCopy extends TranslatedNonConstantExpr {
|
||||
override BuiltInVarArgCopy expr;
|
||||
|
||||
final override Opcode getOpcode() { result instanceof Opcode::VarArgCopy }
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = VarArgsVAListLoadTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getTypeForPRValue(getVAListType(expr.getSourceVAList().getFullyConverted()))
|
||||
or
|
||||
tag = VarArgsVAListStoreTag() and
|
||||
opcode instanceof Opcode::Store and
|
||||
resultType = getTypeForPRValue(getVAListType(expr.getDestinationVAList().getFullyConverted()))
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
result = getSourceVAList().getFirstInstruction()
|
||||
}
|
||||
|
||||
final override Instruction getResult() { result = getInstruction(VarArgsVAListStoreTag()) }
|
||||
|
||||
final override TranslatedElement getChild(int id) {
|
||||
id = 0 and result = getDestinationVAList()
|
||||
or
|
||||
id = 1 and result = getSourceVAList()
|
||||
}
|
||||
|
||||
private TranslatedExpr getDestinationVAList() {
|
||||
result = getTranslatedExpr(expr.getDestinationVAList().getFullyConverted())
|
||||
}
|
||||
|
||||
private TranslatedExpr getSourceVAList() {
|
||||
result = getTranslatedExpr(expr.getSourceVAList().getFullyConverted())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = VarArgsVAListLoadTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getDestinationVAList().getFirstInstruction()
|
||||
or
|
||||
tag = VarArgsVAListStoreTag() and
|
||||
kind instanceof GotoEdge and
|
||||
result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = getSourceVAList() and
|
||||
result = getInstruction(VarArgsVAListLoadTag())
|
||||
or
|
||||
child = getDestinationVAList() and
|
||||
result = getInstruction(VarArgsVAListStoreTag())
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = VarArgsVAListLoadTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getSourceVAList().getResult()
|
||||
or
|
||||
operandTag instanceof LoadOperandTag and
|
||||
result = getEnclosingFunction().getUnmodeledDefinitionInstruction()
|
||||
)
|
||||
or
|
||||
tag = VarArgsVAListStoreTag() and
|
||||
(
|
||||
operandTag instanceof AddressOperandTag and result = getDestinationVAList().getResult()
|
||||
or
|
||||
operandTag instanceof StoreValueOperandTag and result = getInstruction(VarArgsVAListLoadTag())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,12 +10,45 @@ private import TranslatedElement
|
||||
private import TranslatedExpr
|
||||
private import TranslatedInitialization
|
||||
private import TranslatedStmt
|
||||
private import VarArgs
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedFunction` that represents function `func`.
|
||||
*/
|
||||
TranslatedFunction getTranslatedFunction(Function func) { result.getAST() = func }
|
||||
|
||||
/**
|
||||
* Gets the size, in bytes, of the variable used to represent the `...` parameter in a varargs
|
||||
* function. This is determined by finding the total size of all of the arguments passed to the
|
||||
* `...` in each call in the program, and choosing the maximum of those, with a minimum of 8 bytes.
|
||||
*/
|
||||
private int getEllipsisVariableByteSize() {
|
||||
result =
|
||||
max(int variableSize |
|
||||
variableSize =
|
||||
max(Call call, int callSize |
|
||||
callSize =
|
||||
sum(int argIndex |
|
||||
isEllipsisArgumentIndex(call, argIndex)
|
||||
|
|
||||
call.getArgument(argIndex).getType().getSize()
|
||||
)
|
||||
|
|
||||
callSize
|
||||
)
|
||||
or
|
||||
variableSize = 8
|
||||
|
|
||||
variableSize
|
||||
)
|
||||
}
|
||||
|
||||
CppType getEllipsisVariablePRValueType() {
|
||||
result = getUnknownOpaqueType(getEllipsisVariableByteSize())
|
||||
}
|
||||
|
||||
CppType getEllipsisVariableGLValueType() { result = getTypeForGLValue(any(UnknownType t)) }
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a function. This is the root elements for
|
||||
* all other elements associated with this function.
|
||||
@@ -60,6 +93,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
|
||||
final private TranslatedParameter getParameter(int index) {
|
||||
result = getTranslatedParameter(func.getParameter(index))
|
||||
or
|
||||
index = getEllipsisParameterIndexForFunction(func) and
|
||||
result = getTranslatedEllipsisParameter(func)
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() { result = getInstruction(EnterFunctionTag()) }
|
||||
@@ -113,7 +149,9 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
exists(int paramIndex |
|
||||
child = getParameter(paramIndex) and
|
||||
if exists(func.getParameter(paramIndex + 1))
|
||||
if
|
||||
exists(func.getParameter(paramIndex + 1)) or
|
||||
getEllipsisParameterIndexForFunction(func) = paramIndex + 1
|
||||
then result = getParameter(paramIndex + 1).getFirstInstruction()
|
||||
else result = getConstructorInitList().getFirstInstruction()
|
||||
)
|
||||
@@ -237,10 +275,18 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
result = getReturnVariable()
|
||||
}
|
||||
|
||||
final override predicate needsUnknownOpaqueType(int byteSize) {
|
||||
byteSize = getEllipsisVariableByteSize()
|
||||
}
|
||||
|
||||
final override predicate hasTempVariable(TempVariableTag tag, CppType type) {
|
||||
tag = ReturnValueTempVar() and
|
||||
hasReturnValue() and
|
||||
type = getTypeForPRValue(getReturnType())
|
||||
or
|
||||
tag = EllipsisTempVar() and
|
||||
func.isVarargs() and
|
||||
type = getEllipsisVariablePRValueType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,6 +304,11 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
result = getIRTempVariable(func, ReturnValueTempVar())
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the variable that represents the `...` parameter, if any.
|
||||
*/
|
||||
final IREllipsisVariable getEllipsisVariable() { result.getEnclosingFunction() = func }
|
||||
|
||||
/**
|
||||
* Holds if the function has a non-`void` return type.
|
||||
*/
|
||||
@@ -316,34 +367,29 @@ class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `TranslatedParameter` that represents parameter `param`.
|
||||
* Gets the `TranslatedPositionalParameter` that represents parameter `param`.
|
||||
*/
|
||||
TranslatedParameter getTranslatedParameter(Parameter param) { result.getAST() = param }
|
||||
TranslatedPositionalParameter getTranslatedParameter(Parameter param) { result.getAST() = param }
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a function parameter, including the
|
||||
* initialization of that parameter with the incoming argument.
|
||||
* Gets the `TranslatedEllipsisParameter` for function `func`, if one exists.
|
||||
*/
|
||||
class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
|
||||
Parameter param;
|
||||
TranslatedEllipsisParameter getTranslatedEllipsisParameter(Function func) {
|
||||
result.getFunction() = func
|
||||
}
|
||||
|
||||
TranslatedParameter() { this = TTranslatedParameter(param) }
|
||||
|
||||
final override string toString() { result = param.toString() }
|
||||
|
||||
final override Locatable getAST() { result = param }
|
||||
|
||||
final override Function getFunction() {
|
||||
result = param.getFunction() or
|
||||
result = param.getCatchBlock().getEnclosingFunction()
|
||||
}
|
||||
/**
|
||||
* 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`).
|
||||
*/
|
||||
abstract class TranslatedParameter extends TranslatedElement {
|
||||
final override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
result = getInstruction(InitializerVariableAddressTag())
|
||||
}
|
||||
|
||||
final override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
@@ -368,16 +414,16 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = InitializerVariableAddressTag() and
|
||||
opcode instanceof Opcode::VariableAddress and
|
||||
resultType = getTypeForGLValue(getVariableType(param))
|
||||
resultType = getGLValueType()
|
||||
or
|
||||
tag = InitializerStoreTag() and
|
||||
opcode instanceof Opcode::InitializeParameter and
|
||||
resultType = getTypeForPRValue(getVariableType(param))
|
||||
resultType = getPRValueType()
|
||||
or
|
||||
hasIndirection() and
|
||||
tag = InitializerIndirectAddressTag() and
|
||||
opcode instanceof Opcode::Load and
|
||||
resultType = getTypeForPRValue(getVariableType(param))
|
||||
resultType = getPRValueType()
|
||||
or
|
||||
hasIndirection() and
|
||||
tag = InitializerIndirectStoreTag() and
|
||||
@@ -391,7 +437,7 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
|
||||
tag = InitializerVariableAddressTag() or
|
||||
tag = InitializerIndirectStoreTag()
|
||||
) and
|
||||
result = getIRUserVariable(getFunction(), param)
|
||||
result = getIRVariable()
|
||||
}
|
||||
|
||||
final override Instruction getInstructionOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -416,13 +462,74 @@ class TranslatedParameter extends TranslatedElement, TTranslatedParameter {
|
||||
result = getInstruction(InitializerIndirectAddressTag())
|
||||
}
|
||||
|
||||
predicate hasIndirection() {
|
||||
abstract predicate hasIndirection();
|
||||
|
||||
abstract CppType getGLValueType();
|
||||
|
||||
abstract CppType getPRValueType();
|
||||
|
||||
abstract IRAutomaticVariable getIRVariable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the IR translation of a function parameter, including the
|
||||
* initialization of that parameter with the incoming argument.
|
||||
*/
|
||||
class TranslatedPositionalParameter extends TranslatedParameter, TTranslatedParameter {
|
||||
Parameter param;
|
||||
|
||||
TranslatedPositionalParameter() { this = TTranslatedParameter(param) }
|
||||
|
||||
final override string toString() { result = param.toString() }
|
||||
|
||||
final override Locatable getAST() { result = param }
|
||||
|
||||
final override Function getFunction() {
|
||||
result = param.getFunction() or
|
||||
result = param.getCatchBlock().getEnclosingFunction()
|
||||
}
|
||||
|
||||
final override predicate hasIndirection() {
|
||||
exists(Type t | t = param.getUnspecifiedType() |
|
||||
t instanceof ArrayType or
|
||||
t instanceof PointerType or
|
||||
t instanceof ReferenceType
|
||||
)
|
||||
}
|
||||
|
||||
final override CppType getGLValueType() { result = getTypeForGLValue(getVariableType(param)) }
|
||||
|
||||
final override CppType getPRValueType() { result = getTypeForPRValue(getVariableType(param)) }
|
||||
|
||||
final override IRAutomaticUserVariable getIRVariable() {
|
||||
result = getIRUserVariable(getFunction(), param)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the synthesized parameter used to represent the `...` in a varargs
|
||||
* function.
|
||||
*/
|
||||
class TranslatedEllipsisParameter extends TranslatedParameter, TTranslatedEllipsisParameter {
|
||||
Function func;
|
||||
|
||||
TranslatedEllipsisParameter() { this = TTranslatedEllipsisParameter(func) }
|
||||
|
||||
final override string toString() { result = "..." }
|
||||
|
||||
final override Locatable getAST() { result = func }
|
||||
|
||||
final override Function getFunction() { result = func }
|
||||
|
||||
final override predicate hasIndirection() { any() }
|
||||
|
||||
final override CppType getGLValueType() { result = getEllipsisVariableGLValueType() }
|
||||
|
||||
final override CppType getPRValueType() { result = getEllipsisVariablePRValueType() }
|
||||
|
||||
final override IREllipsisVariable getIRVariable() {
|
||||
result = getTranslatedFunction(func).getEllipsisVariable()
|
||||
}
|
||||
}
|
||||
|
||||
private TranslatedConstructorInitList getTranslatedConstructorInitList(Function func) {
|
||||
|
||||
@@ -115,10 +115,14 @@ abstract class TranslatedVariableInitialization extends TranslatedElement, Initi
|
||||
* evaluating the initializer.
|
||||
*/
|
||||
final predicate hasUninitializedInstruction() {
|
||||
not exists(getInitialization()) or
|
||||
getInitialization() instanceof TranslatedListInitialization or
|
||||
getInitialization() instanceof TranslatedConstructorInitialization or
|
||||
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
|
||||
(
|
||||
not exists(getInitialization()) or
|
||||
getInitialization() instanceof TranslatedListInitialization or
|
||||
getInitialization() instanceof TranslatedConstructorInitialization or
|
||||
getInitialization().(TranslatedStringLiteralInitialization).zeroInitRange(_, _)
|
||||
) and
|
||||
// Variables with static or thread-local storage duration are zero-initialized at program startup.
|
||||
getIRVariable() instanceof IRAutomaticVariable
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Utilities for determining which parameters and arguments correspond to the `...` parameter for
|
||||
* varargs functions.
|
||||
*/
|
||||
|
||||
private import cpp
|
||||
|
||||
/**
|
||||
* Gets the index of the `...` parameter, if any. If present, the value will always be equal to
|
||||
* `func.getNumberOfParameters()`.
|
||||
*/
|
||||
int getEllipsisParameterIndexForFunction(Function func) {
|
||||
func.isVarargs() and result = func.getNumberOfParameters()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the `...` parameter, if any.
|
||||
*/
|
||||
int getEllipsisParameterIndexForRoutineType(RoutineType type) {
|
||||
// Since the extractor doesn't record this information directly, we look for routine types whose
|
||||
// last parameter type is `UnknownType`.
|
||||
type.getParameterType(result) instanceof UnknownType and
|
||||
result = strictcount(type.getAParameterType()) - 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the `...` parameter, if any. This will be one greater than the index of the
|
||||
* last declared positional parameter.
|
||||
*/
|
||||
int getEllipsisParameterIndex(Call call) {
|
||||
exists(FunctionCall funcCall |
|
||||
funcCall = call and
|
||||
if funcCall.getTargetType() instanceof RoutineType
|
||||
then result = getEllipsisParameterIndexForRoutineType(funcCall.getTargetType())
|
||||
else result = getEllipsisParameterIndexForFunction(funcCall.getTarget())
|
||||
)
|
||||
or
|
||||
exists(ExprCall exprCall |
|
||||
exprCall = call and
|
||||
result = getEllipsisParameterIndexForRoutineType(exprCall.getExpr().getType().stripType())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that will be initialized with the value of the argument
|
||||
* specified by `argIndex`. For ordinary positional parameters, the argument and parameter indices
|
||||
* will be equal. For a call to a varargs function, all arguments passed to the `...` will be
|
||||
* mapped to the index returned by `getEllipsisParameterIndex()`.
|
||||
*/
|
||||
int getParameterIndexForArgument(Call call, int argIndex) {
|
||||
exists(call.getArgument(argIndex)) and
|
||||
if argIndex >= getEllipsisParameterIndex(call)
|
||||
then result = getEllipsisParameterIndex(call)
|
||||
else result = argIndex
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the argument specified by `index` is an argument to the `...` of a varargs function.
|
||||
*/
|
||||
predicate isEllipsisArgumentIndex(Call call, int index) {
|
||||
exists(call.getArgument(index)) and index >= getEllipsisParameterIndex(call)
|
||||
}
|
||||
@@ -27,6 +27,9 @@ class IRBlockBase extends TIRBlock {
|
||||
* by debugging and printing code only.
|
||||
*/
|
||||
int getDisplayIndex() {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
) and
|
||||
this =
|
||||
rank[result + 1](IRBlock funcBlock |
|
||||
funcBlock.getEnclosingFunction() = getEnclosingFunction()
|
||||
|
||||
@@ -5,6 +5,7 @@ import IRTypeSanity // module is in IRType.qll
|
||||
module InstructionSanity {
|
||||
private import internal.InstructionImports as Imports
|
||||
private import Imports::OperandTag
|
||||
private import Imports::Overlap
|
||||
private import internal.IRInternal
|
||||
|
||||
/**
|
||||
@@ -272,4 +273,48 @@ module InstructionSanity {
|
||||
func = switchInstr.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is on the chain of chi/phi instructions for all aliased
|
||||
* memory.
|
||||
*/
|
||||
private predicate isOnAliasedDefinitionChain(Instruction instr) {
|
||||
instr instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(ChiInstruction).getTotal())
|
||||
or
|
||||
isOnAliasedDefinitionChain(instr.(PhiInstruction).getAnInputOperand().getAnyDef())
|
||||
}
|
||||
|
||||
private predicate shouldBeConflated(Instruction instr) {
|
||||
isOnAliasedDefinitionChain(instr)
|
||||
or
|
||||
instr instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
instr.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
}
|
||||
|
||||
query predicate notMarkedAsConflated(Instruction instr) {
|
||||
shouldBeConflated(instr) and
|
||||
not instr.isResultConflated()
|
||||
}
|
||||
|
||||
query predicate wronglyMarkedAsConflated(Instruction instr) {
|
||||
instr.isResultConflated() and
|
||||
not shouldBeConflated(instr)
|
||||
}
|
||||
|
||||
query predicate invalidOverlap(
|
||||
MemoryOperand useOperand, string message, IRFunction func, string funcText
|
||||
) {
|
||||
exists(Overlap overlap |
|
||||
overlap = useOperand.getDefinitionOverlap() and
|
||||
overlap instanceof MayPartiallyOverlap and
|
||||
message =
|
||||
"MemoryOperand '" + useOperand.toString() + "' has a `getDefinitionOverlap()` of '" +
|
||||
overlap.toString() + "'." and
|
||||
func = useOperand.getEnclosingIRFunction() and
|
||||
funcText = Language::getIdentityString(func.getFunction())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ class IRVariable extends TIRVariable {
|
||||
IRVariable() {
|
||||
this = TIRUserVariable(_, _, func) or
|
||||
this = TIRTempVariable(func, _, _, _) or
|
||||
this = TIRStringLiteral(func, _, _, _)
|
||||
this = TIRStringLiteral(func, _, _, _) or
|
||||
this = TIRDynamicInitializationFlag(func, _, _)
|
||||
}
|
||||
|
||||
string toString() { none() }
|
||||
@@ -149,7 +150,8 @@ class IRGeneratedVariable extends IRVariable {
|
||||
|
||||
IRGeneratedVariable() {
|
||||
this = TIRTempVariable(func, ast, _, type) or
|
||||
this = TIRStringLiteral(func, ast, type, _)
|
||||
this = TIRStringLiteral(func, ast, type, _) or
|
||||
this = TIRDynamicInitializationFlag(func, ast, type)
|
||||
}
|
||||
|
||||
final override Language::LanguageType getLanguageType() { result = type }
|
||||
@@ -208,7 +210,17 @@ class IRReturnVariable extends IRTempVariable {
|
||||
class IRThrowVariable extends IRTempVariable {
|
||||
IRThrowVariable() { tag = ThrowTempVar() }
|
||||
|
||||
override string getBaseString() { result = "#throw" }
|
||||
final override string getBaseString() { result = "#throw" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary variable generated to hold the contents of all arguments passed to the `...` of a
|
||||
* function that accepts a variable number of arguments.
|
||||
*/
|
||||
class IREllipsisVariable extends IRTempVariable {
|
||||
IREllipsisVariable() { tag = EllipsisTempVar() }
|
||||
|
||||
final override string toString() { result = "#ellipsis" }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +238,30 @@ class IRStringLiteral extends IRGeneratedVariable, TIRStringLiteral {
|
||||
result = "String: " + getLocationString() + "=" + Language::getStringLiteralText(literal)
|
||||
}
|
||||
|
||||
override string getBaseString() { result = "#string" }
|
||||
final override string getBaseString() { result = "#string" }
|
||||
|
||||
final Language::StringLiteral getLiteral() { result = literal }
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable generated to track whether a specific non-stack variable has been initialized. This is
|
||||
* used to model the runtime initialization of static local variables in C++, as well as static
|
||||
* fields in C#.
|
||||
*/
|
||||
class IRDynamicInitializationFlag extends IRGeneratedVariable, TIRDynamicInitializationFlag {
|
||||
Language::Variable var;
|
||||
|
||||
IRDynamicInitializationFlag() {
|
||||
this = TIRDynamicInitializationFlag(func, var, type) and ast = var
|
||||
}
|
||||
|
||||
final override string toString() { result = var.toString() + "#init" }
|
||||
|
||||
final Language::Variable getVariable() { result = var }
|
||||
|
||||
final override string getUniqueId() {
|
||||
result = "Init: " + getVariable().toString() + " " + getVariable().getLocation().toString()
|
||||
}
|
||||
|
||||
final override string getBaseString() { result = "#init:" + var.toString() + ":" }
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@ private import Imports::OperandTag
|
||||
* `File` and line number. Used for assigning register names when printing IR.
|
||||
*/
|
||||
private Instruction getAnInstructionAtLine(IRFunction irFunc, Language::File file, int line) {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(irFunc.getFunction())
|
||||
) and
|
||||
exists(Language::Location location |
|
||||
irFunc = result.getEnclosingIRFunction() and
|
||||
location = result.getLocation() and
|
||||
@@ -39,6 +42,12 @@ class Instruction extends Construction::TInstruction {
|
||||
result = getResultString() + " = " + getOperationString() + " " + getOperandsString()
|
||||
}
|
||||
|
||||
private predicate shouldGenerateDumpStrings() {
|
||||
exists(IRConfiguration::IRConfiguration config |
|
||||
config.shouldEvaluateDebugStringsForFunction(this.getEnclosingFunction())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string describing the operation of this instruction. This includes
|
||||
* the opcode and the immediate value, if any. For example:
|
||||
@@ -46,6 +55,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* VariableAddress[x]
|
||||
*/
|
||||
final string getOperationString() {
|
||||
shouldGenerateDumpStrings() and
|
||||
if exists(getImmediateString())
|
||||
then result = getOperationPrefix() + getOpcode().toString() + "[" + getImmediateString() + "]"
|
||||
else result = getOperationPrefix() + getOpcode().toString()
|
||||
@@ -57,10 +67,12 @@ class Instruction extends Construction::TInstruction {
|
||||
string getImmediateString() { none() }
|
||||
|
||||
private string getOperationPrefix() {
|
||||
shouldGenerateDumpStrings() and
|
||||
if this instanceof SideEffectInstruction then result = "^" else result = ""
|
||||
}
|
||||
|
||||
private string getResultPrefix() {
|
||||
shouldGenerateDumpStrings() and
|
||||
if getResultIRType() instanceof IRVoidType
|
||||
then result = "v"
|
||||
else
|
||||
@@ -74,6 +86,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* used by debugging and printing code only.
|
||||
*/
|
||||
int getDisplayIndexInBlock() {
|
||||
shouldGenerateDumpStrings() and
|
||||
exists(IRBlock block |
|
||||
this = block.getInstruction(result)
|
||||
or
|
||||
@@ -87,6 +100,7 @@ class Instruction extends Construction::TInstruction {
|
||||
}
|
||||
|
||||
private int getLineRank() {
|
||||
shouldGenerateDumpStrings() and
|
||||
this =
|
||||
rank[result](Instruction instr |
|
||||
instr =
|
||||
@@ -105,6 +119,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `r1_1`
|
||||
*/
|
||||
string getResultId() {
|
||||
shouldGenerateDumpStrings() and
|
||||
result = getResultPrefix() + getAST().getLocation().getStartLine() + "_" + getLineRank()
|
||||
}
|
||||
|
||||
@@ -116,6 +131,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `r1_1(int*)`
|
||||
*/
|
||||
final string getResultString() {
|
||||
shouldGenerateDumpStrings() and
|
||||
result = getResultId() + "(" + getResultLanguageType().getDumpString() + ")"
|
||||
}
|
||||
|
||||
@@ -126,6 +142,7 @@ class Instruction extends Construction::TInstruction {
|
||||
* Example: `func:r3_4, this:r3_5`
|
||||
*/
|
||||
string getOperandsString() {
|
||||
shouldGenerateDumpStrings() and
|
||||
result =
|
||||
concat(Operand operand |
|
||||
operand = getAnOperand()
|
||||
@@ -321,6 +338,17 @@ class Instruction extends Construction::TInstruction {
|
||||
Construction::hasModeledMemoryResult(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is an instruction with a memory result that represents a
|
||||
* conflation of more than one memory allocation.
|
||||
*
|
||||
* This happens in practice when dereferencing a pointer that cannot be
|
||||
* tracked back to a single local allocation. Such memory is instead modeled
|
||||
* as originating on the `AliasedDefinitionInstruction` at the entry of the
|
||||
* function.
|
||||
*/
|
||||
final predicate isResultConflated() { Construction::hasConflatedMemoryResult(this) }
|
||||
|
||||
/**
|
||||
* Gets the successor of this instruction along the control flow edge
|
||||
* specified by `kind`.
|
||||
|
||||
@@ -384,6 +384,8 @@ class PositionalArgumentOperand extends ArgumentOperand {
|
||||
|
||||
class SideEffectOperand extends TypedOperand {
|
||||
override SideEffectOperandTag tag;
|
||||
|
||||
override string toString() { result = "SideEffect" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,19 +18,19 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
|
||||
predicate shouldPrintFunction(Language::Function func) { any() }
|
||||
}
|
||||
|
||||
private predicate shouldPrintFunction(Language::Function func) {
|
||||
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
|
||||
}
|
||||
|
||||
/**
|
||||
* Override of `IRConfiguration` to only create IR for the functions that are to be dumped.
|
||||
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
|
||||
*/
|
||||
private class FilteredIRConfiguration extends IRConfiguration {
|
||||
override predicate shouldCreateIRForFunction(Language::Function func) {
|
||||
override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
|
||||
shouldPrintFunction(func)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate shouldPrintFunction(Language::Function func) {
|
||||
exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
|
||||
}
|
||||
|
||||
private string getAdditionalInstructionProperty(Instruction instr, string key) {
|
||||
exists(IRPropertyProvider provider | result = provider.getInstructionProperty(instr, key))
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
import SSAConstruction as Construction
|
||||
import semmle.code.cpp.ir.implementation.IRConfiguration as IRConfiguration
|
||||
|
||||
@@ -3,3 +3,4 @@ import semmle.code.cpp.ir.implementation.IRType as IRType
|
||||
import semmle.code.cpp.ir.implementation.MemoryAccessKind as MemoryAccessKind
|
||||
import semmle.code.cpp.ir.implementation.Opcode as Opcode
|
||||
import semmle.code.cpp.ir.implementation.internal.OperandTag as OperandTag
|
||||
import semmle.code.cpp.ir.internal.Overlap as Overlap
|
||||
|
||||
@@ -65,6 +65,29 @@ private module Cached {
|
||||
instruction instanceof ChiInstruction // Chis always have modeled results
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasConflatedMemoryResult(Instruction instruction) {
|
||||
instruction instanceof UnmodeledDefinitionInstruction
|
||||
or
|
||||
instruction instanceof AliasedDefinitionInstruction
|
||||
or
|
||||
instruction.getOpcode() instanceof Opcode::InitializeNonLocal
|
||||
or
|
||||
// Chi instructions track virtual variables, and therefore a chi instruction is
|
||||
// conflated if it's associated with the aliased virtual variable.
|
||||
exists(OldInstruction oldInstruction | instruction = Chi(oldInstruction) |
|
||||
Alias::getResultMemoryLocation(oldInstruction).getVirtualVariable() instanceof
|
||||
Alias::AliasedVirtualVariable
|
||||
)
|
||||
or
|
||||
// Phi instructions track locations, and therefore a phi instruction is
|
||||
// conflated if it's associated with a conflated location.
|
||||
exists(Alias::MemoryLocation location |
|
||||
instruction = Phi(_, location) and
|
||||
not exists(location.getAllocation())
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
Instruction getRegisterOperandDefinition(Instruction instruction, RegisterOperandTag tag) {
|
||||
exists(OldInstruction oldInstruction, OldIR::RegisterOperand oldOperand |
|
||||
@@ -84,14 +107,15 @@ private module Cached {
|
||||
oldOperand instanceof OldIR::NonPhiMemoryOperand and
|
||||
exists(
|
||||
OldBlock useBlock, int useRank, Alias::MemoryLocation useLocation,
|
||||
Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset
|
||||
Alias::MemoryLocation defLocation, OldBlock defBlock, int defRank, int defOffset,
|
||||
Alias::MemoryLocation actualDefLocation
|
||||
|
|
||||
useLocation = Alias::getOperandMemoryLocation(oldOperand) and
|
||||
hasUseAtRank(useLocation, useBlock, useRank, oldInstruction) and
|
||||
definitionReachesUse(useLocation, defBlock, defRank, useBlock, useRank) and
|
||||
hasDefinitionAtRank(useLocation, defLocation, defBlock, defRank, defOffset) and
|
||||
instr = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, _) and
|
||||
overlap = Alias::getOverlap(defLocation, useLocation)
|
||||
instr = getDefinitionOrChiInstruction(defBlock, defOffset, defLocation, actualDefLocation) and
|
||||
overlap = Alias::getOverlap(actualDefLocation, useLocation)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@ class MemoryLocation extends TMemoryLocation {
|
||||
|
||||
class VirtualVariable extends MemoryLocation { }
|
||||
|
||||
/** A virtual variable that groups all escaped memory within a function. */
|
||||
class AliasedVirtualVariable extends VirtualVariable {
|
||||
AliasedVirtualVariable() { none() }
|
||||
}
|
||||
|
||||
Overlap getOverlap(MemoryLocation def, MemoryLocation use) {
|
||||
def = use and result instanceof MustExactlyOverlap
|
||||
or
|
||||
|
||||
@@ -363,6 +363,11 @@ CppPRValueType getIntType() {
|
||||
*/
|
||||
CppPRValueType getBoolType() { exists(BoolType type | result.hasType(type, false)) }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a glvalue of type `bool`.
|
||||
*/
|
||||
CppType getBoolGLValueType() { exists(BoolType type | result.hasType(type, true)) }
|
||||
|
||||
/**
|
||||
* Gets the `CppType` that represents a glvalue of function type.
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,8 @@ newtype TTempVariableTag =
|
||||
ConditionValueTempVar() or
|
||||
ReturnValueTempVar() or
|
||||
ThrowTempVar() or
|
||||
LambdaTempVar()
|
||||
LambdaTempVar() or
|
||||
EllipsisTempVar()
|
||||
|
||||
string getTempVariableTagId(TTempVariableTag tag) {
|
||||
tag = ConditionValueTempVar() and result = "CondVal"
|
||||
@@ -12,4 +13,6 @@ string getTempVariableTagId(TTempVariableTag tag) {
|
||||
tag = ThrowTempVar() and result = "Throw"
|
||||
or
|
||||
tag = LambdaTempVar() and result = "Lambda"
|
||||
or
|
||||
tag = EllipsisTempVar() and result = "Ellipsis"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import implementations.Allocation
|
||||
private import implementations.Deallocation
|
||||
private import implementations.Fread
|
||||
private import implementations.Gets
|
||||
private import implementations.IdentityFunction
|
||||
private import implementations.Inet
|
||||
private import implementations.Memcpy
|
||||
|
||||
@@ -215,6 +215,40 @@ class SizelessAllocationFunction extends AllocationFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator new` or `operator new[]` function that may be associated with `new` or
|
||||
* `new[]` expressions. Note that `new` and `new[]` are not function calls, but these
|
||||
* functions may also be called directly.
|
||||
*/
|
||||
class OperatorNewAllocationFunction extends AllocationFunction {
|
||||
OperatorNewAllocationFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
(
|
||||
// operator new(bytes, ...)
|
||||
name = "operator new"
|
||||
or
|
||||
// operator new[](bytes, ...)
|
||||
name = "operator new[]"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getSizeArg() { result = 0 }
|
||||
|
||||
override predicate requiresDealloc() { not exists(getPlacementArgument()) }
|
||||
|
||||
/**
|
||||
* Gets the position of the placement pointer if this is a placement
|
||||
* `operator new` function.
|
||||
*/
|
||||
int getPlacementArgument() {
|
||||
getNumberOfParameters() = 2 and
|
||||
getParameter(1).getType() instanceof VoidPointerType and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An allocation expression that is a function call, such as call to `malloc`.
|
||||
*/
|
||||
@@ -227,7 +261,9 @@ class CallAllocationExpr extends AllocationExpr, FunctionCall {
|
||||
not (
|
||||
exists(target.getReallocPtrArg()) and
|
||||
getArgument(target.getSizeArg()).getValue().toInt() = 0
|
||||
)
|
||||
) and
|
||||
// these are modelled directly (and more accurately), avoid duplication
|
||||
not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
|
||||
}
|
||||
|
||||
override Expr getSizeExpr() { result = getArgument(target.getSizeArg()) }
|
||||
|
||||
@@ -79,6 +79,28 @@ class StandardDeallocationFunction extends DeallocationFunction {
|
||||
override int getFreedArg() { result = freedArg }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator delete` or `operator delete[]` function that may be associated
|
||||
* with `delete` or `delete[]` expressions. Note that `delete` and `delete[]`
|
||||
* are not function calls, but these functions may also be called directly.
|
||||
*/
|
||||
class OperatorDeleteDeallocationFunction extends DeallocationFunction {
|
||||
OperatorDeleteDeallocationFunction() {
|
||||
exists(string name |
|
||||
hasGlobalName(name) and
|
||||
(
|
||||
// operator delete(pointer, ...)
|
||||
name = "operator delete"
|
||||
or
|
||||
// operator delete[](pointer, ...)
|
||||
name = "operator delete[]"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override int getFreedArg() { result = 0 }
|
||||
}
|
||||
|
||||
/**
|
||||
* An deallocation expression that is a function call, such as call to `free`.
|
||||
*/
|
||||
|
||||
45
cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
Normal file
45
cpp/ql/src/semmle/code/cpp/models/implementations/Gets.qll
Normal file
@@ -0,0 +1,45 @@
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard functions `gets` and `fgets`.
|
||||
*/
|
||||
class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
|
||||
SideEffectFunction {
|
||||
GetsFunction() {
|
||||
exists(string name | hasGlobalOrStdName(name) |
|
||||
name = "gets" or // gets(str)
|
||||
name = "fgets" or // fgets(str, num, stream)
|
||||
name = "fgetws" // fgetws(wstr, num, stream)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(2) and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { index = 2 }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { index = 0 }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and
|
||||
buffer = true and
|
||||
mustWrite = true
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function `strcat` and its wide, sized, and Microsoft variants.
|
||||
*/
|
||||
class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
|
||||
class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, SideEffectFunction {
|
||||
StrcatFunction() {
|
||||
exists(string name | name = getName() |
|
||||
name = "strcat" or // strcat(dst, src)
|
||||
@@ -56,4 +57,19 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction {
|
||||
override predicate hasArrayWithNullTerminator(int param) { param = 1 }
|
||||
|
||||
override predicate hasArrayWithUnknownSize(int param) { param = 0 }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and
|
||||
buffer = true and
|
||||
mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
(i = 0 or i = 1) and
|
||||
buffer = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import semmle.code.cpp.models.interfaces.ArrayFunction
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function `strcpy` and its wide, sized, and Microsoft variants.
|
||||
*/
|
||||
class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
||||
class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, SideEffectFunction {
|
||||
StrcpyFunction() {
|
||||
this.hasName("strcpy") or
|
||||
this.hasName("_mbscpy") or
|
||||
@@ -74,4 +75,23 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction {
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and
|
||||
buffer = true and
|
||||
mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
i = 1 and
|
||||
buffer = true
|
||||
}
|
||||
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) {
|
||||
hasArrayWithVariableSize(i, result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,17 +93,27 @@ private float wideningUpperBounds(ArithmeticType t) {
|
||||
|
||||
/**
|
||||
* Gets the value of the expression `e`, if it is a constant.
|
||||
* This predicate also handles the case of constant variables initialized in compilation units,
|
||||
* which doesn't necessarily have a getValue() result from the extractor.
|
||||
* This predicate also handles the case of constant variables initialized in different
|
||||
* compilation units, which doesn't necessarily have a getValue() result from the extractor.
|
||||
*/
|
||||
private string getValue(Expr e) {
|
||||
if exists(e.getValue())
|
||||
then result = e.getValue()
|
||||
else
|
||||
exists(VariableAccess access, Variable v |
|
||||
/*
|
||||
* It should be safe to propagate the initialization value to a variable if:
|
||||
* The type of v is const, and
|
||||
* The type of v is not volatile, and
|
||||
* Either:
|
||||
* v is a local/global variable, or
|
||||
* v is a static member variable
|
||||
*/
|
||||
|
||||
exists(VariableAccess access, StaticStorageDurationVariable v |
|
||||
not v.getUnderlyingType().isVolatile() and
|
||||
v.getUnderlyingType().isConst() and
|
||||
e = access and
|
||||
v = access.getTarget() and
|
||||
v.getUnderlyingType().isConst() and
|
||||
result = getValue(v.getAnAssignedValue())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -99,6 +99,8 @@ class ArrayExecFunctionCall extends FunctionCall {
|
||||
getTarget().hasGlobalName("execv") or
|
||||
getTarget().hasGlobalName("execvp") or
|
||||
getTarget().hasGlobalName("execvpe") or
|
||||
getTarget().hasGlobalName("execve") or
|
||||
getTarget().hasGlobalName("fexecve") or
|
||||
// Windows variants
|
||||
getTarget().hasGlobalName("_execv") or
|
||||
getTarget().hasGlobalName("_execve") or
|
||||
|
||||
@@ -1231,6 +1231,7 @@ funbind(
|
||||
| @ltexpr
|
||||
| @geexpr
|
||||
| @leexpr
|
||||
| @spaceshipexpr
|
||||
;
|
||||
|
||||
@bin_bitwise_op_expr = @lshiftexpr
|
||||
@@ -1636,6 +1637,7 @@ case @expr.kind of
|
||||
| 323 = @vec_fill
|
||||
| 324 = @builtinconvertvector
|
||||
| 325 = @builtincomplex
|
||||
| 326 = @spaceshipexpr
|
||||
;
|
||||
|
||||
new_allocated_type(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -143,3 +143,9 @@ void multidimensionalNew(int x, int y) {
|
||||
auto p2 = new char[20][20];
|
||||
auto p3 = new char[x][30][30];
|
||||
}
|
||||
|
||||
void directOperatorCall() {
|
||||
void *ptr;
|
||||
ptr = operator new(sizeof(int));
|
||||
operator delete(ptr);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
newExprs
|
||||
| allocators.cpp:49:3:49:9 | new | int | operator new(unsigned long) -> void * | 4 | 4 | |
|
||||
| allocators.cpp:50:3:50:15 | new | int | operator new(size_t, float) -> void * | 4 | 4 | |
|
||||
| allocators.cpp:51:3:51:11 | new | int | operator new(unsigned long) -> void * | 4 | 4 | |
|
||||
| allocators.cpp:52:3:52:14 | new | String | operator new(unsigned long) -> void * | 8 | 8 | |
|
||||
| allocators.cpp:53:3:53:27 | new | String | operator new(size_t, float) -> void * | 8 | 8 | |
|
||||
| allocators.cpp:54:3:54:17 | new | Overaligned | operator new(unsigned long, align_val_t) -> void * | 256 | 128 | aligned |
|
||||
| allocators.cpp:55:3:55:25 | new | Overaligned | operator new(size_t, align_val_t, float) -> void * | 256 | 128 | aligned |
|
||||
| allocators.cpp:107:3:107:18 | new | FailedInit | FailedInit::operator new(size_t) -> void * | 1 | 1 | |
|
||||
| allocators.cpp:109:3:109:35 | new | FailedInitOveraligned | FailedInitOveraligned::operator new(size_t, align_val_t, float) -> void * | 128 | 128 | aligned |
|
||||
| allocators.cpp:129:3:129:21 | new | int | operator new(size_t, void *) -> void * | 4 | 4 | |
|
||||
| allocators.cpp:135:3:135:26 | new | int | operator new(size_t, const nothrow_t &) -> void * | 4 | 4 | |
|
||||
| allocators.cpp:49:3:49:9 | new | int | operator new(unsigned long) -> void * | 4 | 4 | | |
|
||||
| allocators.cpp:50:3:50:15 | new | int | operator new(size_t, float) -> void * | 4 | 4 | | |
|
||||
| allocators.cpp:51:3:51:11 | new | int | operator new(unsigned long) -> void * | 4 | 4 | | |
|
||||
| allocators.cpp:52:3:52:14 | new | String | operator new(unsigned long) -> void * | 8 | 8 | | |
|
||||
| allocators.cpp:53:3:53:27 | new | String | operator new(size_t, float) -> void * | 8 | 8 | | |
|
||||
| allocators.cpp:54:3:54:17 | new | Overaligned | operator new(unsigned long, align_val_t) -> void * | 256 | 128 | aligned | |
|
||||
| allocators.cpp:55:3:55:25 | new | Overaligned | operator new(size_t, align_val_t, float) -> void * | 256 | 128 | aligned | |
|
||||
| allocators.cpp:107:3:107:18 | new | FailedInit | FailedInit::operator new(size_t) -> void * | 1 | 1 | | |
|
||||
| allocators.cpp:109:3:109:35 | new | FailedInitOveraligned | FailedInitOveraligned::operator new(size_t, align_val_t, float) -> void * | 128 | 128 | aligned | |
|
||||
| allocators.cpp:129:3:129:21 | new | int | operator new(size_t, void *) -> void * | 4 | 4 | | & ... |
|
||||
| allocators.cpp:135:3:135:26 | new | int | operator new(size_t, const nothrow_t &) -> void * | 4 | 4 | | |
|
||||
newArrayExprs
|
||||
| allocators.cpp:68:3:68:12 | new[] | int[] | int | operator new[](unsigned long) -> void * | 4 | 4 | | n |
|
||||
| allocators.cpp:69:3:69:18 | new[] | int[] | int | operator new[](size_t, float) -> void * | 4 | 4 | | n |
|
||||
| allocators.cpp:70:3:70:15 | new[] | String[] | String | operator new[](unsigned long) -> void * | 8 | 8 | | n |
|
||||
| allocators.cpp:71:3:71:20 | new[] | Overaligned[] | Overaligned | operator new[](unsigned long, align_val_t) -> void * | 256 | 128 | aligned | n |
|
||||
| allocators.cpp:72:3:72:16 | new[] | String[10] | String | operator new[](unsigned long) -> void * | 8 | 8 | | |
|
||||
| allocators.cpp:108:3:108:19 | new[] | FailedInit[] | FailedInit | FailedInit::operator new[](size_t) -> void * | 1 | 1 | | n |
|
||||
| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned[10] | FailedInitOveraligned | FailedInitOveraligned::operator new[](size_t, align_val_t, float) -> void * | 128 | 128 | aligned | |
|
||||
| allocators.cpp:132:3:132:17 | new[] | int[1] | int | operator new[](size_t, void *) -> void * | 4 | 4 | | |
|
||||
| allocators.cpp:136:3:136:26 | new[] | int[2] | int | operator new[](size_t, const nothrow_t &) -> void * | 4 | 4 | | |
|
||||
| allocators.cpp:142:13:142:27 | new[] | char[][10] | char[10] | operator new[](unsigned long) -> void * | 10 | 1 | | x |
|
||||
| allocators.cpp:143:13:143:28 | new[] | char[20][20] | char[20] | operator new[](unsigned long) -> void * | 20 | 1 | | |
|
||||
| allocators.cpp:144:13:144:31 | new[] | char[][30][30] | char[30][30] | operator new[](unsigned long) -> void * | 900 | 1 | | x |
|
||||
| allocators.cpp:68:3:68:12 | new[] | int[] | int | operator new[](unsigned long) -> void * | 4 | 4 | | n | |
|
||||
| allocators.cpp:69:3:69:18 | new[] | int[] | int | operator new[](size_t, float) -> void * | 4 | 4 | | n | |
|
||||
| allocators.cpp:70:3:70:15 | new[] | String[] | String | operator new[](unsigned long) -> void * | 8 | 8 | | n | |
|
||||
| allocators.cpp:71:3:71:20 | new[] | Overaligned[] | Overaligned | operator new[](unsigned long, align_val_t) -> void * | 256 | 128 | aligned | n | |
|
||||
| allocators.cpp:72:3:72:16 | new[] | String[10] | String | operator new[](unsigned long) -> void * | 8 | 8 | | | |
|
||||
| allocators.cpp:108:3:108:19 | new[] | FailedInit[] | FailedInit | FailedInit::operator new[](size_t) -> void * | 1 | 1 | | n | |
|
||||
| allocators.cpp:110:3:110:37 | new[] | FailedInitOveraligned[10] | FailedInitOveraligned | FailedInitOveraligned::operator new[](size_t, align_val_t, float) -> void * | 128 | 128 | aligned | | |
|
||||
| allocators.cpp:132:3:132:17 | new[] | int[1] | int | operator new[](size_t, void *) -> void * | 4 | 4 | | | buf |
|
||||
| allocators.cpp:136:3:136:26 | new[] | int[2] | int | operator new[](size_t, const nothrow_t &) -> void * | 4 | 4 | | | |
|
||||
| allocators.cpp:142:13:142:27 | new[] | char[][10] | char[10] | operator new[](unsigned long) -> void * | 10 | 1 | | x | |
|
||||
| allocators.cpp:143:13:143:28 | new[] | char[20][20] | char[20] | operator new[](unsigned long) -> void * | 20 | 1 | | | |
|
||||
| allocators.cpp:144:13:144:31 | new[] | char[][30][30] | char[30][30] | operator new[](unsigned long) -> void * | 900 | 1 | | x | |
|
||||
newExprDeallocators
|
||||
| allocators.cpp:52:3:52:14 | new | String | operator delete(void *, unsigned long) -> void | 8 | 8 | sized |
|
||||
| allocators.cpp:53:3:53:27 | new | String | operator delete(void *, float) -> void | 8 | 8 | |
|
||||
@@ -46,3 +46,65 @@ deleteArrayExprs
|
||||
| allocators.cpp:81:3:81:45 | delete[] | Overaligned | operator delete[](void *, unsigned long, align_val_t) -> void | 256 | 128 | sized aligned |
|
||||
| allocators.cpp:82:3:82:49 | delete[] | PolymorphicBase | operator delete[](void *, unsigned long) -> void | 8 | 8 | sized |
|
||||
| allocators.cpp:83:3:83:23 | delete[] | int | operator delete[](void *, unsigned long) -> void | 4 | 4 | sized |
|
||||
allocationFunctions
|
||||
| allocators.cpp:7:7:7:18 | operator new | getSizeArg = 0, requiresDealloc |
|
||||
| allocators.cpp:8:7:8:20 | operator new[] | getSizeArg = 0, requiresDealloc |
|
||||
| allocators.cpp:9:7:9:18 | operator new | getSizeArg = 0, requiresDealloc |
|
||||
| allocators.cpp:10:7:10:20 | operator new[] | getSizeArg = 0, requiresDealloc |
|
||||
| allocators.cpp:121:7:121:18 | operator new | getPlacementArgument = 1, getSizeArg = 0 |
|
||||
| allocators.cpp:122:7:122:20 | operator new[] | getPlacementArgument = 1, getSizeArg = 0 |
|
||||
| allocators.cpp:123:7:123:18 | operator new | getSizeArg = 0, requiresDealloc |
|
||||
| allocators.cpp:124:7:124:20 | operator new[] | getSizeArg = 0, requiresDealloc |
|
||||
| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc |
|
||||
| file://:0:0:0:0 | operator new | getSizeArg = 0, requiresDealloc |
|
||||
| file://:0:0:0:0 | operator new[] | getSizeArg = 0, requiresDealloc |
|
||||
| file://:0:0:0:0 | operator new[] | getSizeArg = 0, requiresDealloc |
|
||||
allocationExprs
|
||||
| allocators.cpp:49:3:49:9 | new | getSizeBytes = 4, requiresDealloc |
|
||||
| allocators.cpp:50:3:50:15 | new | getSizeBytes = 4, requiresDealloc |
|
||||
| allocators.cpp:51:3:51:11 | new | getSizeBytes = 4, requiresDealloc |
|
||||
| allocators.cpp:52:3:52:14 | new | getSizeBytes = 8, requiresDealloc |
|
||||
| allocators.cpp:53:3:53:27 | new | getSizeBytes = 8, requiresDealloc |
|
||||
| allocators.cpp:54:3:54:17 | new | getSizeBytes = 256, requiresDealloc |
|
||||
| allocators.cpp:55:3:55:25 | new | getSizeBytes = 256, requiresDealloc |
|
||||
| allocators.cpp:68:3:68:12 | new[] | getSizeExpr = n, getSizeMult = 4, requiresDealloc |
|
||||
| allocators.cpp:69:3:69:18 | new[] | getSizeExpr = n, getSizeMult = 4, requiresDealloc |
|
||||
| allocators.cpp:70:3:70:15 | new[] | getSizeExpr = n, getSizeMult = 8, requiresDealloc |
|
||||
| allocators.cpp:71:3:71:20 | new[] | getSizeExpr = n, getSizeMult = 256, requiresDealloc |
|
||||
| allocators.cpp:72:3:72:16 | new[] | getSizeBytes = 80, requiresDealloc |
|
||||
| allocators.cpp:107:3:107:18 | new | getSizeBytes = 1, requiresDealloc |
|
||||
| allocators.cpp:108:3:108:19 | new[] | getSizeExpr = n, getSizeMult = 1, requiresDealloc |
|
||||
| allocators.cpp:109:3:109:35 | new | getSizeBytes = 128, requiresDealloc |
|
||||
| allocators.cpp:110:3:110:37 | new[] | getSizeBytes = 1280, requiresDealloc |
|
||||
| allocators.cpp:129:3:129:21 | new | getSizeBytes = 4 |
|
||||
| allocators.cpp:132:3:132:17 | new[] | getSizeBytes = 4 |
|
||||
| allocators.cpp:135:3:135:26 | new | getSizeBytes = 4, requiresDealloc |
|
||||
| allocators.cpp:136:3:136:26 | new[] | getSizeBytes = 8, requiresDealloc |
|
||||
| allocators.cpp:142:13:142:27 | new[] | getSizeExpr = x, getSizeMult = 10, requiresDealloc |
|
||||
| allocators.cpp:143:13:143:28 | new[] | getSizeBytes = 400, requiresDealloc |
|
||||
| allocators.cpp:144:13:144:31 | new[] | getSizeExpr = x, getSizeMult = 900, requiresDealloc |
|
||||
| allocators.cpp:149:8:149:19 | call to operator new | getSizeBytes = 4, getSizeExpr = sizeof(int), getSizeMult = 1, requiresDealloc |
|
||||
deallocationFunctions
|
||||
| allocators.cpp:11:6:11:20 | operator delete | getFreedArg = 0 |
|
||||
| allocators.cpp:12:6:12:22 | operator delete[] | getFreedArg = 0 |
|
||||
| allocators.cpp:13:6:13:20 | operator delete | getFreedArg = 0 |
|
||||
| allocators.cpp:14:6:14:22 | operator delete[] | getFreedArg = 0 |
|
||||
| file://:0:0:0:0 | operator delete | getFreedArg = 0 |
|
||||
| file://:0:0:0:0 | operator delete | getFreedArg = 0 |
|
||||
| file://:0:0:0:0 | operator delete | getFreedArg = 0 |
|
||||
| file://:0:0:0:0 | operator delete[] | getFreedArg = 0 |
|
||||
| file://:0:0:0:0 | operator delete[] | getFreedArg = 0 |
|
||||
deallocationExprs
|
||||
| allocators.cpp:59:3:59:35 | delete | getFreedExpr = 0 |
|
||||
| allocators.cpp:60:3:60:38 | delete | getFreedExpr = 0 |
|
||||
| allocators.cpp:61:3:61:44 | delete | getFreedExpr = 0 |
|
||||
| allocators.cpp:62:3:62:43 | delete | getFreedExpr = 0 |
|
||||
| allocators.cpp:63:3:63:47 | delete | getFreedExpr = 0 |
|
||||
| allocators.cpp:64:3:64:44 | delete | getFreedExpr = 0 |
|
||||
| allocators.cpp:78:3:78:37 | delete[] | getFreedExpr = 0 |
|
||||
| allocators.cpp:79:3:79:40 | delete[] | getFreedExpr = 0 |
|
||||
| allocators.cpp:80:3:80:46 | delete[] | getFreedExpr = 0 |
|
||||
| allocators.cpp:81:3:81:45 | delete[] | getFreedExpr = 0 |
|
||||
| allocators.cpp:82:3:82:49 | delete[] | getFreedExpr = 0 |
|
||||
| allocators.cpp:83:3:83:23 | delete[] | getFreedExpr = call to GetPointer |
|
||||
| allocators.cpp:150:2:150:16 | call to operator delete | getFreedExpr = ptr |
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import default
|
||||
import semmle.code.cpp.models.implementations.Allocation
|
||||
|
||||
query predicate newExprs(NewExpr expr, string type, string sig, int size, int alignment, string form) {
|
||||
query predicate newExprs(
|
||||
NewExpr expr, string type, string sig, int size, int alignment, string form, string placement
|
||||
) {
|
||||
exists(Function allocator, Type allocatedType |
|
||||
expr.getAllocator() = allocator and
|
||||
sig = allocator.getFullSignature() and
|
||||
@@ -8,13 +11,16 @@ query predicate newExprs(NewExpr expr, string type, string sig, int size, int al
|
||||
type = allocatedType.toString() and
|
||||
size = allocatedType.getSize() and
|
||||
alignment = allocatedType.getAlignment() and
|
||||
if expr.hasAlignedAllocation() then form = "aligned" else form = ""
|
||||
(if expr.hasAlignedAllocation() then form = "aligned" else form = "") and
|
||||
if exists(expr.getPlacementPointer())
|
||||
then placement = expr.getPlacementPointer().toString()
|
||||
else placement = ""
|
||||
)
|
||||
}
|
||||
|
||||
query predicate newArrayExprs(
|
||||
NewArrayExpr expr, string t1, string t2, string sig, int size, int alignment, string form,
|
||||
string extents
|
||||
string extents, string placement
|
||||
) {
|
||||
exists(Function allocator, Type arrayType, Type elementType |
|
||||
expr.getAllocator() = allocator and
|
||||
@@ -26,7 +32,10 @@ query predicate newArrayExprs(
|
||||
size = elementType.getSize() and
|
||||
alignment = elementType.getAlignment() and
|
||||
(if expr.hasAlignedAllocation() then form = "aligned" else form = "") and
|
||||
extents = concat(Expr e | e = expr.getExtent() | e.toString(), ", ")
|
||||
extents = concat(Expr e | e = expr.getExtent() | e.toString(), ", ") and
|
||||
if exists(expr.getPlacementPointer())
|
||||
then placement = expr.getPlacementPointer().toString()
|
||||
else placement = ""
|
||||
)
|
||||
}
|
||||
|
||||
@@ -101,3 +110,54 @@ query predicate deleteArrayExprs(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
string describeAllocationFunction(AllocationFunction f) {
|
||||
result = "getSizeArg = " + f.getSizeArg().toString()
|
||||
or
|
||||
result = "getSizeMult = " + f.getSizeMult().toString()
|
||||
or
|
||||
result = "getReallocPtrArg = " + f.getReallocPtrArg().toString()
|
||||
or
|
||||
f.requiresDealloc() and
|
||||
result = "requiresDealloc"
|
||||
or
|
||||
result =
|
||||
"getPlacementArgument = " + f.(OperatorNewAllocationFunction).getPlacementArgument().toString()
|
||||
}
|
||||
|
||||
query predicate allocationFunctions(AllocationFunction f, string descr) {
|
||||
descr = concat(describeAllocationFunction(f), ", ")
|
||||
}
|
||||
|
||||
string describeAllocationExpr(AllocationExpr e) {
|
||||
result = "getSizeExpr = " + e.getSizeExpr().toString()
|
||||
or
|
||||
result = "getSizeMult = " + e.getSizeMult().toString()
|
||||
or
|
||||
result = "getSizeBytes = " + e.getSizeBytes().toString()
|
||||
or
|
||||
result = "getReallocPtr = " + e.getReallocPtr().toString()
|
||||
or
|
||||
e.requiresDealloc() and
|
||||
result = "requiresDealloc"
|
||||
}
|
||||
|
||||
query predicate allocationExprs(AllocationExpr e, string descr) {
|
||||
descr = concat(describeAllocationExpr(e), ", ")
|
||||
}
|
||||
|
||||
string describeDeallocationFunction(DeallocationFunction f) {
|
||||
result = "getFreedArg = " + f.getFreedArg().toString()
|
||||
}
|
||||
|
||||
query predicate deallocationFunctions(DeallocationFunction f, string descr) {
|
||||
descr = concat(describeDeallocationFunction(f), ", ")
|
||||
}
|
||||
|
||||
string describeDeallocationExpr(DeallocationExpr e) {
|
||||
result = "getFreedExpr = " + e.getFreedExpr().toString()
|
||||
}
|
||||
|
||||
query predicate deallocationExprs(DeallocationExpr e, string descr) {
|
||||
descr = concat(describeDeallocationExpr(e), ", ")
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user