Merge branch 'main' into jty/python/emailInjection

This commit is contained in:
${sleep,7}
2022-04-20 09:50:08 -04:00
committed by GitHub
2207 changed files with 142269 additions and 105970 deletions

3
.bazelrc Normal file
View File

@@ -0,0 +1,3 @@
build --copt="-std=c++17"
try-import %workspace%/local.bazelrc

1
.bazelversion Normal file
View File

@@ -0,0 +1 @@
5.0.0

View File

@@ -3,12 +3,22 @@ description: Fetches the latest version of CodeQL
runs:
using: composite
steps:
- name: Select platform - Linux
if: runner.os == 'Linux'
shell: bash
run: echo "GA_CODEQL_CLI_PLATFORM=linux64" >> $GITHUB_ENV
- name: Select platform - MacOS
if: runner.os == 'MacOS'
shell: bash
run: echo "GA_CODEQL_CLI_PLATFORM=osx64" >> $GITHUB_ENV
- name: Fetch CodeQL
shell: bash
run: |
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1)
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST"
unzip -q -d "${RUNNER_TEMP}" codeql-linux64.zip
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-$GA_CODEQL_CLI_PLATFORM.zip "$LATEST"
unzip -q -d "${RUNNER_TEMP}" codeql-$GA_CODEQL_CLI_PLATFORM.zip
echo "${RUNNER_TEMP}/codeql" >> "${GITHUB_PATH}"
env:
GITHUB_TOKEN: ${{ github.token }}

53
.github/workflows/check-qldoc.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: "Check QLdoc coverage"
on:
pull_request:
paths:
- "*/ql/lib/**"
- .github/workflows/check-qldoc.yml
branches:
- main
- "rc/*"
jobs:
qldoc:
runs-on: ubuntu-latest
steps:
- name: Install CodeQL
run: |
gh extension install github/gh-codeql
gh codeql set-channel nightly
gh codeql version
env:
GITHUB_TOKEN: ${{ github.token }}
- uses: actions/checkout@v2
with:
fetch-depth: 2
- name: Check QLdoc coverage
shell: bash
run: |
EXIT_CODE=0
changed_lib_packs="$(git diff --name-only --diff-filter=ACMRT HEAD^ HEAD | { grep -o '^[a-z]*/ql/lib' || true; } | sort -u)"
for pack_dir in ${changed_lib_packs}; do
lang="${pack_dir%/ql/lib}"
gh codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-current.txt" --dir="${pack_dir}"
done
git checkout HEAD^
for pack_dir in ${changed_lib_packs}; do
# When we add a new language, pack_dir would not exist in HEAD^.
# In this case the right thing to do is to skip the check.
[[ ! -d "${pack_dir}" ]] && continue
lang="${pack_dir%/ql/lib}"
gh codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-baseline.txt" --dir="${pack_dir}"
awk -F, '{gsub(/"/,""); if ($4==0 && $6=="public") print "\""$3"\"" }' "${RUNNER_TEMP}/${lang}-current.txt" | sort -u > "${RUNNER_TEMP}/current-undocumented.txt"
awk -F, '{gsub(/"/,""); if ($4==0 && $6=="public") print "\""$3"\"" }' "${RUNNER_TEMP}/${lang}-baseline.txt" | sort -u > "${RUNNER_TEMP}/baseline-undocumented.txt"
UNDOCUMENTED="$(grep -f <(comm -13 "${RUNNER_TEMP}/baseline-undocumented.txt" "${RUNNER_TEMP}/current-undocumented.txt") "${RUNNER_TEMP}/${lang}-current.txt" || true)"
if [ -n "$UNDOCUMENTED" ]; then
echo "$UNDOCUMENTED" | awk -F, '{gsub(/"/,""); print "::warning file='"${pack_dir}"'/"$1",line="$2"::Missing QLdoc for "$5, $3 }'
EXIT_CODE=1
fi
done
exit "${EXIT_CODE}"

View File

@@ -16,9 +16,10 @@ jobs:
- uses: actions/checkout@v2
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
languages: javascript # does not matter
tools: latest
- name: Get CodeQL version
id: get-codeql-version
run: |
@@ -159,7 +160,7 @@ jobs:
PACK: ${{ runner.temp }}/pack
- name: Hack codeql-action options
run: |
JSON=$(jq -nc --arg pack "${PACK}" '.resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .database.init=["--search-path", $pack]')
JSON=$(jq -nc --arg pack "${PACK}" '.database."run-queries"=["--search-path", $pack] | .resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .database.init=["--search-path", $pack]')
echo "CODEQL_ACTION_EXTRA_OPTIONS=${JSON}" >> ${GITHUB_ENV}
env:
PACK: ${{ runner.temp }}/pack
@@ -171,22 +172,25 @@ jobs:
echo "paths:" > ${CONF}
echo " - ${FOLDER}" >> ${CONF}
echo "paths-ignore:" >> ${CONF}
echo " - ql/ql/test" >> ${CONF}
echo " - ql/ql/test" >> ${CONF}
echo "disable-default-queries: true" >> ${CONF}
echo "packs:" >> ${CONF}
echo " - codeql/ql" >> ${CONF}
echo "Config file: "
cat ${CONF}
env:
CONF: ./ql-for-ql-config.yml
FOLDER: ${{ matrix.folder }}
- name: Initialize CodeQL
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
languages: ql
db-location: ${{ runner.temp }}/db
config-file: ./ql-for-ql-config.yml
tools: latest
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@erik-krogh/ql
uses: github/codeql-action/analyze@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
category: "ql-for-ql-${{ matrix.folder }}"
- name: Copy sarif file to CWD

View File

@@ -26,7 +26,7 @@ jobs:
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
languages: javascript # does not matter
- uses: actions/cache@v2

View File

@@ -20,7 +20,7 @@ jobs:
- uses: actions/checkout@v2
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@erik-krogh/ql
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
languages: javascript # does not matter
- uses: actions/cache@v2

View File

@@ -63,6 +63,7 @@ jobs:
qltest:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
slice: ["1/2", "2/2"]
steps:

51
.github/workflows/swift-qltest.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: "Swift: Run QL Tests"
on:
pull_request:
paths:
- "swift/**"
- .github/workflows/swift-qltest.yml
branches:
- main
defaults:
run:
working-directory: swift
jobs:
qlformat:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/fetch-codeql
- name: Check QL formatting
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
qltest:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os : [ubuntu-20.04, macos-latest]
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/fetch-codeql
- name: Install bazelisk - Linux
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y wget
wget https://github.com/bazelbuild/bazelisk/releases/download/v1.11.0/bazelisk-linux-amd64
mv bazelisk-linux-amd64 /usr/local/bin/bazel
chmod +x /usr/local/bin/bazel
- name: Install bazelisk - macOS
if: runner.os == 'MacOS'
run: |
brew install bazelisk
- name: Build Swift extractor
run: |
bazel run //swift:create-extractor-pack
- name: Run QL tests
run: |
codeql test run --threads=0 --ram 5000 --search-path "${{ github.workspace }}/swift/extractor-pack" --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition ql/test
env:
GITHUB_TOKEN: ${{ github.token }}

10
.gitignore vendored
View File

@@ -17,7 +17,7 @@
# Byte-compiled python files
*.pyc
# python virtual environment folder
# python virtual environment folder
.venv/
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself
@@ -29,4 +29,10 @@ csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
.codeql
# Compiled class file
*.class
*.class
# links created by bazel
/bazel-*
# CLion project files
/.clwb

29
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,29 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- repo: local
hooks:
- id: codeql-format
name: Fix QL file formatting
files: \.qll?$
language: system
entry: codeql query format --in-place
- id: sync-files
name: Fix files required to be identical
language: system
entry: python3 config/sync-files.py --latest
pass_filenames: false
- id: qhelp
name: Check query help generation
files: \.qhelp$
language: system
entry: python3 misc/scripts/check-qhelp.py

View File

@@ -5,14 +5,6 @@
/python/ @github/codeql-python
/ruby/ @github/codeql-ruby
# Make @xcorail (GitHub Security Lab) a code owner for experimental queries so he gets pinged when we promote a query out of experimental
/cpp/**/experimental/**/* @github/codeql-c-analysis @xcorail
/csharp/**/experimental/**/* @github/codeql-csharp @xcorail
/java/**/experimental/**/* @github/codeql-java @xcorail
/javascript/**/experimental/**/* @github/codeql-javascript @xcorail
/python/**/experimental/**/* @github/codeql-python @xcorail
/ruby/**/experimental/**/* @github/codeql-ruby @xcorail
# ML-powered queries
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
@@ -31,3 +23,7 @@
# QL for QL reviewers
/ql/ @github/codeql-ql-for-ql-reviewers
# Bazel
**/*.bazel @github/codeql-ci-reviewers
**/*.bzl @github/codeql-ci-reviewers

View File

@@ -36,13 +36,17 @@ If you have an idea for a query that you would like to share with other CodeQL u
For details, see the [guide on query metadata](docs/query-metadata-style-guide.md).
Make sure the `select` statement is compatible with the query `@kind`. See [About CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/introduction-to-queries.html#select-clause) on help.semmle.com.
Make sure the `select` statement is compatible with the query `@kind`. See [About CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/about-codeql-queries/#select-clause) on codeql.github.com.
3. **Formatting**
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode/procedures/about-codeql-for-vscode.html).
If you prefer, you can use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted. See the [pre-commit hook installation guide](docs/pre-commit-hook-setup.md) for instructions on how to install the hook.
If you prefer, you can either:
1. install the [pre-commit framework](https://pre-commit.com/) and install the configured hooks on this repo via `pre-commit install`, or
2. use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted.
See the [pre-commit hook installation guide](docs/pre-commit-hook-setup.md) for instructions on the two approaches.
4. **Compilation**
@@ -63,6 +67,10 @@ After the experimental query is merged, we welcome pull requests to improve it.
## 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 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 CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product.
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 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 CLA agreements signed in the past, but no longer require contributors to sign a CLA. Under GDPR legislation, we do this on the basis of our legitimate interest in creating the CodeQL product.
Please do get in touch (privacy@github.com) if you have any questions about this or our data protection policies.
## Bazel
Please notice that any bazel targets and definitions in this repository are currently experimental
and for internal use only.

12
WORKSPACE.bazel Normal file
View File

@@ -0,0 +1,12 @@
# Please notice that any bazel targets and definitions in this repository are currently experimental
# and for internal use only.
workspace(name = "codeql")
load("//misc/bazel:workspace.bzl", "codeql_workspace")
codeql_workspace()
load("//misc/bazel:workspace_deps.bzl", "codeql_workspace_deps")
codeql_workspace_deps()

View File

@@ -0,0 +1,58 @@
import fs from "fs";
import path from "path";
import cp from "child_process";
function* walk(dir) {
for (const file of fs.readdirSync(dir)) {
const filePath = path.join(dir, file);
if (fs.statSync(filePath).isDirectory()) {
yield* walk(filePath);
} else {
yield filePath;
}
}
}
function* deprecatedFiles(dir) {
for (const file of walk(dir)) {
if (file.endsWith(".ql") || file.endsWith(".qll")) {
const contents = fs.readFileSync(file, "utf8");
if (/\sdeprecated\s/.test(contents)) {
yield file;
}
}
}
}
const blameRegExp =
/^(\^?\w+)\s.+\s+(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} (?:\+|-)\d{4})\s+(\d+)\).*$/;
function* deprecationMessages(dir) {
for (const file of deprecatedFiles(dir)) {
const blame = cp.execFileSync("git", ["blame", "--", file]);
const lines = blame.toString().split("\n");
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.includes(" deprecated ")) {
try {
const [_, sha, time, lineNumber] = line.match(blameRegExp);
const date = new Date(time);
// check if it's within the last 14 months (a year, plus 2 months for safety, in case a PR was delayed)
if (date.getTime() >= Date.now() - 14 * 31 * 24 * 60 * 60 * 1000) {
continue;
}
const message = `${file}:${lineNumber} was last updated on ${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`;
yield [message, date];
} catch (e) {
console.log(e);
console.log("----");
console.log(line);
console.log("----");
process.exit(0);
}
}
}
}
}
[...deprecationMessages(".")]
.sort((a, b) => a[1].getTime() - b[1].getTime())
.forEach((msg) => console.log(msg[0]));

View File

@@ -27,7 +27,8 @@
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll"
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll"
],
"DataFlow Java/C++/C#/Python Common": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll",
@@ -54,7 +55,8 @@
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking2/TaintTrackingImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttrackingforlibraries/TaintTrackingImpl.qll"
],
"DataFlow Java/C++/C#/Python Consistency checks": [
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll",
@@ -73,6 +75,10 @@
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
],
"Model as Data Generation Java/C# - CaptureModels": [
"java/ql/src/utils/model-generator/internal/CaptureModels.qll",
"csharp/ql/src/utils/model-generator/internal/CaptureModels.qll"
],
"Sign Java/C#": [
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
@@ -377,7 +383,8 @@
"csharp/ql/test/TestUtilities/InlineExpectationsTest.qll",
"java/ql/test/TestUtilities/InlineExpectationsTest.qll",
"python/ql/test/TestUtilities/InlineExpectationsTest.qll",
"ruby/ql/test/TestUtilities/InlineExpectationsTest.qll"
"ruby/ql/test/TestUtilities/InlineExpectationsTest.qll",
"ql/ql/test/TestUtilities/InlineExpectationsTest.qll"
],
"C++ ExternalAPIs": [
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
@@ -426,7 +433,6 @@
"python/ql/src/Lexical/CommentedOutCodeMetricOverview.inc.qhelp"
],
"FLinesOfDuplicatedCodeCommon.inc.qhelp": [
"cpp/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.inc.qhelp",
"java/ql/src/Metrics/Files/FLinesOfDuplicatedCodeCommon.inc.qhelp",
"javascript/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.inc.qhelp",
"python/ql/src/Metrics/FLinesOfDuplicatedCodeCommon.inc.qhelp"
@@ -473,11 +479,12 @@
"python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll",
"ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll"
],
"ReDoS Exponential Python/JS": [
"ReDoS Exponential Python/JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/performance/ExponentialBackTracking.qll",
"python/ql/lib/semmle/python/security/performance/ExponentialBackTracking.qll"
"python/ql/lib/semmle/python/security/performance/ExponentialBackTracking.qll",
"ruby/ql/lib/codeql/ruby/security/performance/ExponentialBackTracking.qll"
],
"ReDoS Polynomial Python/JS": [
"ReDoS Polynomial Python/JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
"python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll",
"ruby/ql/lib/codeql/ruby/security/performance/SuperlinearBackTracking.qll"
@@ -508,5 +515,39 @@
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll"
],
"IncompleteUrlSubstringSanitization": [
"javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.qll",
"ruby/ql/src/queries/security/cwe-020/IncompleteUrlSubstringSanitization.qll"
],
"Concepts Python/Ruby/JS": [
"python/ql/lib/semmle/python/internal/ConceptsShared.qll",
"ruby/ql/lib/codeql/ruby/internal/ConceptsShared.qll",
"javascript/ql/lib/semmle/javascript/internal/ConceptsShared.qll"
],
"Hostname Regexp queries": [
"javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll",
"python/ql/src/Security/CWE-020/HostnameRegexpShared.qll",
"ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll"
],
"ApiGraphModels": [
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll",
"ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll"
],
"TaintedFormatStringQuery Ruby/JS": [
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringQuery.qll",
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringQuery.qll"
],
"TaintedFormatStringCustomizations Ruby/JS": [
"javascript/ql/lib/semmle/javascript/security/dataflow/TaintedFormatStringCustomizations.qll",
"ruby/ql/lib/codeql/ruby/security/TaintedFormatStringCustomizations.qll"
],
"HttpToFileAccessQuery JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessQuery.qll",
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessQuery.qll"
],
"HttpToFileAccessCustomizations JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessCustomizations.qll",
"ruby/ql/lib/codeql/ruby/security/HttpToFileAccessCustomizations.qll"
]
}
}

17
cpp/BUILD.bazel Normal file
View File

@@ -0,0 +1,17 @@
package(default_visibility = ["//visibility:public"])
load("@rules_pkg//:mappings.bzl", "pkg_filegroup")
alias(
name = "dbscheme",
actual = "//cpp/ql/lib:dbscheme",
)
pkg_filegroup(
name = "db-files",
srcs = [
":dbscheme",
"//cpp/downgrades",
"//cpp/ql/lib:dbscheme-stats",
],
)

View File

@@ -0,0 +1,12 @@
load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix")
pkg_files(
name = "downgrades",
srcs = glob(
["**"],
exclude = ["BUILD.bazel"],
),
prefix = "cpp/downgrades",
strip_prefix = strip_prefix.from_pkg(),
visibility = ["//cpp:__pkg__"],
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
description: Remove uniqueness constraint from the uuid property
compatibility: full

15
cpp/ql/lib/BUILD.bazel Normal file
View File

@@ -0,0 +1,15 @@
package(default_visibility = ["//cpp:__pkg__"])
load("@rules_pkg//:mappings.bzl", "pkg_files")
pkg_files(
name = "dbscheme",
srcs = ["semmlecode.cpp.dbscheme"],
prefix = "cpp",
)
pkg_files(
name = "dbscheme-stats",
srcs = ["semmlecode.cpp.dbscheme.stats"],
prefix = "cpp",
)

View File

@@ -1,5 +1,41 @@
## 0.0.13
## 0.0.12
### Breaking Changes
* The flow state variants of `isBarrier` and `isAdditionalFlowStep` are no longer exposed in the taint tracking library. The `isSanitizer` and `isAdditionalTaintStep` predicates should be used instead.
### Deprecated APIs
* Many classes/predicates/modules that had upper-case acronyms have been renamed to follow our style-guide.
The old name still exists as a deprecated alias.
### New Features
* The data flow and taint tracking libraries have been extended with versions of `isBarrierIn`, `isBarrierOut`, and `isBarrierGuard`, respectively `isSanitizerIn`, `isSanitizerOut`, and `isSanitizerGuard`, that support flow states.
### Minor Analysis Improvements
* `DefaultOptions::exits` now holds for C11 functions with the `_Noreturn` or `noreturn` specifier.
* `hasImplicitCopyConstructor` and `hasImplicitCopyAssignmentOperator` now correctly handle implicitly-deleted operators in templates.
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
## 0.0.11
### Minor Analysis Improvements
* Many queries now support structured bindings, as structured bindings are now handled in the IR translation.
## 0.0.10
### New Features
* Added a `isStructuredBinding` predicate to the `Variable` class which holds when the variable is declared as part of a structured binding declaration.
## 0.0.9
## 0.0.8
### Deprecated APIs

View File

@@ -54,11 +54,13 @@ class Options extends string {
*
* By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`,
* `longjmp`, `__builtin_unreachable` and any function with a
* `noreturn` attribute.
* `noreturn` attribute or specifier.
*/
predicate exits(Function f) {
f.getAnAttribute().hasName("noreturn")
or
f.getASpecifier().hasName("noreturn")
or
f.hasGlobalOrStdName([
"exit", "_exit", "abort", "__assert_fail", "longjmp", "__builtin_unreachable"
])

View File

@@ -39,7 +39,7 @@ class CustomOptions extends Options {
*
* By default, this holds for `exit`, `_exit`, `abort`, `__assert_fail`,
* `longjmp`, `error`, `__builtin_unreachable` and any function with a
* `noreturn` attribute.
* `noreturn` attribute or specifier.
*/
override predicate exits(Function f) { Options.super.exits(f) }

View File

@@ -0,0 +1,4 @@
---
category: feature
---
* A new library `semmle.code.cpp.security.PrivateData` has been added. The new library heuristically detects variables and functions dealing with sensitive private data, such as e-mail addresses and credit card numbers.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `semmle.code.cpp.security.SensitiveExprs` library has been enhanced with some additional rules for detecting credentials.

View File

@@ -0,0 +1,4 @@
---
category: breaking
---
The recently added flow-state versions of `isBarrierIn`, `isBarrierOut`, `isSanitizerIn`, and `isSanitizerOut` in the data flow and taint tracking libraries have been removed.

View File

@@ -1,4 +1,5 @@
---
category: feature
---
## 0.0.10
### New Features
* Added a `isStructuredBinding` predicate to the `Variable` class which holds when the variable is declared as part of a structured binding declaration.

View File

@@ -0,0 +1,5 @@
## 0.0.11
### Minor Analysis Improvements
* Many queries now support structured bindings, as structured bindings are now handled in the IR translation.

View File

@@ -0,0 +1,20 @@
## 0.0.12
### Breaking Changes
* The flow state variants of `isBarrier` and `isAdditionalFlowStep` are no longer exposed in the taint tracking library. The `isSanitizer` and `isAdditionalTaintStep` predicates should be used instead.
### Deprecated APIs
* Many classes/predicates/modules that had upper-case acronyms have been renamed to follow our style-guide.
The old name still exists as a deprecated alias.
### New Features
* The data flow and taint tracking libraries have been extended with versions of `isBarrierIn`, `isBarrierOut`, and `isBarrierGuard`, respectively `isSanitizerIn`, `isSanitizerOut`, and `isSanitizerGuard`, that support flow states.
### Minor Analysis Improvements
* `DefaultOptions::exits` now holds for C11 functions with the `_Noreturn` or `noreturn` specifier.
* `hasImplicitCopyConstructor` and `hasImplicitCopyAssignmentOperator` now correctly handle implicitly-deleted operators in templates.
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.

View File

@@ -0,0 +1 @@
## 0.0.13

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.0.9
lastReleaseVersion: 0.0.13

View File

@@ -69,6 +69,4 @@ import semmle.code.cpp.Comments
import semmle.code.cpp.Preprocessor
import semmle.code.cpp.Iteration
import semmle.code.cpp.NameQualifiers
import semmle.code.cpp.ObjectiveC
import semmle.code.cpp.exprs.ObjectiveC
import DefaultOptions

View File

@@ -4,7 +4,7 @@
import cpp
import semmle.code.cpp.dataflow.TaintTracking
import experimental.semmle.code.cpp.security.PrivateData
import semmle.code.cpp.security.PrivateData
import semmle.code.cpp.security.FileWrite
import semmle.code.cpp.security.BufferWrite

View File

@@ -1,52 +0,0 @@
/**
* Provides classes and predicates for identifying private data and functions for security.
*
* 'Private' data in general is anything that would compromise user privacy if exposed. This
* library tries to guess where private data may either be stored in a variable or produced by a
* function.
*
* This library is not concerned with credentials. See `SensitiveActions` for expressions related
* to credentials.
*/
import cpp
/** A string for `match` that identifies strings that look like they represent private data. */
private string privateNames() {
result =
[
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
// Government identifiers, such as Social Security Numbers
"%social%security%number%",
// Contact information, such as home addresses and telephone numbers
"%postcode%", "%zipcode%",
// result = "%telephone%" or
// Geographic location - where the user is (or was)
"%latitude%", "%longitude%",
// Financial data - such as credit card numbers, salary, bank accounts, and debts
"%creditcard%", "%salary%", "%bankaccount%",
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
// result = "%email%" or
// result = "%mobile%" or
"%employer%",
// Health - medical conditions, insurance status, prescription records
"%medical%"
]
}
/** An expression that might contain private data. */
abstract class PrivateDataExpr extends Expr { }
/** A functiond call that might produce private data. */
class PrivateFunctionCall extends PrivateDataExpr, FunctionCall {
PrivateFunctionCall() {
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
}
}
/** An access to a variable that might contain private data. */
class PrivateVariableAccess extends PrivateDataExpr, VariableAccess {
PrivateVariableAccess() {
exists(string s | this.getTarget().getName().toLowerCase() = s | s.matches(privateNames()))
}
}

View File

@@ -0,0 +1,7 @@
import SemanticExpr
import SemanticBound
import SemanticSSA
import SemanticGuard
import SemanticCFG
import SemanticType
import SemanticOpcode

View File

@@ -0,0 +1,42 @@
/**
* Semantic wrapper around the language-specific bounds library.
*/
private import SemanticExpr
private import SemanticExprSpecific::SemanticExprConfig as Specific
private import SemanticSSA
/**
* A valid base for an expression bound.
*
* Can be either a variable (`SemSsaBound`) or zero (`SemZeroBound`).
*/
class SemBound instanceof Specific::Bound {
final string toString() { result = super.toString() }
final SemExpr getExpr(int delta) { result = Specific::getBoundExpr(this, delta) }
}
/**
* A bound that is a constant zero.
*/
class SemZeroBound extends SemBound {
SemZeroBound() { Specific::zeroBound(this) }
}
/**
* A bound that is an SSA definition.
*/
class SemSsaBound extends SemBound {
/**
* The variables whose value is used as the bound.
*
* Can be multi-valued in some implementations. If so, all variables will be equivalent.
*/
SemSsaVariable var;
SemSsaBound() { Specific::ssaBound(this, var) }
/** Gets a variable whose value is used as the bound. */
final SemSsaVariable getAVariable() { result = var }
}

View File

@@ -0,0 +1,22 @@
/**
* Semantic interface to the control flow graph.
*/
private import Semantic
private import SemanticExprSpecific::SemanticExprConfig as Specific
/**
* A basic block in the control-flow graph.
*/
class SemBasicBlock extends Specific::BasicBlock {
/** Holds if this block (transitively) dominates `otherblock`. */
final predicate bbDominates(SemBasicBlock otherBlock) { Specific::bbDominates(this, otherBlock) }
/** Holds if this block has dominance information. */
final predicate hasDominanceInformation() { Specific::hasDominanceInformation(this) }
/** Gets an expression that is evaluated in this basic block. */
final SemExpr getAnExpr() { result.getBasicBlock() = this }
final int getUniqueId() { result = Specific::getBasicBlockUniqueId(this) }
}

View File

@@ -0,0 +1,309 @@
/**
* Semantic interface for expressions.
*/
private import Semantic
private import SemanticExprSpecific::SemanticExprConfig as Specific
/**
* An language-neutral expression.
*
* The expression computes a value of type `getSemType()`. The actual computation is determined by
* the expression's opcode (`getOpcode()`).
*/
class SemExpr instanceof Specific::Expr {
final string toString() { result = super.toString() }
final Specific::Location getLocation() { result = super.getLocation() }
Opcode getOpcode() { result instanceof Opcode::Unknown }
SemType getSemType() { result = Specific::getUnknownExprType(this) }
final SemBasicBlock getBasicBlock() { result = Specific::getExprBasicBlock(this) }
}
/** An expression with an opcode other than `Unknown`. */
abstract private class SemKnownExpr extends SemExpr {
Opcode opcode;
SemType type;
final override Opcode getOpcode() { result = opcode }
final override SemType getSemType() { result = type }
}
/** An expression that returns a literal value. */
class SemLiteralExpr extends SemKnownExpr {
SemLiteralExpr() {
Specific::integerLiteral(this, type, _) and opcode instanceof Opcode::Constant
or
Specific::largeIntegerLiteral(this, type, _) and opcode instanceof Opcode::Constant
or
Specific::booleanLiteral(this, type, _) and opcode instanceof Opcode::Constant
or
Specific::floatingPointLiteral(this, type, _) and opcode instanceof Opcode::Constant
or
Specific::nullLiteral(this, type) and opcode instanceof Opcode::Constant
or
Specific::stringLiteral(this, type, _) and opcode instanceof Opcode::StringConstant
}
}
/** An expression that returns a numeric literal value. */
class SemNumericLiteralExpr extends SemLiteralExpr {
SemNumericLiteralExpr() {
Specific::integerLiteral(this, _, _)
or
Specific::largeIntegerLiteral(this, _, _)
or
Specific::floatingPointLiteral(this, _, _)
}
/**
* Gets an approximation of the value of the literal, as a `float`.
*
* If the value can be precisely represented as a `float`, the result will be exact. If the actual
* value cannot be precisely represented (for example, it is an integer with more than 53
* significant bits), then the result is an approximation.
*/
float getApproximateFloatValue() { none() }
}
/** An expression that returns an integer literal value. */
class SemIntegerLiteralExpr extends SemNumericLiteralExpr {
SemIntegerLiteralExpr() {
Specific::integerLiteral(this, _, _)
or
Specific::largeIntegerLiteral(this, _, _)
}
/**
* Gets the value of the literal, if it can be represented as an `int`.
*
* If the value is outside the range of an `int`, use `getApproximateFloatValue()` to get a value
* that is equal to the actual integer value, within rounding error.
*/
final int getIntValue() { Specific::integerLiteral(this, _, result) }
final override float getApproximateFloatValue() {
result = getIntValue()
or
Specific::largeIntegerLiteral(this, _, result)
}
}
/**
* An expression that returns a floating-point literal value.
*/
class SemFloatingPointLiteralExpr extends SemNumericLiteralExpr {
float value;
SemFloatingPointLiteralExpr() { Specific::floatingPointLiteral(this, _, value) }
final override float getApproximateFloatValue() { result = value }
/** Gets the value of the literal. */
final float getFloatValue() { result = value }
}
/**
* An expression that consumes two operands.
*/
class SemBinaryExpr extends SemKnownExpr {
SemExpr leftOperand;
SemExpr rightOperand;
SemBinaryExpr() { Specific::binaryExpr(this, opcode, type, leftOperand, rightOperand) }
/** Gets the left operand. */
final SemExpr getLeftOperand() { result = leftOperand }
/** Gets the right operand. */
final SemExpr getRightOperand() { result = rightOperand }
/** Holds if `a` and `b` are the two operands, in either order. */
final predicate hasOperands(SemExpr a, SemExpr b) {
a = getLeftOperand() and b = getRightOperand()
or
a = getRightOperand() and b = getLeftOperand()
}
/** Gets the two operands. */
final SemExpr getAnOperand() { result = getLeftOperand() or result = getRightOperand() }
}
/** An expression that performs and ordered comparison of two operands. */
class SemRelationalExpr extends SemBinaryExpr {
SemRelationalExpr() {
opcode instanceof Opcode::CompareLT
or
opcode instanceof Opcode::CompareLE
or
opcode instanceof Opcode::CompareGT
or
opcode instanceof Opcode::CompareGE
}
/**
* Get the operand that will be less than the other operand if the result of the comparison is
* `true`.
*
* For `x < y` or `x <= y`, this will return `x`.
* For `x > y` or `x >= y`, this will return `y`.`
*/
final SemExpr getLesserOperand() {
if opcode instanceof Opcode::CompareLT or opcode instanceof Opcode::CompareLE
then result = getLeftOperand()
else result = getRightOperand()
}
/**
* Get the operand that will be greater than the other operand if the result of the comparison is
* `true`.
*
* For `x < y` or `x <= y`, this will return `y`.
* For `x > y` or `x >= y`, this will return `x`.`
*/
final SemExpr getGreaterOperand() {
if opcode instanceof Opcode::CompareGT or opcode instanceof Opcode::CompareGE
then result = getLeftOperand()
else result = getRightOperand()
}
/** Holds if this comparison returns `false` if the two operands are equal. */
final predicate isStrict() {
opcode instanceof Opcode::CompareLT or opcode instanceof Opcode::CompareGT
}
}
class SemAddExpr extends SemBinaryExpr {
SemAddExpr() { opcode instanceof Opcode::Add }
}
class SemSubExpr extends SemBinaryExpr {
SemSubExpr() { opcode instanceof Opcode::Sub }
}
class SemMulExpr extends SemBinaryExpr {
SemMulExpr() { opcode instanceof Opcode::Mul }
}
class SemDivExpr extends SemBinaryExpr {
SemDivExpr() { opcode instanceof Opcode::Div }
}
class SemRemExpr extends SemBinaryExpr {
SemRemExpr() { opcode instanceof Opcode::Rem }
}
class SemShiftLeftExpr extends SemBinaryExpr {
SemShiftLeftExpr() { opcode instanceof Opcode::ShiftLeft }
}
class SemShiftRightExpr extends SemBinaryExpr {
SemShiftRightExpr() { opcode instanceof Opcode::ShiftRight }
}
class SemShiftRightUnsignedExpr extends SemBinaryExpr {
SemShiftRightUnsignedExpr() { opcode instanceof Opcode::ShiftRightUnsigned }
}
class SemBitAndExpr extends SemBinaryExpr {
SemBitAndExpr() { opcode instanceof Opcode::BitAnd }
}
class SemBitOrExpr extends SemBinaryExpr {
SemBitOrExpr() { opcode instanceof Opcode::BitOr }
}
class SemBitXorExpr extends SemBinaryExpr {
SemBitXorExpr() { opcode instanceof Opcode::BitXor }
}
class SemUnaryExpr extends SemKnownExpr {
SemExpr operand;
SemUnaryExpr() { Specific::unaryExpr(this, opcode, type, operand) }
final SemExpr getOperand() { result = operand }
}
class SemBoxExpr extends SemUnaryExpr {
SemBoxExpr() { opcode instanceof Opcode::Box }
}
class SemUnboxExpr extends SemUnaryExpr {
SemUnboxExpr() { opcode instanceof Opcode::Unbox }
}
class SemConvertExpr extends SemUnaryExpr {
SemConvertExpr() { opcode instanceof Opcode::Convert }
}
class SemCopyValueExpr extends SemUnaryExpr {
SemCopyValueExpr() { opcode instanceof Opcode::CopyValue }
}
class SemNegateExpr extends SemUnaryExpr {
SemNegateExpr() { opcode instanceof Opcode::Negate }
}
class SemBitComplementExpr extends SemUnaryExpr {
SemBitComplementExpr() { opcode instanceof Opcode::BitComplement }
}
class SemLogicalNotExpr extends SemUnaryExpr {
SemLogicalNotExpr() { opcode instanceof Opcode::LogicalNot }
}
class SemAddOneExpr extends SemUnaryExpr {
SemAddOneExpr() { opcode instanceof Opcode::AddOne }
}
class SemSubOneExpr extends SemUnaryExpr {
SemSubOneExpr() { opcode instanceof Opcode::SubOne }
}
private class SemNullaryExpr extends SemKnownExpr {
SemNullaryExpr() { Specific::nullaryExpr(this, opcode, type) }
}
class SemInitializeParameterExpr extends SemNullaryExpr {
SemInitializeParameterExpr() { opcode instanceof Opcode::InitializeParameter }
}
class SemLoadExpr extends SemNullaryExpr {
SemLoadExpr() { opcode instanceof Opcode::Load }
final SemSsaVariable getDef() { result.getAUse() = this }
}
class SemSsaLoadExpr extends SemLoadExpr {
SemSsaLoadExpr() { exists(getDef()) }
}
class SemNonSsaLoadExpr extends SemLoadExpr {
SemNonSsaLoadExpr() { not exists(getDef()) }
}
class SemStoreExpr extends SemUnaryExpr {
SemStoreExpr() { opcode instanceof Opcode::Store }
}
class SemConditionalExpr extends SemKnownExpr {
SemExpr condition;
SemExpr trueResult;
SemExpr falseResult;
SemConditionalExpr() {
opcode instanceof Opcode::Conditional and
Specific::conditionalExpr(this, type, condition, trueResult, falseResult)
}
final SemExpr getBranchExpr(boolean branch) {
branch = true and result = trueResult
or
branch = false and result = falseResult
}
}

View File

@@ -0,0 +1,297 @@
/**
* C++-specific implementation of the semantic interface.
*/
private import cpp as Cpp
private import semmle.code.cpp.ir.IR as IR
private import Semantic
private import experimental.semmle.code.cpp.rangeanalysis.Bound as IRBound
private import semmle.code.cpp.controlflow.IRGuards as IRGuards
module SemanticExprConfig {
class Location = Cpp::Location;
class Expr = IR::Instruction;
SemBasicBlock getExprBasicBlock(Expr e) { result = getSemanticBasicBlock(e.getBlock()) }
private predicate anyConstantExpr(Expr expr, SemType type, string value) {
exists(IR::ConstantInstruction instr | instr = expr |
type = getSemanticType(instr.getResultIRType()) and
value = instr.getValue()
)
}
predicate integerLiteral(Expr expr, SemIntegerType type, int value) {
exists(string valueString |
anyConstantExpr(expr, type, valueString) and
value = valueString.toInt()
)
}
predicate largeIntegerLiteral(Expr expr, SemIntegerType type, float approximateFloatValue) {
exists(string valueString |
anyConstantExpr(expr, type, valueString) and
not exists(valueString.toInt()) and
approximateFloatValue = valueString.toFloat()
)
}
predicate floatingPointLiteral(Expr expr, SemFloatingPointType type, float value) {
exists(string valueString |
anyConstantExpr(expr, type, valueString) and value = valueString.toFloat()
)
}
predicate booleanLiteral(Expr expr, SemBooleanType type, boolean value) {
exists(string valueString |
anyConstantExpr(expr, type, valueString) and
(
valueString = "true" and value = true
or
valueString = "false" and value = false
)
)
}
predicate nullLiteral(Expr expr, SemAddressType type) { anyConstantExpr(expr, type, _) }
predicate stringLiteral(Expr expr, SemType type, string value) {
anyConstantExpr(expr, type, value) and expr instanceof IR::StringConstantInstruction
}
predicate binaryExpr(Expr expr, Opcode opcode, SemType type, Expr leftOperand, Expr rightOperand) {
exists(IR::BinaryInstruction instr | instr = expr |
type = getSemanticType(instr.getResultIRType()) and
leftOperand = instr.getLeft() and
rightOperand = instr.getRight() and
// REVIEW: Merge the two `Opcode` types.
opcode.toString() = instr.getOpcode().toString()
)
}
predicate unaryExpr(Expr expr, Opcode opcode, SemType type, Expr operand) {
type = getSemanticType(expr.getResultIRType()) and
(
exists(IR::UnaryInstruction instr | instr = expr |
operand = instr.getUnary() and
// REVIEW: Merge the two operand types.
opcode.toString() = instr.getOpcode().toString()
)
or
exists(IR::StoreInstruction instr | instr = expr |
operand = instr.getSourceValue() and
opcode instanceof Opcode::Store
)
)
}
predicate nullaryExpr(Expr expr, Opcode opcode, SemType type) {
type = getSemanticType(expr.getResultIRType()) and
(
expr instanceof IR::LoadInstruction and opcode instanceof Opcode::Load
or
expr instanceof IR::InitializeParameterInstruction and
opcode instanceof Opcode::InitializeParameter
)
}
predicate conditionalExpr(
Expr expr, SemType type, Expr condition, Expr trueResult, Expr falseResult
) {
none()
}
SemType getUnknownExprType(Expr expr) { result = getSemanticType(expr.getResultIRType()) }
class BasicBlock = IR::IRBlock;
predicate bbDominates(BasicBlock dominator, BasicBlock dominated) {
dominator.dominates(dominated)
}
predicate hasDominanceInformation(BasicBlock block) { any() }
int getBasicBlockUniqueId(BasicBlock block) {
// REVIEW: `getDisplayIndex()` is not intended for use in real queries, but for now it's the
// best we can do because `equivalentRelation` won't accept a predicate whose parameters are IPA
// types.
result = block.getDisplayIndex()
}
class SsaVariable instanceof IR::Instruction {
SsaVariable() { super.hasMemoryResult() }
final string toString() { result = super.toString() }
final Location getLocation() { result = super.getLocation() }
}
predicate explicitUpdate(SsaVariable v, Expr sourceExpr) { v = sourceExpr }
predicate phi(SsaVariable v) { v instanceof IR::PhiInstruction }
SsaVariable getAPhiInput(SsaVariable v) { result = v.(IR::PhiInstruction).getAnInput() }
Expr getAUse(SsaVariable v) { result.(IR::LoadInstruction).getSourceValue() = v }
SemType getSsaVariableType(SsaVariable v) {
result = getSemanticType(v.(IR::Instruction).getResultIRType())
}
BasicBlock getSsaVariableBasicBlock(SsaVariable v) { result = v.(IR::Instruction).getBlock() }
private newtype TReadPosition =
TReadPositionBlock(IR::IRBlock block) or
TReadPositionPhiInputEdge(IR::IRBlock pred, IR::IRBlock succ) {
exists(IR::PhiInputOperand input |
pred = input.getPredecessorBlock() and
succ = input.getUse().getBlock()
)
}
class SsaReadPosition extends TReadPosition {
string toString() { none() }
Location getLocation() { none() }
predicate hasRead(SsaVariable v) { none() }
}
private class SsaReadPositionBlock extends SsaReadPosition, TReadPositionBlock {
IR::IRBlock block;
SsaReadPositionBlock() { this = TReadPositionBlock(block) }
final override string toString() { result = block.toString() }
final override Location getLocation() { result = block.getLocation() }
final override predicate hasRead(SsaVariable v) {
exists(IR::Operand operand |
operand.getDef() = v and not operand instanceof IR::PhiInputOperand
)
}
}
private class SsaReadPositionPhiInputEdge extends SsaReadPosition, TReadPositionPhiInputEdge {
IR::IRBlock pred;
IR::IRBlock succ;
SsaReadPositionPhiInputEdge() { this = TReadPositionPhiInputEdge(pred, succ) }
final override string toString() { result = pred.toString() + "->" + succ.toString() }
final override Location getLocation() { result = succ.getLocation() }
final override predicate hasRead(SsaVariable v) {
exists(IR::PhiInputOperand operand |
operand.getDef() = v and
operand.getPredecessorBlock() = pred and
operand.getUse().getBlock() = succ
)
}
}
predicate hasReadOfSsaVariable(SsaReadPosition pos, SsaVariable v) { pos.hasRead(v) }
predicate readBlock(SsaReadPosition pos, BasicBlock block) { pos = TReadPositionBlock(block) }
predicate phiInputEdge(SsaReadPosition pos, BasicBlock origBlock, BasicBlock phiBlock) {
pos = TReadPositionPhiInputEdge(origBlock, phiBlock)
}
predicate phiInput(SsaReadPosition pos, SsaVariable phi, SsaVariable input) {
exists(IR::PhiInputOperand operand |
pos = TReadPositionPhiInputEdge(operand.getPredecessorBlock(), operand.getUse().getBlock())
|
phi = operand.getUse() and input = operand.getDef()
)
}
class Bound instanceof IRBound::Bound {
Bound() {
this instanceof IRBound::ZeroBound
or
this.(IRBound::ValueNumberBound).getValueNumber().getAnInstruction() instanceof SsaVariable
}
string toString() { result = super.toString() }
final Location getLocation() { result = super.getLocation() }
}
private class ValueNumberBound extends Bound {
IRBound::ValueNumberBound bound;
ValueNumberBound() { bound = this }
override string toString() {
result =
min(SsaVariable instr |
instr = bound.getValueNumber().getAnInstruction()
|
instr
order by
instr.(IR::Instruction).getBlock().getDisplayIndex(),
instr.(IR::Instruction).getDisplayIndexInBlock()
).toString()
}
}
predicate zeroBound(Bound bound) { bound instanceof IRBound::ZeroBound }
predicate ssaBound(Bound bound, SsaVariable v) {
v = bound.(IRBound::ValueNumberBound).getValueNumber().getAnInstruction()
}
Expr getBoundExpr(Bound bound, int delta) {
result = bound.(IRBound::Bound).getInstruction(delta)
}
class Guard = IRGuards::IRGuardCondition;
predicate guard(Guard guard, BasicBlock block) {
block = guard.(IRGuards::IRGuardCondition).getBlock()
}
Expr getGuardAsExpr(Guard guard) { result = guard }
predicate equalityGuard(Guard guard, Expr e1, Expr e2, boolean polarity) {
guard.(IRGuards::IRGuardCondition).comparesEq(e1.getAUse(), e2.getAUse(), 0, true, polarity)
}
predicate guardDirectlyControlsBlock(Guard guard, BasicBlock controlled, boolean branch) {
guard.(IRGuards::IRGuardCondition).controls(controlled, branch)
}
predicate guardHasBranchEdge(Guard guard, BasicBlock bb1, BasicBlock bb2, boolean branch) {
guard.(IRGuards::IRGuardCondition).controlsEdge(bb1, bb2, branch)
}
Guard comparisonGuard(Expr e) { result = e }
predicate implies_v2(Guard g1, boolean b1, Guard g2, boolean b2) {
none() // TODO
}
}
SemExpr getSemanticExpr(IR::Instruction instr) { result = instr }
IR::Instruction getCppInstruction(SemExpr e) { e = result }
SemBasicBlock getSemanticBasicBlock(IR::IRBlock block) { result = block }
IR::IRBlock getCppBasicBlock(SemBasicBlock block) { block = result }
SemSsaVariable getSemanticSsaVariable(IR::Instruction instr) { result = instr }
IR::Instruction getCppSsaVariableInstruction(SemSsaVariable v) { v = result }
SemBound getSemanticBound(IRBound::Bound bound) { result = bound }
IRBound::Bound getCppBound(SemBound bound) { bound = result }
SemGuard getSemanticGuard(IRGuards::IRGuardCondition guard) { result = guard }
IRGuards::IRGuardCondition getCppGuard(SemGuard guard) { guard = result }

View File

@@ -0,0 +1,65 @@
/**
* Semantic interface to the guards library.
*/
private import Semantic
private import SemanticExprSpecific::SemanticExprConfig as Specific
class SemGuard instanceof Specific::Guard {
SemBasicBlock block;
SemGuard() { Specific::guard(this, block) }
final string toString() { result = super.toString() }
final Specific::Location getLocation() { result = super.getLocation() }
final predicate isEquality(SemExpr e1, SemExpr e2, boolean polarity) {
Specific::equalityGuard(this, e1, e2, polarity)
}
final predicate directlyControls(SemBasicBlock controlled, boolean branch) {
Specific::guardDirectlyControlsBlock(this, controlled, branch)
}
final predicate hasBranchEdge(SemBasicBlock bb1, SemBasicBlock bb2, boolean branch) {
Specific::guardHasBranchEdge(this, bb1, bb2, branch)
}
final SemBasicBlock getBasicBlock() { result = block }
final SemExpr asExpr() { result = Specific::getGuardAsExpr(this) }
}
predicate semImplies_v2(SemGuard g1, boolean b1, SemGuard g2, boolean b2) {
Specific::implies_v2(g1, b1, g2, b2)
}
/**
* Holds if `guard` directly controls the position `controlled` with the
* value `testIsTrue`.
*/
predicate semGuardDirectlyControlsSsaRead(
SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue
) {
guard.directlyControls(controlled.(SemSsaReadPositionBlock).getBlock(), testIsTrue)
or
exists(SemSsaReadPositionPhiInputEdge controlledEdge | controlledEdge = controlled |
guard.directlyControls(controlledEdge.getOrigBlock(), testIsTrue) or
guard.hasBranchEdge(controlledEdge.getOrigBlock(), controlledEdge.getPhiBlock(), testIsTrue)
)
}
/**
* Holds if `guard` controls the position `controlled` with the value `testIsTrue`.
*/
predicate semGuardControlsSsaRead(SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue) {
semGuardDirectlyControlsSsaRead(guard, controlled, testIsTrue)
or
exists(SemGuard guard0, boolean testIsTrue0 |
semImplies_v2(guard0, testIsTrue0, guard, testIsTrue) and
semGuardControlsSsaRead(guard0, controlled, testIsTrue0)
)
}
SemGuard semGetComparisonGuard(SemRelationalExpr e) { result = Specific::comparisonGuard(e) }

View File

@@ -0,0 +1,179 @@
/**
* Definitions of all possible opcodes for `SemExpr`.
*/
private newtype TOpcode =
TInitializeParameter() or
TCopyValue() or
TLoad() or
TStore() or
TAdd() or
TSub() or
TMul() or
TDiv() or
TRem() or
TNegate() or
TShiftLeft() or
TShiftRight() or
TShiftRightUnsigned() or // TODO: Based on type
TBitAnd() or
TBitOr() or
TBitXor() or
TBitComplement() or
TLogicalNot() or
TCompareEQ() or
TCompareNE() or
TCompareLT() or
TCompareGT() or
TCompareLE() or
TCompareGE() or
TPointerAdd() or
TPointerSub() or
TPointerDiff() or
TConvert() or
TConstant() or
TStringConstant() or
TAddOne() or // TODO: Combine with `TAdd`
TSubOne() or // TODO: Combine with `TSub`
TConditional() or // TODO: Represent as flow
TCall() or
TBox() or
TUnbox() or
TUnknown()
class Opcode extends TOpcode {
string toString() { result = "???" }
}
module Opcode {
class InitializeParameter extends Opcode, TInitializeParameter {
override string toString() { result = "InitializeParameter" }
}
class CopyValue extends Opcode, TCopyValue {
override string toString() { result = "CopyValue" }
}
class Load extends Opcode, TLoad {
override string toString() { result = "Load" }
}
class Store extends Opcode, TStore {
override string toString() { result = "Store" }
}
class Add extends Opcode, TAdd {
override string toString() { result = "Add" }
}
class Sub extends Opcode, TSub {
override string toString() { result = "Sub" }
}
class Mul extends Opcode, TMul {
override string toString() { result = "Mul" }
}
class Div extends Opcode, TDiv {
override string toString() { result = "Div" }
}
class Rem extends Opcode, TRem {
override string toString() { result = "Rem" }
}
class Negate extends Opcode, TNegate {
override string toString() { result = "Negate" }
}
class ShiftLeft extends Opcode, TShiftLeft {
override string toString() { result = "ShiftLeft" }
}
class ShiftRight extends Opcode, TShiftRight {
override string toString() { result = "ShiftRight" }
}
class ShiftRightUnsigned extends Opcode, TShiftRightUnsigned {
override string toString() { result = "ShiftRightUnsigned" }
}
class BitAnd extends Opcode, TBitAnd {
override string toString() { result = "BitAnd" }
}
class BitOr extends Opcode, TBitOr {
override string toString() { result = "BitOr" }
}
class BitXor extends Opcode, TBitXor {
override string toString() { result = "BitXor" }
}
class BitComplement extends Opcode, TBitComplement {
override string toString() { result = "BitComplement" }
}
class LogicalNot extends Opcode, TLogicalNot {
override string toString() { result = "LogicalNot" }
}
class CompareEQ extends Opcode, TCompareEQ {
override string toString() { result = "CompareEQ" }
}
class CompareNE extends Opcode, TCompareNE {
override string toString() { result = "CompareNE" }
}
class CompareLT extends Opcode, TCompareLT {
override string toString() { result = "CompareLT" }
}
class CompareLE extends Opcode, TCompareLE {
override string toString() { result = "CompareLE" }
}
class CompareGT extends Opcode, TCompareGT {
override string toString() { result = "CompareGT" }
}
class CompareGE extends Opcode, TCompareGE {
override string toString() { result = "CompareGE" }
}
class Convert extends Opcode, TConvert {
override string toString() { result = "Convert" }
}
class AddOne extends Opcode, TAddOne {
override string toString() { result = "AddOne" }
}
class SubOne extends Opcode, TSubOne {
override string toString() { result = "SubOne" }
}
class Conditional extends Opcode, TConditional {
override string toString() { result = "Conditional" }
}
class Constant extends Opcode, TConstant {
override string toString() { result = "Constant" }
}
class StringConstant extends Opcode, TStringConstant {
override string toString() { result = "StringConstant" }
}
class Box extends Opcode, TBox {
override string toString() { result = "Box" }
}
class Unbox extends Opcode, TUnbox {
override string toString() { result = "Unbox" }
}
class Unknown extends Opcode, TUnknown {
override string toString() { result = "Unknown" }
}
}

View File

@@ -0,0 +1,75 @@
/**
* Semantic interface to the SSA library.
*/
private import Semantic
private import SemanticExprSpecific::SemanticExprConfig as Specific
class SemSsaVariable instanceof Specific::SsaVariable {
final string toString() { result = super.toString() }
final Specific::Location getLocation() { result = super.getLocation() }
final SemLoadExpr getAUse() { result = Specific::getAUse(this) }
final SemType getType() { result = Specific::getSsaVariableType(this) }
final SemBasicBlock getBasicBlock() { result = Specific::getSsaVariableBasicBlock(this) }
}
class SemSsaExplicitUpdate extends SemSsaVariable {
SemExpr sourceExpr;
SemSsaExplicitUpdate() { Specific::explicitUpdate(this, sourceExpr) }
final SemExpr getSourceExpr() { result = sourceExpr }
}
class SemSsaPhiNode extends SemSsaVariable {
SemSsaPhiNode() { Specific::phi(this) }
final SemSsaVariable getAPhiInput() { result = Specific::getAPhiInput(this) }
}
class SemSsaReadPosition instanceof Specific::SsaReadPosition {
final string toString() { result = super.toString() }
final Specific::Location getLocation() { result = super.getLocation() }
final predicate hasReadOfVar(SemSsaVariable var) { Specific::hasReadOfSsaVariable(this, var) }
}
class SemSsaReadPositionPhiInputEdge extends SemSsaReadPosition {
SemBasicBlock origBlock;
SemBasicBlock phiBlock;
SemSsaReadPositionPhiInputEdge() { Specific::phiInputEdge(this, origBlock, phiBlock) }
predicate phiInput(SemSsaPhiNode phi, SemSsaVariable inp) { Specific::phiInput(this, phi, inp) }
SemBasicBlock getOrigBlock() { result = origBlock }
SemBasicBlock getPhiBlock() { result = phiBlock }
}
class SemSsaReadPositionBlock extends SemSsaReadPosition {
SemBasicBlock block;
SemSsaReadPositionBlock() { Specific::readBlock(this, block) }
SemBasicBlock getBlock() { result = block }
SemExpr getAnExpr() { result = getBlock().getAnExpr() }
}
/**
* Holds if `inp` is an input to `phi` along a back edge.
*/
predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge) {
edge.phiInput(phi, inp) and
// Conservatively assume that every edge is a back edge if we don't have dominance information.
(
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
not edge.getOrigBlock().hasDominanceInformation()
)
}

View File

@@ -0,0 +1,301 @@
/**
* Minimal, language-neutral type system for semantic analysis.
*/
private import SemanticTypeSpecific as Specific
class LanguageType = Specific::Type;
cached
private newtype TSemType =
TSemVoidType() { Specific::voidType(_) } or
TSemUnknownType() { Specific::unknownType(_) } or
TSemErrorType() { Specific::errorType(_) } or
TSemBooleanType(int byteSize) { Specific::booleanType(_, byteSize) } or
TSemIntegerType(int byteSize, boolean signed) { Specific::integerType(_, byteSize, signed) } or
TSemFloatingPointType(int byteSize) { Specific::floatingPointType(_, byteSize) } or
TSemAddressType(int byteSize) { Specific::addressType(_, byteSize) } or
TSemFunctionAddressType(int byteSize) { Specific::functionAddressType(_, byteSize) } or
TSemOpaqueType(int byteSize, Specific::OpaqueTypeTag tag) {
Specific::opaqueType(_, byteSize, tag)
}
/**
* The language-neutral type of a semantic expression,
* The interface to `SemType` and its subclasses is the same across all languages for which the IR
* is supported, so analyses that expect to be used for multiple languages should generally use
* `SemType` rather than a language-specific type.
*
* Many types from the language-specific type system will map to a single canonical `SemType`. Two
* types that map to the same `SemType` are considered equivalent by semantic analysis. As an
* example, in C++, all pointer types map to the same instance of `SemAddressType`.
*/
class SemType extends TSemType {
/** Gets a textual representation of this type. */
string toString() { none() }
/**
* Gets a string that uniquely identifies this `SemType`. This string is often the same as the
* result of `SemType.toString()`, but for some types it may be more verbose to ensure uniqueness.
*/
string getIdentityString() { result = toString() }
/**
* Gets the size of the type, in bytes, if known.
*
* This will hold for all `SemType` objects except `SemUnknownType` and `SemErrorType`.
*/
// This predicate is overridden with `pragma[noinline]` in every leaf subclass.
// This allows callers to ask for things like _the_ floating-point type of
// size 4 without getting a join that first finds all types of size 4 and
// _then_ restricts them to floating-point types.
int getByteSize() { none() }
}
/**
* An unknown type. Generally used to represent results and operands that access an unknown set of
* memory locations, such as the side effects of a function call.
*/
class SemUnknownType extends SemType, TSemUnknownType {
final override string toString() { result = "unknown" }
final override int getByteSize() { none() }
}
/**
* A void type, which has no values. Used to represent the result type of an expression that does
* not produce a result.
*/
class SemVoidType extends SemType, TSemVoidType {
final override string toString() { result = "void" }
final override int getByteSize() { result = 0 }
}
/**
* An error type. Used when an error in the source code prevents the extractor from determining the
* proper type.
*/
class SemErrorType extends SemType, TSemErrorType {
final override string toString() { result = "error" }
final override int getByteSize() { result = 0 }
}
private class SemSizedType extends SemType {
int byteSize;
SemSizedType() {
this = TSemBooleanType(byteSize) or
this = TSemIntegerType(byteSize, _) or
this = TSemFloatingPointType(byteSize) or
this = TSemAddressType(byteSize) or
this = TSemFunctionAddressType(byteSize) or
this = TSemOpaqueType(byteSize, _)
}
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
// overridden only in the leaf classes.
}
/**
* A Boolean type, which can hold the values `true` (non-zero) or `false` (zero).
*/
class SemBooleanType extends SemSizedType, TSemBooleanType {
final override string toString() { result = "bool" + byteSize.toString() }
pragma[noinline]
final override int getByteSize() { result = byteSize }
}
/**
* A numeric type. This includes `SemSignedIntegerType`, `SemUnsignedIntegerType`, and
* `SemFloatingPointType`.
*/
class SemNumericType extends SemSizedType {
SemNumericType() {
this = TSemIntegerType(byteSize, _) or
this = TSemFloatingPointType(byteSize)
}
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
// overridden only in the leaf classes.
}
/**
* An integer type. This includes `SemSignedIntegerType` and `SemUnsignedIntegerType`.
*/
class SemIntegerType extends SemNumericType {
boolean signed;
SemIntegerType() { this = TSemIntegerType(byteSize, signed) }
/** Holds if this integer type is signed. */
final predicate isSigned() { signed = true }
/** Holds if this integer type is unsigned. */
final predicate isUnsigned() { not isSigned() }
// Don't override `getByteSize()` here. The optimizer seems to generate better code when this is
// overridden only in the leaf classes.
}
/**
* A signed two's-complement integer. Also used to represent enums whose underlying type is a signed
* integer, as well as character types whose representation is signed.
*/
class SemSignedIntegerType extends SemIntegerType {
SemSignedIntegerType() { signed = true }
final override string toString() { result = "int" + byteSize.toString() }
pragma[noinline]
final override int getByteSize() { result = byteSize }
}
/**
* An unsigned two's-complement integer. Also used to represent enums whose underlying type is an
* unsigned integer, as well as character types whose representation is unsigned.
*/
class SemUnsignedIntegerType extends SemIntegerType {
SemUnsignedIntegerType() { signed = false }
final override string toString() { result = "uint" + byteSize.toString() }
pragma[noinline]
final override int getByteSize() { result = byteSize }
}
/**
* A floating-point type.
*/
class SemFloatingPointType extends SemNumericType, TSemFloatingPointType {
final override string toString() { result = "float" + byteSize.toString() }
pragma[noinline]
final override int getByteSize() { result = byteSize }
}
/**
* An address type, representing the memory address of data. Used to represent pointers, references,
* and lvalues, include those that are garbage collected.
*
* The address of a function is represented by the separate `SemFunctionAddressType`.
*/
class SemAddressType extends SemSizedType, TSemAddressType {
final override string toString() { result = "addr" + byteSize.toString() }
pragma[noinline]
final override int getByteSize() { result = byteSize }
}
/**
* An address type, representing the memory address of code. Used to represent function pointers,
* function references, and the target of a direct function call.
*/
class SemFunctionAddressType extends SemSizedType, TSemFunctionAddressType {
final override string toString() { result = "func" + byteSize.toString() }
pragma[noinline]
final override int getByteSize() { result = byteSize }
}
/**
* A type with known size that does not fit any of the other kinds of type. Used to represent
* classes, structs, unions, fixed-size arrays, pointers-to-member, and more.
*/
class SemOpaqueType extends SemSizedType, TSemOpaqueType {
Specific::OpaqueTypeTag tag;
SemOpaqueType() { this = TSemOpaqueType(byteSize, tag) }
final override string toString() {
result = "opaque" + byteSize.toString() + "{" + tag.toString() + "}"
}
final override string getIdentityString() {
result = "opaque" + byteSize.toString() + "{" + Specific::getOpaqueTagIdentityString(tag) + "}"
}
/**
* Gets the "tag" that differentiates this type from other incompatible opaque types that have the
* same size.
*/
final Specific::OpaqueTypeTag getTag() { result = tag }
pragma[noinline]
final override int getByteSize() { result = byteSize }
}
cached
SemType getSemanticType(Specific::Type type) {
exists(int byteSize |
Specific::booleanType(type, byteSize) and result = TSemBooleanType(byteSize)
or
exists(boolean signed |
Specific::integerType(type, byteSize, signed) and
result = TSemIntegerType(byteSize, signed)
)
or
Specific::floatingPointType(type, byteSize) and result = TSemFloatingPointType(byteSize)
or
Specific::addressType(type, byteSize) and result = TSemAddressType(byteSize)
or
Specific::functionAddressType(type, byteSize) and result = TSemFunctionAddressType(byteSize)
or
exists(Specific::OpaqueTypeTag tag |
Specific::opaqueType(type, byteSize, tag) and result = TSemOpaqueType(byteSize, tag)
)
)
or
Specific::errorType(type) and result = TSemErrorType()
or
Specific::unknownType(type) and result = TSemUnknownType()
}
/**
* Holds if the conversion from `fromType` to `toType` can never overflow or underflow.
*/
predicate conversionCannotOverflow(SemNumericType fromType, SemNumericType toType) {
// Identity cast
fromType = toType
or
// Treat any cast to an FP type as safe. It can lose precision, but not overflow.
toType instanceof SemFloatingPointType and fromType = any(SemNumericType n)
or
exists(SemIntegerType fromInteger, SemIntegerType toInteger, int fromSize, int toSize |
fromInteger = fromType and
toInteger = toType and
fromSize = fromInteger.getByteSize() and
toSize = toInteger.getByteSize()
|
// Conversion to a larger type. Safe unless converting signed -> unsigned.
fromSize < toSize and
(
toInteger.isSigned()
or
not fromInteger.isSigned()
)
)
}
/**
* INTERNAL: Do not use.
* Query predicates used to check invariants that should hold for all `SemType` objects.
*/
module SemTypeConsistency {
/**
* Holds if the type has no result for `getSemanticType()`.
*/
query predicate missingSemType(Specific::Type type, string message) {
not exists(getSemanticType(type)) and
message = "`Type` does not have a corresponding `SemType`."
}
/**
* Holds if the type has more than one result for `getSemanticType()`.
*/
query predicate multipleSemTypes(Specific::Type type, string message) {
strictcount(getSemanticType(type)) > 1 and
message =
"`Type` " + type + " has multiple `SemType`s: " +
concat(getSemanticType(type).toString(), ", ")
}
}

View File

@@ -0,0 +1,43 @@
/**
* C++-specific implementation of the semantic type system.
*/
private import semmle.code.cpp.ir.IR as IR
private import cpp as Cpp
private import semmle.code.cpp.ir.internal.IRCppLanguage as Language
class Type = IR::IRType;
class OpaqueTypeTag = Language::OpaqueTypeTag;
predicate voidType(Type type) { type instanceof IR::IRVoidType }
predicate errorType(Type type) { type instanceof IR::IRErrorType }
predicate unknownType(Type type) { type instanceof IR::IRUnknownType }
predicate booleanType(Type type, int byteSize) { byteSize = type.(IR::IRBooleanType).getByteSize() }
predicate integerType(Type type, int byteSize, boolean signed) {
byteSize = type.(IR::IRSignedIntegerType).getByteSize() and signed = true
or
byteSize = type.(IR::IRUnsignedIntegerType).getByteSize() and signed = false
}
predicate floatingPointType(Type type, int byteSize) {
byteSize = type.(IR::IRFloatingPointType).getByteSize()
}
predicate addressType(Type type, int byteSize) { byteSize = type.(IR::IRAddressType).getByteSize() }
predicate functionAddressType(Type type, int byteSize) {
byteSize = type.(IR::IRFunctionAddressType).getByteSize()
}
predicate opaqueType(Type type, int byteSize, OpaqueTypeTag tag) {
exists(IR::IROpaqueType opaque | opaque = type |
byteSize = opaque.getByteSize() and tag = opaque.getTag()
)
}
predicate getOpaqueTagIdentityString = Language::getOpaqueTagIdentityString/1;

View File

@@ -0,0 +1,31 @@
/**
* Simple constant analysis using the Semantic interface.
*/
private import experimental.semmle.code.cpp.semantic.Semantic
private import ConstantAnalysisSpecific as Specific
/** An expression that always has the same integer value. */
pragma[nomagic]
private predicate constantIntegerExpr(SemExpr e, int val) {
// An integer literal
e.(SemIntegerLiteralExpr).getIntValue() = val
or
// Copy of another constant
exists(SemSsaExplicitUpdate v, SemExpr src |
e = v.getAUse() and
src = v.getSourceExpr() and
constantIntegerExpr(src, val)
)
or
// Language-specific enhancements
val = Specific::getIntConstantValue(e)
}
/** An expression that always has the same integer value. */
class SemConstantIntegerExpr extends SemExpr {
SemConstantIntegerExpr() { constantIntegerExpr(this, _) }
/** Gets the integer value of this expression. */
int getIntValue() { constantIntegerExpr(this, result) }
}

View File

@@ -0,0 +1,10 @@
/**
* C++-specific implementation of constant analysis.
*/
private import experimental.semmle.code.cpp.semantic.Semantic
/**
* Gets the constant integer value of the specified expression, if any.
*/
int getIntConstantValue(SemExpr expr) { none() }

View File

@@ -0,0 +1,310 @@
/**
* Provides inferences of the form: `e` equals `b + v` modulo `m` where `e` is
* an expression, `b` is a `Bound` (typically zero or the value of an SSA
* variable), and `v` is an integer in the range `[0 .. m-1]`.
*/
private import ModulusAnalysisSpecific::Private
private import experimental.semmle.code.cpp.semantic.Semantic
private import ConstantAnalysis
private import RangeUtils
/**
* Holds if `e + delta` equals `v` at `pos`.
*/
private predicate valueFlowStepSsa(SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta) {
semSsaUpdateStep(v, e, delta) and pos.hasReadOfVar(v)
or
exists(SemGuard guard, boolean testIsTrue |
pos.hasReadOfVar(v) and
guard = semEqFlowCond(v, e, delta, true, testIsTrue) and
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
)
}
/**
* Holds if `add` is the addition of `larg` and `rarg`, neither of which are
* `ConstantIntegerExpr`s.
*/
private predicate nonConstAddition(SemExpr add, SemExpr larg, SemExpr rarg) {
exists(SemAddExpr a | a = add |
larg = a.getLeftOperand() and
rarg = a.getRightOperand()
) and
not larg instanceof SemConstantIntegerExpr and
not rarg instanceof SemConstantIntegerExpr
}
/**
* Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not
* a `ConstantIntegerExpr`.
*/
private predicate nonConstSubtraction(SemExpr sub, SemExpr larg, SemExpr rarg) {
exists(SemSubExpr s | s = sub |
larg = s.getLeftOperand() and
rarg = s.getRightOperand()
) and
not rarg instanceof SemConstantIntegerExpr
}
/** Gets an expression that is the remainder modulo `mod` of `arg`. */
private SemExpr modExpr(SemExpr arg, int mod) {
exists(SemRemExpr rem |
result = rem and
arg = rem.getLeftOperand() and
rem.getRightOperand().(SemConstantIntegerExpr).getIntValue() = mod and
mod >= 2
)
or
exists(SemConstantIntegerExpr c |
mod = 2.pow([1 .. 30]) and
c.getIntValue() = mod - 1 and
result.(SemBitAndExpr).hasOperands(arg, c)
)
}
/**
* Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on
* its `testIsTrue` branch.
*/
private SemGuard moduloCheck(SemSsaVariable v, int val, int mod, boolean testIsTrue) {
exists(SemExpr rem, SemConstantIntegerExpr c, int r, boolean polarity |
result.isEquality(rem, c, polarity) and
c.getIntValue() = r and
rem = modExpr(v.getAUse(), mod) and
(
testIsTrue = polarity and val = r
or
testIsTrue = polarity.booleanNot() and
mod = 2 and
val = 1 - r and
(r = 0 or r = 1)
)
)
}
/**
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
*/
private predicate moduloGuardedRead(SemSsaVariable v, SemSsaReadPosition pos, int val, int mod) {
exists(SemGuard guard, boolean testIsTrue |
pos.hasReadOfVar(v) and
guard = moduloCheck(v, val, mod, testIsTrue) and
semGuardControlsSsaRead(guard, pos, testIsTrue)
)
}
/** Holds if `factor` is a power of 2 that divides `mask`. */
bindingset[mask]
private predicate andmaskFactor(int mask, int factor) {
mask % factor = 0 and
factor = 2.pow([1 .. 30])
}
/** Holds if `e` is evenly divisible by `factor`. */
private predicate evenlyDivisibleExpr(SemExpr e, int factor) {
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() |
e.(SemMulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2
or
e.(SemShiftLeftExpr).getRightOperand() = c and factor = 2.pow(k) and k > 0
or
e.(SemBitAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f))
)
}
/**
* Holds if `rix` is the number of input edges to `phi`.
*/
private predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
rix = max(int r | rankedPhiInput(phi, _, _, r))
}
/**
* Gets the remainder of `val` modulo `mod`.
*
* For `mod = 0` the result equals `val` and for `mod > 1` the result is within
* the range `[0 .. mod-1]`.
*/
bindingset[val, mod]
private int remainder(int val, int mod) {
mod = 0 and result = val
or
mod > 1 and result = ((val % mod) + mod) % mod
}
/**
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
*/
private predicate phiSelfModulus(
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod
) {
exists(SemSsaBound phibound, int v, int m |
edge.phiInput(phi, inp) and
phibound.getAVariable() = phi and
ssaModulus(inp, edge, phibound, v, m) and
mod = m.gcd(v) and
mod != 1
)
}
/**
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
*/
private predicate phiModulusInit(SemSsaPhiNode phi, SemBound b, int val, int mod) {
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
edge.phiInput(phi, inp) and
ssaModulus(inp, edge, b, val, mod)
)
}
/**
* Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`.
*/
private predicate phiModulusRankStep(SemSsaPhiNode phi, SemBound b, int val, int mod, int rix) {
rix = 0 and
phiModulusInit(phi, b, val, mod)
or
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int v1, int m1 |
mod != 1 and
val = remainder(v1, mod)
|
exists(int v2, int m2 |
rankedPhiInput(phi, inp, edge, rix) and
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
ssaModulus(inp, edge, b, v2, m2) and
mod = m1.gcd(m2).gcd(v1 - v2)
)
or
exists(int m2 |
rankedPhiInput(phi, inp, edge, rix) and
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
phiSelfModulus(phi, inp, edge, m2) and
mod = m1.gcd(m2)
)
)
}
/**
* Holds if `phi` is equal to `b + val` modulo `mod`.
*/
private predicate phiModulus(SemSsaPhiNode phi, SemBound b, int val, int mod) {
exists(int r |
maxPhiInputRank(phi, r) and
phiModulusRankStep(phi, b, val, mod, r)
)
}
/**
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
*/
private predicate ssaModulus(SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int val, int mod) {
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
or
b.(SemSsaBound).getAVariable() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0
or
exists(SemExpr e, int val0, int delta |
semExprModulus(e, b, val0, mod) and
valueFlowStepSsa(v, pos, e, delta) and
val = remainder(val0 + delta, mod)
)
or
moduloGuardedRead(v, pos, val, mod) and b instanceof SemZeroBound
}
/**
* Holds if `e` is equal to `b + val` modulo `mod`.
*
* There are two cases for the modulus:
* - `mod = 0`: The equality `e = b + val` is an ordinary equality.
* - `mod > 1`: `val` lies within the range `[0 .. mod-1]`.
*/
cached
predicate semExprModulus(SemExpr e, SemBound b, int val, int mod) {
not ignoreExprModulus(e) and
(
e = b.getExpr(val) and mod = 0
or
evenlyDivisibleExpr(e, mod) and
val = 0 and
b instanceof SemZeroBound
or
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
ssaModulus(v, bb, b, val, mod) and
e = v.getAUse() and
bb.getAnExpr() = e
)
or
exists(SemExpr mid, int val0, int delta |
semExprModulus(mid, b, val0, mod) and
semValueFlowStep(e, mid, delta) and
val = remainder(val0 + delta, mod)
)
or
exists(SemConditionalExpr cond, int v1, int v2, int m1, int m2 |
cond = e and
condExprBranchModulus(cond, true, b, v1, m1) and
condExprBranchModulus(cond, false, b, v2, m2) and
mod = m1.gcd(m2).gcd(v1 - v2) and
mod != 1 and
val = remainder(v1, mod)
)
or
exists(SemBound b1, SemBound b2, int v1, int v2, int m1, int m2 |
addModulus(e, true, b1, v1, m1) and
addModulus(e, false, b2, v2, m2) and
mod = m1.gcd(m2) and
mod != 1 and
val = remainder(v1 + v2, mod)
|
b = b1 and b2 instanceof SemZeroBound
or
b = b2 and b1 instanceof SemZeroBound
)
or
exists(int v1, int v2, int m1, int m2 |
subModulus(e, true, b, v1, m1) and
subModulus(e, false, any(SemZeroBound zb), v2, m2) and
mod = m1.gcd(m2) and
mod != 1 and
val = remainder(v1 - v2, mod)
)
)
}
private predicate condExprBranchModulus(
SemConditionalExpr cond, boolean branch, SemBound b, int val, int mod
) {
semExprModulus(cond.getBranchExpr(branch), b, val, mod)
}
private predicate addModulus(SemExpr add, boolean isLeft, SemBound b, int val, int mod) {
exists(SemExpr larg, SemExpr rarg | nonConstAddition(add, larg, rarg) |
semExprModulus(larg, b, val, mod) and isLeft = true
or
semExprModulus(rarg, b, val, mod) and isLeft = false
)
}
private predicate subModulus(SemExpr sub, boolean isLeft, SemBound b, int val, int mod) {
exists(SemExpr larg, SemExpr rarg | nonConstSubtraction(sub, larg, rarg) |
semExprModulus(larg, b, val, mod) and isLeft = true
or
semExprModulus(rarg, b, val, mod) and isLeft = false
)
}
/**
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
* in an arbitrary 1-based numbering of the input edges to `phi`.
*/
private predicate rankedPhiInput(
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
) {
edge.phiInput(phi, inp) and
edge =
rank[r](SemSsaReadPositionPhiInputEdge e |
e.phiInput(phi, _)
|
e order by e.getOrigBlock().getUniqueId()
)
}

View File

@@ -0,0 +1,8 @@
/**
* C++-specific implementation of modulus analysis.
*/
module Private {
private import experimental.semmle.code.cpp.semantic.Semantic
predicate ignoreExprModulus(SemExpr e) { none() }
}

View File

@@ -0,0 +1,807 @@
/**
* Provides classes and predicates for range analysis.
*
* An inferred bound can either be a specific integer, the abstract value of an
* SSA variable, or the abstract value of an interesting expression. The latter
* category includes array lengths that are not SSA variables.
*
* If an inferred bound relies directly on a condition, then this condition is
* reported as the reason for the bound.
*/
/*
* This library tackles range analysis as a flow problem. Consider e.g.:
* ```
* len = arr.length;
* if (x < len) { ... y = x-1; ... y ... }
* ```
* In this case we would like to infer `y <= arr.length - 2`, and this is
* accomplished by tracking the bound through a sequence of steps:
* ```
* arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y
* ```
*
* In its simplest form the step relation `E1 --> E2` relates two expressions
* such that `E1 <= B` implies `E2 <= B` for any `B` (with a second separate
* step relation handling lower bounds). Examples of such steps include
* assignments `E2 = E1` and conditions `x <= E1` where `E2` is a use of `x`
* guarded by the condition.
*
* In order to handle subtractions and additions with constants, and strict
* comparisons, the step relation is augmented with an integer delta. With this
* generalization `E1 --(delta)--> E2` relates two expressions and an integer
* such that `E1 <= B` implies `E2 <= B + delta` for any `B`. This corresponds
* to the predicate `boundFlowStep`.
*
* The complete range analysis is then implemented as the transitive closure of
* the step relation summing the deltas along the way. If `E1` transitively
* steps to `E2`, `delta` is the sum of deltas along the path, and `B` is an
* interesting bound equal to the value of `E1` then `E2 <= B + delta`. This
* corresponds to the predicate `bounded`.
*
* Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`.
* There are essentially two cases:
* - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`.
* - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`.
* The first case is for whenever a bound can be proven without taking looping
* into account. The second case is relevant when `x2` comes from a back-edge
* where we can prove that the variable has been non-increasing through the
* loop-iteration as this means that any upper bound that holds prior to the
* loop also holds for the variable during the loop.
* This generalizes to a phi node with `n` inputs, so if
* `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we
* also have `x0 <= B + delta` if we can prove either:
* - `xj <= B + d` with `d <= delta` or
* - `xj <= x0 + d` with `d <= 0`
* for each input `xj`.
*
* As all inferred bounds can be related directly to a path in the source code
* the only source of non-termination is if successive redundant (and thereby
* increasingly worse) bounds are calculated along a loop in the source code.
* We prevent this by weakening the bound to a small finite set of bounds when
* a path follows a second back-edge (we postpone weakening till the second
* back-edge as a precise bound might require traversing a loop once).
*/
private import RangeAnalysisSpecific as Specific
private import RangeUtils
private import SignAnalysisCommon
private import ModulusAnalysis
private import experimental.semmle.code.cpp.semantic.Semantic
private import ConstantAnalysis
cached
private module RangeAnalysisCache {
cached
module RangeAnalysisPublic {
/**
* Holds if `b + delta` is a valid bound for `e`.
* - `upper = true` : `e <= b + delta`
* - `upper = false` : `e >= b + delta`
*
* The reason for the bound is given by `reason` and may be either a condition
* or `NoReason` if the bound was proven directly without the use of a bounding
* condition.
*/
cached
predicate semBounded(SemExpr e, SemBound b, int delta, boolean upper, SemReason reason) {
bounded(e, b, delta, upper, _, _, reason) and
bestBound(e, b, delta, upper)
}
}
/**
* Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`.
*/
cached
predicate possibleReason(SemGuard guard) {
guard = boundFlowCond(_, _, _, _, _) or guard = semEqFlowCond(_, _, _, _, _)
}
}
private import RangeAnalysisCache
import RangeAnalysisPublic
/**
* Holds if `b + delta` is a valid bound for `e` and this is the best such delta.
* - `upper = true` : `e <= b + delta`
* - `upper = false` : `e >= b + delta`
*/
private predicate bestBound(SemExpr e, SemBound b, int delta, boolean upper) {
delta = min(int d | bounded(e, b, d, upper, _, _, _)) and upper = true
or
delta = max(int d | bounded(e, b, d, upper, _, _, _)) and upper = false
}
/**
* Holds if `comp` corresponds to:
* - `upper = true` : `v <= e + delta` or `v < e + delta`
* - `upper = false` : `v >= e + delta` or `v > e + delta`
*/
private predicate boundCondition(
SemRelationalExpr comp, SemSsaVariable v, SemExpr e, int delta, boolean upper
) {
comp.getLesserOperand() = semSsaRead(v, delta) and e = comp.getGreaterOperand() and upper = true
or
comp.getGreaterOperand() = semSsaRead(v, delta) and e = comp.getLesserOperand() and upper = false
or
exists(SemSubExpr sub, SemConstantIntegerExpr c, int d |
// (v - d) - e < c
comp.getLesserOperand() = sub and
comp.getGreaterOperand() = c and
sub.getLeftOperand() = semSsaRead(v, d) and
sub.getRightOperand() = e and
upper = true and
delta = d + c.getIntValue()
or
// (v - d) - e > c
comp.getGreaterOperand() = sub and
comp.getLesserOperand() = c and
sub.getLeftOperand() = semSsaRead(v, d) and
sub.getRightOperand() = e and
upper = false and
delta = d + c.getIntValue()
or
// e - (v - d) < c
comp.getLesserOperand() = sub and
comp.getGreaterOperand() = c and
sub.getLeftOperand() = e and
sub.getRightOperand() = semSsaRead(v, d) and
upper = false and
delta = d - c.getIntValue()
or
// e - (v - d) > c
comp.getGreaterOperand() = sub and
comp.getLesserOperand() = c and
sub.getLeftOperand() = e and
sub.getRightOperand() = semSsaRead(v, d) and
upper = true and
delta = d - c.getIntValue()
)
}
/**
* Holds if `comp` is a comparison between `x` and `y` for which `y - x` has a
* fixed value modulo some `mod > 1`, such that the comparison can be
* strengthened by `strengthen` when evaluating to `testIsTrue`.
*/
private predicate modulusComparison(SemRelationalExpr comp, boolean testIsTrue, int strengthen) {
exists(
SemBound b, int v1, int v2, int mod1, int mod2, int mod, boolean resultIsStrict, int d, int k
|
// If `x <= y` and `x =(mod) b + v1` and `y =(mod) b + v2` then
// `0 <= y - x =(mod) v2 - v1`. By choosing `k =(mod) v2 - v1` with
// `0 <= k < mod` we get `k <= y - x`. If the resulting comparison is
// strict then the strengthening amount is instead `k - 1` modulo `mod`:
// `x < y` means `0 <= y - x - 1 =(mod) k - 1` so `k - 1 <= y - x - 1` and
// thus `k - 1 < y - x` with `0 <= k - 1 < mod`.
semExprModulus(comp.getLesserOperand(), b, v1, mod1) and
semExprModulus(comp.getGreaterOperand(), b, v2, mod2) and
mod = mod1.gcd(mod2) and
mod != 1 and
(testIsTrue = true or testIsTrue = false) and
(
if comp.isStrict()
then resultIsStrict = testIsTrue
else resultIsStrict = testIsTrue.booleanNot()
) and
(
resultIsStrict = true and d = 1
or
resultIsStrict = false and d = 0
) and
(
testIsTrue = true and k = v2 - v1
or
testIsTrue = false and k = v1 - v2
) and
strengthen = (((k - d) % mod) + mod) % mod
)
}
/**
* Gets a condition that tests whether `v` is bounded by `e + delta`.
*
* If the condition evaluates to `testIsTrue`:
* - `upper = true` : `v <= e + delta`
* - `upper = false` : `v >= e + delta`
*/
private SemGuard boundFlowCond(
SemSsaVariable v, SemExpr e, int delta, boolean upper, boolean testIsTrue
) {
exists(
SemRelationalExpr comp, int d1, int d2, int d3, int strengthen, boolean compIsUpper,
boolean resultIsStrict
|
comp = result.asExpr() and
boundCondition(comp, v, e, d1, compIsUpper) and
(testIsTrue = true or testIsTrue = false) and
upper = compIsUpper.booleanXor(testIsTrue.booleanNot()) and
(
if comp.isStrict()
then resultIsStrict = testIsTrue
else resultIsStrict = testIsTrue.booleanNot()
) and
(
if getTrackedTypeForSsaVariable(v) instanceof SemIntegerType
then
upper = true and strengthen = -1
or
upper = false and strengthen = 1
else strengthen = 0
) and
(
exists(int k | modulusComparison(comp, testIsTrue, k) and d2 = strengthen * k)
or
not modulusComparison(comp, testIsTrue, _) and d2 = 0
) and
// A strict inequality `x < y` can be strengthened to `x <= y - 1`.
(
resultIsStrict = true and d3 = strengthen
or
resultIsStrict = false and d3 = 0
) and
delta = d1 + d2 + d3
)
or
exists(boolean testIsTrue0 |
semImplies_v2(result, testIsTrue, boundFlowCond(v, e, delta, upper, testIsTrue0), testIsTrue0)
)
or
result = semEqFlowCond(v, e, delta, true, testIsTrue) and
(upper = true or upper = false)
or
// guard that tests whether `v2` is bounded by `e + delta + d1 - d2` and
// exists a guard `guardEq` such that `v = v2 - d1 + d2`.
exists(SemSsaVariable v2, SemGuard guardEq, boolean eqIsTrue, int d1, int d2 |
guardEq = semEqFlowCond(v, semSsaRead(v2, d1), d2, true, eqIsTrue) and
result = boundFlowCond(v2, e, delta + d1 - d2, upper, testIsTrue) and
// guardEq needs to control guard
guardEq.directlyControls(result.getBasicBlock(), eqIsTrue)
)
}
private newtype TSemReason =
TSemNoReason() or
TSemCondReason(SemGuard guard) { possibleReason(guard) }
/**
* A reason for an inferred bound. This can either be `CondReason` if the bound
* is due to a specific condition, or `NoReason` if the bound is inferred
* without going through a bounding condition.
*/
abstract class SemReason extends TSemReason {
/** Gets a textual representation of this reason. */
abstract string toString();
}
/**
* A reason for an inferred bound that indicates that the bound is inferred
* without going through a bounding condition.
*/
class SemNoReason extends SemReason, TSemNoReason {
override string toString() { result = "NoReason" }
}
/** A reason for an inferred bound pointing to a condition. */
class SemCondReason extends SemReason, TSemCondReason {
/** Gets the condition that is the reason for the bound. */
SemGuard getCond() { this = TSemCondReason(result) }
override string toString() { result = getCond().toString() }
}
/**
* Holds if `e + delta` is a valid bound for `v` at `pos`.
* - `upper = true` : `v <= e + delta`
* - `upper = false` : `v >= e + delta`
*/
private predicate boundFlowStepSsa(
SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta, boolean upper, SemReason reason
) {
semSsaUpdateStep(v, e, delta) and
pos.hasReadOfVar(v) and
(upper = true or upper = false) and
reason = TSemNoReason()
or
exists(SemGuard guard, boolean testIsTrue |
pos.hasReadOfVar(v) and
guard = boundFlowCond(v, e, delta, upper, testIsTrue) and
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
reason = TSemCondReason(guard)
)
}
/** Holds if `v != e + delta` at `pos` and `v` is of integral type. */
private predicate unequalFlowStepIntegralSsa(
SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta, SemReason reason
) {
getTrackedTypeForSsaVariable(v) instanceof SemIntegerType and
exists(SemGuard guard, boolean testIsTrue |
pos.hasReadOfVar(v) and
guard = semEqFlowCond(v, e, delta, false, testIsTrue) and
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
reason = TSemCondReason(guard)
)
}
/**
* An expression that does conversion, boxing, or unboxing
*/
private class ConvertOrBoxExpr extends SemUnaryExpr {
ConvertOrBoxExpr() {
this instanceof SemConvertExpr
or
this instanceof SemBoxExpr
or
this instanceof SemUnboxExpr
}
}
/**
* A cast that can be ignored for the purpose of range analysis.
*/
private class SafeCastExpr extends ConvertOrBoxExpr {
SafeCastExpr() { conversionCannotOverflow(getTrackedType(getOperand()), getTrackedType(this)) }
}
/**
* Holds if `typ` is a small integral type with the given lower and upper bounds.
*/
private predicate typeBound(SemIntegerType typ, int lowerbound, int upperbound) {
exists(int bitSize | bitSize = typ.getByteSize() * 8 |
bitSize < 32 and
(
if typ.isSigned()
then (
upperbound = 1.bitShiftLeft(bitSize - 1) - 1 and
lowerbound = -upperbound - 1
) else (
lowerbound = 0 and
upperbound = 1.bitShiftLeft(bitSize) - 1
)
)
)
}
/**
* A cast to a small integral type that may overflow or underflow.
*/
private class NarrowingCastExpr extends ConvertOrBoxExpr {
NarrowingCastExpr() {
not this instanceof SafeCastExpr and
typeBound(getTrackedType(this), _, _)
}
/** Gets the lower bound of the resulting type. */
int getLowerBound() { typeBound(getTrackedType(this), result, _) }
/** Gets the upper bound of the resulting type. */
int getUpperBound() { typeBound(getTrackedType(this), _, result) }
}
/** Holds if `e >= 1` as determined by sign analysis. */
private predicate strictlyPositiveIntegralExpr(SemExpr e) {
semStrictlyPositive(e) and getTrackedType(e) instanceof SemIntegerType
}
/** Holds if `e <= -1` as determined by sign analysis. */
private predicate strictlyNegativeIntegralExpr(SemExpr e) {
semStrictlyNegative(e) and getTrackedType(e) instanceof SemIntegerType
}
/**
* Holds if `e1 + delta` is a valid bound for `e2`.
* - `upper = true` : `e2 <= e1 + delta`
* - `upper = false` : `e2 >= e1 + delta`
*/
private predicate boundFlowStep(SemExpr e2, SemExpr e1, int delta, boolean upper) {
semValueFlowStep(e2, e1, delta) and
(upper = true or upper = false)
or
e2.(SafeCastExpr).getOperand() = e1 and
delta = 0 and
(upper = true or upper = false)
or
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
not x instanceof SemConstantIntegerExpr and
not e1 instanceof SemConstantIntegerExpr and
if strictlyPositiveIntegralExpr(x)
then upper = false and delta = 1
else
if semPositive(x)
then upper = false and delta = 0
else
if strictlyNegativeIntegralExpr(x)
then upper = true and delta = -1
else
if semNegative(x)
then upper = true and delta = 0
else none()
)
or
exists(SemExpr x, SemSubExpr sub |
e2 = sub and
sub.getLeftOperand() = e1 and
sub.getRightOperand() = x
|
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
not x instanceof SemConstantIntegerExpr and
if strictlyPositiveIntegralExpr(x)
then upper = true and delta = -1
else
if semPositive(x)
then upper = true and delta = 0
else
if strictlyNegativeIntegralExpr(x)
then upper = false and delta = 1
else
if semNegative(x)
then upper = false and delta = 0
else none()
)
or
e2.(SemRemExpr).getRightOperand() = e1 and
semPositive(e1) and
delta = -1 and
upper = true
or
e2.(SemRemExpr).getLeftOperand() = e1 and semPositive(e1) and delta = 0 and upper = true
or
e2.(SemBitAndExpr).getAnOperand() = e1 and
semPositive(e1) and
delta = 0 and
upper = true
or
e2.(SemBitOrExpr).getAnOperand() = e1 and
semPositive(e2) and
delta = 0 and
upper = false
or
Specific::hasBound(e2, e1, delta, upper)
}
/** Holds if `e2 = e1 * factor` and `factor > 0`. */
private predicate boundFlowStepMul(SemExpr e2, SemExpr e1, int factor) {
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 |
e2.(SemMulExpr).hasOperands(e1, c) and factor = k
or
exists(SemShiftLeftExpr e |
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
)
)
}
/**
* Holds if `e2 = e1 / factor` and `factor > 0`.
*
* This conflates division, right shift, and unsigned right shift and is
* therefore only valid for non-negative numbers.
*/
private predicate boundFlowStepDiv(SemExpr e2, SemExpr e1, int factor) {
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 |
exists(SemDivExpr e |
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = k
)
or
exists(SemShiftRightExpr e |
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
)
or
exists(SemShiftRightUnsignedExpr e |
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
)
)
}
/**
* Holds if `b + delta` is a valid bound for `v` at `pos`.
* - `upper = true` : `v <= b + delta`
* - `upper = false` : `v >= b + delta`
*/
private predicate boundedSsa(
SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int delta, boolean upper,
boolean fromBackEdge, int origdelta, SemReason reason
) {
exists(SemExpr mid, int d1, int d2, SemReason r1, SemReason r2 |
boundFlowStepSsa(v, pos, mid, d1, upper, r1) and
bounded(mid, b, d2, upper, fromBackEdge, origdelta, r2) and
// upper = true: v <= mid + d1 <= b + d1 + d2 = b + delta
// upper = false: v >= mid + d1 >= b + d1 + d2 = b + delta
delta = d1 + d2 and
(if r1 instanceof SemNoReason then reason = r2 else reason = r1)
)
or
exists(int d, SemReason r1, SemReason r2 |
boundedSsa(v, pos, b, d, upper, fromBackEdge, origdelta, r2) or
boundedPhi(v, b, d, upper, fromBackEdge, origdelta, r2)
|
unequalIntegralSsa(v, pos, b, d, r1) and
(
upper = true and delta = d - 1
or
upper = false and delta = d + 1
) and
(
reason = r1
or
reason = r2 and not r2 instanceof SemNoReason
)
)
}
/**
* Holds if `v != b + delta` at `pos` and `v` is of integral type.
*/
private predicate unequalIntegralSsa(
SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int delta, SemReason reason
) {
exists(SemExpr e, int d1, int d2 |
unequalFlowStepIntegralSsa(v, pos, e, d1, reason) and
bounded(e, b, d2, true, _, _, _) and
bounded(e, b, d2, false, _, _, _) and
delta = d2 + d1
)
}
/** Weakens a delta to lie in the range `[-1..1]`. */
bindingset[delta, upper]
private int weakenDelta(boolean upper, int delta) {
delta in [-1 .. 1] and result = delta
or
upper = true and result = -1 and delta < -1
or
upper = false and result = 1 and delta > 1
}
/**
* Holds if `b + delta` is a valid bound for `inp` when used as an input to
* `phi` along `edge`.
* - `upper = true` : `inp <= b + delta`
* - `upper = false` : `inp >= b + delta`
*/
private predicate boundedPhiInp(
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, SemBound b, int delta,
boolean upper, boolean fromBackEdge, int origdelta, SemReason reason
) {
edge.phiInput(phi, inp) and
exists(int d, boolean fromBackEdge0 |
boundedSsa(inp, edge, b, d, upper, fromBackEdge0, origdelta, reason)
or
boundedPhi(inp, b, d, upper, fromBackEdge0, origdelta, reason)
or
b.(SemSsaBound).getAVariable() = inp and
d = 0 and
(upper = true or upper = false) and
fromBackEdge0 = false and
origdelta = 0 and
reason = TSemNoReason()
|
if semBackEdge(phi, inp, edge)
then
fromBackEdge = true and
(
fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta
or
fromBackEdge0 = false and delta = d
)
else (
delta = d and fromBackEdge = fromBackEdge0
)
)
}
/**
* Holds if `b + delta` is a valid bound for `inp` when used as an input to
* `phi` along `edge`.
* - `upper = true` : `inp <= b + delta`
* - `upper = false` : `inp >= b + delta`
*
* Equivalent to `boundedPhiInp(phi, inp, edge, b, delta, upper, _, _, _)`.
*/
pragma[noinline]
private predicate boundedPhiInp1(
SemSsaPhiNode phi, SemBound b, boolean upper, SemSsaVariable inp,
SemSsaReadPositionPhiInputEdge edge, int delta
) {
boundedPhiInp(phi, inp, edge, b, delta, upper, _, _, _)
}
/**
* Holds if `phi` is a valid bound for `inp` when used as an input to `phi`
* along `edge`.
* - `upper = true` : `inp <= phi`
* - `upper = false` : `inp >= phi`
*/
private predicate selfBoundedPhiInp(
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, boolean upper
) {
exists(int d, SemSsaBound phibound |
phibound.getAVariable() = phi and
boundedPhiInp(phi, inp, edge, phibound, d, upper, _, _, _) and
(
upper = true and d <= 0
or
upper = false and d >= 0
)
)
}
/**
* Holds if `b + delta` is a valid bound for some input, `inp`, to `phi`, and
* thus a candidate bound for `phi`.
* - `upper = true` : `inp <= b + delta`
* - `upper = false` : `inp >= b + delta`
*/
pragma[noinline]
private predicate boundedPhiCand(
SemSsaPhiNode phi, boolean upper, SemBound b, int delta, boolean fromBackEdge, int origdelta,
SemReason reason
) {
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
boundedPhiInp(phi, inp, edge, b, delta, upper, fromBackEdge, origdelta, reason)
)
}
/**
* Holds if the candidate bound `b + delta` for `phi` is valid for the phi input
* `inp` along `edge`.
*/
private predicate boundedPhiCandValidForEdge(
SemSsaPhiNode phi, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
SemReason reason, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge
) {
boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and
(
exists(int d | boundedPhiInp1(phi, b, upper, inp, edge, d) | upper = true and d <= delta)
or
exists(int d | boundedPhiInp1(phi, b, upper, inp, edge, d) | upper = false and d >= delta)
or
selfBoundedPhiInp(phi, inp, edge, upper)
)
}
/**
* Holds if `b + delta` is a valid bound for `phi`.
* - `upper = true` : `phi <= b + delta`
* - `upper = false` : `phi >= b + delta`
*/
private predicate boundedPhi(
SemSsaPhiNode phi, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
SemReason reason
) {
forex(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge | edge.phiInput(phi, inp) |
boundedPhiCandValidForEdge(phi, b, delta, upper, fromBackEdge, origdelta, reason, inp, edge)
)
}
/**
* Holds if `e` has an upper (for `upper = true`) or lower
* (for `upper = false`) bound of `b`.
*/
private predicate baseBound(SemExpr e, int b, boolean upper) {
Specific::hasConstantBound(e, b, upper)
or
upper = false and
b = 0 and
semPositive(e.(SemBitAndExpr).getAnOperand()) and
// REVIEW: We let the language opt out here to preserve original results.
not Specific::ignoreZeroLowerBound(e)
}
/**
* Holds if the value being cast has an upper (for `upper = true`) or lower
* (for `upper = false`) bound within the bounds of the resulting type.
* For `upper = true` this means that the cast will not overflow and for
* `upper = false` this means that the cast will not underflow.
*/
private predicate safeNarrowingCast(NarrowingCastExpr cast, boolean upper) {
exists(int bound | bounded(cast.getOperand(), any(SemZeroBound zb), bound, upper, _, _, _) |
upper = true and bound <= cast.getUpperBound()
or
upper = false and bound >= cast.getLowerBound()
)
}
pragma[noinline]
private predicate boundedCastExpr(
NarrowingCastExpr cast, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
SemReason reason
) {
bounded(cast.getOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
}
/**
* Holds if `b + delta` is a valid bound for `e`.
* - `upper = true` : `e <= b + delta`
* - `upper = false` : `e >= b + delta`
*/
private predicate bounded(
SemExpr e, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
SemReason reason
) {
not Specific::ignoreExprBound(e) and
(
e = b.getExpr(delta) and
(upper = true or upper = false) and
fromBackEdge = false and
origdelta = delta and
reason = TSemNoReason()
or
baseBound(e, delta, upper) and
b instanceof SemZeroBound and
fromBackEdge = false and
origdelta = delta and
reason = TSemNoReason()
or
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
boundedSsa(v, bb, b, delta, upper, fromBackEdge, origdelta, reason) and
e = v.getAUse() and
bb.getBlock() = e.getBasicBlock()
)
or
exists(SemExpr mid, int d1, int d2 |
boundFlowStep(e, mid, d1, upper) and
// Constants have easy, base-case bounds, so let's not infer any recursive bounds.
not e instanceof SemConstantIntegerExpr and
bounded(mid, b, d2, upper, fromBackEdge, origdelta, reason) and
// upper = true: e <= mid + d1 <= b + d1 + d2 = b + delta
// upper = false: e >= mid + d1 >= b + d1 + d2 = b + delta
delta = d1 + d2
)
or
exists(SemSsaPhiNode phi |
boundedPhi(phi, b, delta, upper, fromBackEdge, origdelta, reason) and
e = phi.getAUse()
)
or
exists(SemExpr mid, int factor, int d |
boundFlowStepMul(e, mid, factor) and
not e instanceof SemConstantIntegerExpr and
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
b instanceof SemZeroBound and
delta = d * factor
)
or
exists(SemExpr mid, int factor, int d |
boundFlowStepDiv(e, mid, factor) and
not e instanceof SemConstantIntegerExpr and
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
b instanceof SemZeroBound and
d >= 0 and
delta = d / factor
)
or
exists(NarrowingCastExpr cast |
cast = e and
safeNarrowingCast(cast, upper.booleanNot()) and
boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason)
)
or
exists(
SemConditionalExpr cond, int d1, int d2, boolean fbe1, boolean fbe2, int od1, int od2,
SemReason r1, SemReason r2
|
cond = e and
boundedConditionalExpr(cond, b, upper, true, d1, fbe1, od1, r1) and
boundedConditionalExpr(cond, b, upper, false, d2, fbe2, od2, r2) and
(
delta = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1
or
delta = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2
)
|
upper = true and delta = d1.maximum(d2)
or
upper = false and delta = d1.minimum(d2)
)
)
}
private predicate boundedConditionalExpr(
SemConditionalExpr cond, SemBound b, boolean upper, boolean branch, int delta,
boolean fromBackEdge, int origdelta, SemReason reason
) {
bounded(cond.getBranchExpr(branch), b, delta, upper, fromBackEdge, origdelta, reason)
}

View File

@@ -0,0 +1,88 @@
/**
* C++-specific implementation of range analysis.
*/
private import experimental.semmle.code.cpp.semantic.Semantic
/**
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreSsaReadCopy(SemExpr e) { none() }
/**
* Ignore the bound on this expression.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreExprBound(SemExpr e) { none() }
/**
* Ignore any inferred zero lower bound on this expression.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreZeroLowerBound(SemExpr e) { none() }
/**
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
/**
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
*
* This predicate is to keep the results identical to the original Java implementation. It should be
* removed once we have the new implementation matching the old results exactly.
*/
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
/**
* Adds additional results to `ssaRead()` that are specific to Java.
*
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
* in exactly the same way as the old Java implementation. Once the new implementation matches the
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
* or not they come from a post-increment/decrement expression.
*/
SemExpr specificSsaRead(SemSsaVariable v, int delta) { none() }
/**
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
*/
predicate hasConstantBound(SemExpr e, int bound, boolean upper) { none() }
/**
* Holds if `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
*/
predicate hasBound(SemExpr e, SemExpr bound, int delta, boolean upper) { none() }
/**
* Holds if the value of `dest` is known to be `src + delta`.
*/
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, int delta) { none() }
/**
* Gets the type that range analysis should use to track the result of the specified expression,
* if a type other than the original type of the expression is to be used.
*
* This predicate is commonly used in languages that support immutable "boxed" types that are
* actually references but whose values can be tracked as the type contained in the box.
*/
SemType getAlternateType(SemExpr e) { none() }
/**
* Gets the type that range analysis should use to track the result of the specified source
* variable, if a type other than the original type of the expression is to be used.
*
* This predicate is commonly used in languages that support immutable "boxed" types that are
* actually references but whose values can be tracked as the type contained in the box.
*/
SemType getAlternateTypeForSsaVariable(SemSsaVariable var) { none() }

View File

@@ -0,0 +1,135 @@
/**
* Provides utility predicates for range analysis.
*/
private import experimental.semmle.code.cpp.semantic.Semantic
private import RangeAnalysisSpecific as Specific
private import ConstantAnalysis
/**
* Gets an expression that equals `v - d`.
*/
SemExpr semSsaRead(SemSsaVariable v, int delta) {
// There are various language-specific extension points that can be removed once we no longer
// expect to match the original Java implementation's results exactly.
result = v.getAUse() and delta = 0
or
exists(int d1, SemConstantIntegerExpr c |
result.(SemAddExpr).hasOperands(semSsaRead(v, d1), c) and
delta = d1 - c.getIntValue() and
not Specific::ignoreSsaReadArithmeticExpr(result)
)
or
exists(SemSubExpr sub, int d1, SemConstantIntegerExpr c |
result = sub and
sub.getLeftOperand() = semSsaRead(v, d1) and
sub.getRightOperand() = c and
delta = d1 + c.getIntValue() and
not Specific::ignoreSsaReadArithmeticExpr(result)
)
or
result = v.(SemSsaExplicitUpdate).getSourceExpr() and
delta = 0 and
not Specific::ignoreSsaReadAssignment(v)
or
result = Specific::specificSsaRead(v, delta)
or
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta) and
not Specific::ignoreSsaReadCopy(result)
or
result.(SemStoreExpr).getOperand() = semSsaRead(v, delta)
}
/**
* Gets a condition that tests whether `v` equals `e + delta`.
*
* If the condition evaluates to `testIsTrue`:
* - `isEq = true` : `v == e + delta`
* - `isEq = false` : `v != e + delta`
*/
SemGuard semEqFlowCond(SemSsaVariable v, SemExpr e, int delta, boolean isEq, boolean testIsTrue) {
exists(boolean eqpolarity |
result.isEquality(semSsaRead(v, delta), e, eqpolarity) and
(testIsTrue = true or testIsTrue = false) and
eqpolarity.booleanXor(testIsTrue).booleanNot() = isEq
)
or
exists(boolean testIsTrue0 |
semImplies_v2(result, testIsTrue, semEqFlowCond(v, e, delta, isEq, testIsTrue0), testIsTrue0)
)
}
/**
* Holds if `v` is an `SsaExplicitUpdate` that equals `e + delta`.
*/
predicate semSsaUpdateStep(SemSsaExplicitUpdate v, SemExpr e, int delta) {
exists(SemExpr defExpr | defExpr = v.getSourceExpr() |
defExpr.(SemCopyValueExpr).getOperand() = e and delta = 0
or
defExpr.(SemStoreExpr).getOperand() = e and delta = 0
or
defExpr.(SemAddOneExpr).getOperand() = e and delta = 1
or
defExpr.(SemSubOneExpr).getOperand() = e and delta = -1
or
e = defExpr and
not (
defExpr instanceof SemCopyValueExpr or
defExpr instanceof SemStoreExpr or
defExpr instanceof SemAddOneExpr or
defExpr instanceof SemSubOneExpr
) and
delta = 0
)
}
/**
* Holds if `e1 + delta` equals `e2`.
*/
predicate semValueFlowStep(SemExpr e2, SemExpr e1, int delta) {
e2.(SemCopyValueExpr).getOperand() = e1 and delta = 0
or
e2.(SemStoreExpr).getOperand() = e1 and delta = 0
or
e2.(SemAddOneExpr).getOperand() = e1 and delta = 1
or
e2.(SemSubOneExpr).getOperand() = e1 and delta = -1
or
Specific::additionalValueFlowStep(e2, e1, delta)
or
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
x.(SemConstantIntegerExpr).getIntValue() = delta
)
or
exists(SemExpr x, SemSubExpr sub |
e2 = sub and
sub.getLeftOperand() = e1 and
sub.getRightOperand() = x
|
x.(SemConstantIntegerExpr).getIntValue() = -delta
)
}
/**
* Gets the type used to track the specified expression's range information.
*
* Usually, this just `e.getSemType()`, but the language can override this to track immutable boxed
* primitive types as the underlying primitive type.
*/
SemType getTrackedType(SemExpr e) {
result = Specific::getAlternateType(e)
or
not exists(Specific::getAlternateType(e)) and result = e.getSemType()
}
/**
* Gets the type used to track the specified source variable's range information.
*
* Usually, this just `e.getType()`, but the language can override this to track immutable boxed
* primitive types as the underlying primitive type.
*/
SemType getTrackedTypeForSsaVariable(SemSsaVariable var) {
result = Specific::getAlternateTypeForSsaVariable(var)
or
not exists(Specific::getAlternateTypeForSsaVariable(var)) and result = var.getType()
}

View File

@@ -0,0 +1,267 @@
private import experimental.semmle.code.cpp.semantic.Semantic
newtype TSign =
TNeg() or
TZero() or
TPos()
/** Class representing expression signs (+, -, 0). */
class Sign extends TSign {
/** Gets the string representation of this sign. */
string toString() {
result = "-" and this = TNeg()
or
result = "0" and this = TZero()
or
result = "+" and this = TPos()
}
/** Gets a possible sign after incrementing an expression that has this sign. */
Sign inc() {
this = TNeg() and result = TNeg()
or
this = TNeg() and result = TZero()
or
this = TZero() and result = TPos()
or
this = TPos() and result = TPos()
}
/** Gets a possible sign after decrementing an expression that has this sign. */
Sign dec() { result.inc() = this }
/** Gets a possible sign after negating an expression that has this sign. */
Sign neg() {
this = TNeg() and result = TPos()
or
this = TZero() and result = TZero()
or
this = TPos() and result = TNeg()
}
/**
* Gets a possible sign after bitwise complementing an expression that has this
* sign.
*/
Sign bitnot() {
this = TNeg() and result = TPos()
or
this = TNeg() and result = TZero()
or
this = TZero() and result = TNeg()
or
this = TPos() and result = TNeg()
}
/**
* Gets a possible sign after adding an expression with sign `s` to an expression
* that has this sign.
*/
Sign add(Sign s) {
this = TZero() and result = s
or
s = TZero() and result = this
or
this = s and this = result
or
this = TPos() and s = TNeg()
or
this = TNeg() and s = TPos()
}
/**
* Gets a possible sign after subtracting an expression with sign `s` from an expression
* that has this sign.
*/
Sign sub(Sign s) { result = add(s.neg()) }
/**
* Gets a possible sign after multiplying an expression with sign `s` to an expression
* that has this sign.
*/
Sign mul(Sign s) {
result = TZero() and this = TZero()
or
result = TZero() and s = TZero()
or
result = TNeg() and this = TPos() and s = TNeg()
or
result = TNeg() and this = TNeg() and s = TPos()
or
result = TPos() and this = TPos() and s = TPos()
or
result = TPos() and this = TNeg() and s = TNeg()
}
/**
* Gets a possible sign after integer dividing an expression that has this sign
* by an expression with sign `s`.
*/
Sign div(Sign s) {
result = TZero() and s = TNeg() // ex: 3 / -5 = 0
or
result = TZero() and s = TPos() // ex: 3 / 5 = 0
or
result = TNeg() and this = TPos() and s = TNeg()
or
result = TNeg() and this = TNeg() and s = TPos()
or
result = TPos() and this = TPos() and s = TPos()
or
result = TPos() and this = TNeg() and s = TNeg()
}
/**
* Gets a possible sign after modulo dividing an expression that has this sign
* by an expression with sign `s`.
*/
Sign rem(Sign s) {
result = TZero() and s = TNeg()
or
result = TZero() and s = TPos()
or
result = this and s = TNeg()
or
result = this and s = TPos()
}
/**
* Gets a possible sign after bitwise `and` of an expression that has this sign
* and an expression with sign `s`.
*/
Sign bitand(Sign s) {
result = TZero() and this = TZero()
or
result = TZero() and s = TZero()
or
result = TZero() and this = TPos()
or
result = TZero() and s = TPos()
or
result = TNeg() and this = TNeg() and s = TNeg()
or
result = TPos() and this = TNeg() and s = TPos()
or
result = TPos() and this = TPos() and s = TNeg()
or
result = TPos() and this = TPos() and s = TPos()
}
/**
* Gets a possible sign after bitwise `or` of an expression that has this sign
* and an expression with sign `s`.
*/
Sign bitor(Sign s) {
result = TZero() and this = TZero() and s = TZero()
or
result = TNeg() and this = TNeg()
or
result = TNeg() and s = TNeg()
or
result = TPos() and this = TPos() and s = TZero()
or
result = TPos() and this = TZero() and s = TPos()
or
result = TPos() and this = TPos() and s = TPos()
}
/**
* Gets a possible sign after bitwise `xor` of an expression that has this sign
* and an expression with sign `s`.
*/
Sign bitxor(Sign s) {
result = TZero() and this = s
or
result = this and s = TZero()
or
result = s and this = TZero()
or
result = TPos() and this = TPos() and s = TPos()
or
result = TNeg() and this = TNeg() and s = TPos()
or
result = TNeg() and this = TPos() and s = TNeg()
or
result = TPos() and this = TNeg() and s = TNeg()
}
/**
* Gets a possible sign after left shift of an expression that has this sign
* by an expression with sign `s`.
*/
Sign lshift(Sign s) {
result = TZero() and this = TZero()
or
result = this and s = TZero()
or
this != TZero() and s != TZero()
}
/**
* Gets a possible sign after right shift of an expression that has this sign
* by an expression with sign `s`.
*/
Sign rshift(Sign s) {
result = TZero() and this = TZero()
or
result = this and s = TZero()
or
result = TNeg() and this = TNeg()
or
result != TNeg() and this = TPos() and s != TZero()
}
/**
* Gets a possible sign after unsigned right shift of an expression that has
* this sign by an expression with sign `s`.
*/
Sign urshift(Sign s) {
result = TZero() and this = TZero()
or
result = this and s = TZero()
or
result != TZero() and this = TNeg() and s != TZero()
or
result != TNeg() and this = TPos() and s != TZero()
}
/** Perform `op` on this sign. */
Sign applyUnaryOp(Opcode op) {
op instanceof Opcode::CopyValue and result = this
or
op instanceof Opcode::Store and result = this
or
op instanceof Opcode::AddOne and result = inc()
or
op instanceof Opcode::SubOne and result = dec()
or
op instanceof Opcode::Negate and result = neg()
or
op instanceof Opcode::BitComplement and result = bitnot()
}
/** Perform `op` on this sign and sign `s`. */
Sign applyBinaryOp(Sign s, Opcode op) {
op instanceof Opcode::Add and result = add(s)
or
op instanceof Opcode::Sub and result = sub(s)
or
op instanceof Opcode::Mul and result = mul(s)
or
op instanceof Opcode::Div and result = div(s)
or
op instanceof Opcode::Rem and result = rem(s)
or
op instanceof Opcode::BitAnd and result = bitand(s)
or
op instanceof Opcode::BitOr and result = bitor(s)
or
op instanceof Opcode::BitXor and result = bitxor(s)
or
op instanceof Opcode::ShiftLeft and result = lshift(s)
or
op instanceof Opcode::ShiftRight and result = rshift(s)
or
op instanceof Opcode::ShiftRightUnsigned and result = urshift(s)
}
}

View File

@@ -0,0 +1,493 @@
/**
* Provides sign analysis to determine whether expression are always positive
* or negative.
*
* The analysis is implemented as an abstract interpretation over the
* three-valued domain `{negative, zero, positive}`.
*/
private import SignAnalysisSpecific as Specific
private import experimental.semmle.code.cpp.semantic.Semantic
private import ConstantAnalysis
private import RangeUtils
private import Sign
/**
* An SSA definition for which the analysis can compute the sign.
*
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
* recursive.
*/
abstract private class SignDef instanceof SemSsaVariable {
final string toString() { result = super.toString() }
/** Gets the possible signs of this SSA definition. */
abstract Sign getSign();
}
/** An SSA definition whose sign is computed based on standard flow. */
abstract private class FlowSignDef extends SignDef {
abstract override Sign getSign();
}
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
private class ExplicitSignDef extends FlowSignDef {
SemSsaExplicitUpdate update;
ExplicitSignDef() { update = this }
final override Sign getSign() { result = semExprSign(update.getSourceExpr()) }
}
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
private class PhiSignDef extends FlowSignDef {
SemSsaPhiNode phi;
PhiSignDef() { phi = this }
final override Sign getSign() {
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
edge.phiInput(phi, inp) and
result = semSsaSign(inp, edge)
)
}
}
/** An SSA definition whose sign is computed by a language-specific implementation. */
abstract class CustomSignDef extends SignDef {
abstract override Sign getSign();
}
/**
* An expression for which the analysis can compute the sign.
*
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
* recursive.
*
* Concrete implementations extend one of the following subclasses:
* - `ConstantSignExpr`, for expressions with a compile-time constant value.
* - `FlowSignExpr`, for expressions whose sign can be computed from the signs of their operands.
* - `CustomsignExpr`, for expressions shose sign can be computed by a language-specific
* implementation.
*
* If the same expression matches more than one of the above subclasses, the sign is computed as
* follows:
* - The sign of a `ConstantSignExpr` is computed solely from `ConstantSignExpr.getSign()`,
* regardless of any other subclasses.
* - If a non-`ConstantSignExpr` expression matches exactly one of `FlowSignExpr` or
* `CustomSignExpr`, the sign is computed by that class' `getSign()` predicate.
* - If a non-`ConstantSignExpr` expression matches both `FlowSignExpr` and `CustomSignExpr`, the
* sign is the _intersection_ of the signs of those two classes' `getSign()` predicates. Thus,
* both classes have the opportunity to _restrict_ the set of possible signs, not to generate new
* possible signs.
* - If an expression does not match any of the three subclasses, then it can have any sign.
*
* Note that the `getSign()` predicate is introduced only in subclasses of `SignExpr`.
*/
abstract class SignExpr instanceof SemExpr {
SignExpr() { not Specific::ignoreExprSign(this) }
final string toString() { result = super.toString() }
abstract Sign getSign();
}
/** An expression whose sign is determined by its constant numeric value. */
private class ConstantSignExpr extends SignExpr {
ConstantSignExpr() {
this instanceof SemConstantIntegerExpr or
exists(this.(SemNumericLiteralExpr).getApproximateFloatValue())
}
final override Sign getSign() {
exists(int i | this.(SemConstantIntegerExpr).getIntValue() = i |
i < 0 and result = TNeg()
or
i = 0 and result = TZero()
or
i > 0 and result = TPos()
)
or
not exists(this.(SemConstantIntegerExpr).getIntValue()) and
exists(float f | f = this.(SemNumericLiteralExpr).getApproximateFloatValue() |
f < 0 and result = TNeg()
or
f = 0 and result = TZero()
or
f > 0 and result = TPos()
)
}
}
abstract private class NonConstantSignExpr extends SignExpr {
NonConstantSignExpr() { not this instanceof ConstantSignExpr }
final override Sign getSign() {
// The result is the _intersection_ of the signs computed from flow and by the language.
(result = this.(FlowSignExpr).getSignRestriction() or not this instanceof FlowSignExpr) and
(result = this.(CustomSignExpr).getSignRestriction() or not this instanceof CustomSignExpr)
}
}
/** An expression whose sign is computed from the signs of its operands. */
abstract private class FlowSignExpr extends NonConstantSignExpr {
abstract Sign getSignRestriction();
}
/** An expression whose sign is computed by a language-specific implementation. */
abstract class CustomSignExpr extends NonConstantSignExpr {
abstract Sign getSignRestriction();
}
/** An expression whose sign is unknown. */
private class UnknownSignExpr extends SignExpr {
UnknownSignExpr() {
not this instanceof FlowSignExpr and
not this instanceof CustomSignExpr and
not this instanceof ConstantSignExpr and
(
// Only track numeric types.
getTrackedType(this) instanceof SemNumericType
or
// Unless the language says to track this expression anyway.
Specific::trackUnknownNonNumericExpr(this)
)
}
final override Sign getSign() { semAnySign(result) }
}
/**
* A `Load` expression whose sign is computed from the sign of its SSA definition, restricted by
* inference from any intervening guards.
*/
class UseSignExpr extends FlowSignExpr {
SemSsaVariable v;
UseSignExpr() { v.getAUse() = this }
override Sign getSignRestriction() {
// Propagate via SSA
// Propagate the sign from the def of `v`, incorporating any inference from guards.
result = semSsaSign(v, any(SemSsaReadPositionBlock bb | bb.getAnExpr() = this))
or
// No block for this read. Just use the sign of the def.
// REVIEW: How can this happen?
not exists(SemSsaReadPositionBlock bb | bb.getAnExpr() = this) and
result = semSsaDefSign(v)
}
}
/** A binary expression whose sign is computed from the signs of its operands. */
private class BinarySignExpr extends FlowSignExpr {
SemBinaryExpr binary;
BinarySignExpr() { binary = this }
override Sign getSignRestriction() {
result =
semExprSign(binary.getLeftOperand())
.applyBinaryOp(semExprSign(binary.getRightOperand()), binary.getOpcode())
or
exists(SemDivExpr div | div = binary |
result = semExprSign(div.getLeftOperand()) and
result != TZero() and
div.getRightOperand().(SemFloatingPointLiteralExpr).getFloatValue() = 0
)
}
}
/**
* A `Convert`, `Box`, or `Unbox` expression.
*/
private class SemCastExpr extends SemUnaryExpr {
SemCastExpr() {
this instanceof SemConvertExpr
or
this instanceof SemBoxExpr
or
this instanceof SemUnboxExpr
}
}
/** A unary expression whose sign is computed from the sign of its operand. */
private class UnarySignExpr extends FlowSignExpr {
SemUnaryExpr unary;
UnarySignExpr() { unary = this and not this instanceof SemCastExpr }
override Sign getSignRestriction() {
result = semExprSign(unary.getOperand()).applyUnaryOp(unary.getOpcode())
}
}
/**
* A `Convert`, `Box`, or `Unbox` expression, whose sign is computed based on
* the sign of its operand and the source and destination types.
*/
abstract private class CastSignExpr extends FlowSignExpr {
SemUnaryExpr cast;
CastSignExpr() { cast = this and cast instanceof SemCastExpr }
override Sign getSignRestriction() { result = semExprSign(cast.getOperand()) }
}
/**
* A `Convert` expression.
*/
private class ConvertSignExpr extends CastSignExpr {
override SemConvertExpr cast;
}
/**
* A `Box` expression.
*/
private class BoxSignExpr extends CastSignExpr {
override SemBoxExpr cast;
}
/**
* An `Unbox` expression.
*/
private class UnboxSignExpr extends CastSignExpr {
override SemUnboxExpr cast;
UnboxSignExpr() {
exists(SemType fromType | fromType = getTrackedType(cast.getOperand()) |
// Only numeric source types are handled here.
fromType instanceof SemNumericType
)
}
}
private predicate unknownSign(SemExpr e) { e instanceof UnknownSignExpr }
/**
* Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted
* to only include bounds for which we might determine a sign.
*/
private predicate lowerBound(
SemExpr lowerbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
) {
exists(boolean testIsTrue, SemRelationalExpr comp |
pos.hasReadOfVar(v) and
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
not unknownSign(lowerbound)
|
testIsTrue = true and
comp.getLesserOperand() = lowerbound and
comp.getGreaterOperand() = semSsaRead(v, 0) and
(if comp.isStrict() then isStrict = true else isStrict = false)
or
testIsTrue = false and
comp.getGreaterOperand() = lowerbound and
comp.getLesserOperand() = semSsaRead(v, 0) and
(if comp.isStrict() then isStrict = false else isStrict = true)
)
}
/**
* Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted
* to only include bounds for which we might determine a sign.
*/
private predicate upperBound(
SemExpr upperbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
) {
exists(boolean testIsTrue, SemRelationalExpr comp |
pos.hasReadOfVar(v) and
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
not unknownSign(upperbound)
|
testIsTrue = true and
comp.getGreaterOperand() = upperbound and
comp.getLesserOperand() = semSsaRead(v, 0) and
(if comp.isStrict() then isStrict = true else isStrict = false)
or
testIsTrue = false and
comp.getLesserOperand() = upperbound and
comp.getGreaterOperand() = semSsaRead(v, 0) and
(if comp.isStrict() then isStrict = false else isStrict = true)
)
}
/**
* Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
* restricted to only include bounds for which we might determine a sign. The
* boolean `isEq` gives the polarity:
* - `isEq = true` : `v = eqbound`
* - `isEq = false` : `v != eqbound`
*/
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
exists(SemGuard guard, boolean testIsTrue, boolean polarity |
pos.hasReadOfVar(v) and
semGuardControlsSsaRead(guard, pos, testIsTrue) and
guard.isEquality(eqbound, semSsaRead(v, 0), polarity) and
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
not unknownSign(eqbound)
)
}
/**
* Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
* order for `v` to be positive.
*/
private predicate posBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
upperBound(bound, v, pos, _) or
eqBound(bound, v, pos, true)
}
/**
* Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
* order for `v` to be negative.
*/
private predicate negBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
lowerBound(bound, v, pos, _) or
eqBound(bound, v, pos, true)
}
/**
* Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
* can be zero.
*/
private predicate zeroBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
lowerBound(bound, v, pos, _) or
upperBound(bound, v, pos, _) or
eqBound(bound, v, pos, _)
}
/** Holds if `bound` allows `v` to be positive at `pos`. */
private predicate posBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
posBound(bound, v, pos) and TPos() = semExprSign(bound)
}
/** Holds if `bound` allows `v` to be negative at `pos`. */
private predicate negBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
negBound(bound, v, pos) and TNeg() = semExprSign(bound)
}
/** Holds if `bound` allows `v` to be zero at `pos`. */
private predicate zeroBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
lowerBound(bound, v, pos, _) and TNeg() = semExprSign(bound)
or
lowerBound(bound, v, pos, false) and TZero() = semExprSign(bound)
or
upperBound(bound, v, pos, _) and TPos() = semExprSign(bound)
or
upperBound(bound, v, pos, false) and TZero() = semExprSign(bound)
or
eqBound(bound, v, pos, true) and TZero() = semExprSign(bound)
or
eqBound(bound, v, pos, false) and TZero() != semExprSign(bound)
}
/**
* Holds if there is a bound that might restrict whether `v` has the sign `s`
* at `pos`.
*/
private predicate hasGuard(SemSsaVariable v, SemSsaReadPosition pos, Sign s) {
s = TPos() and posBound(_, v, pos)
or
s = TNeg() and negBound(_, v, pos)
or
s = TZero() and zeroBound(_, v, pos)
}
/**
* Gets a possible sign of `v` at `pos` based on its definition, where the sign
* might be ruled out by a guard.
*/
pragma[noinline]
private Sign guardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
result = semSsaDefSign(v) and
pos.hasReadOfVar(v) and
hasGuard(v, pos, result)
}
/**
* Gets a possible sign of `v` at `pos` based on its definition, where no guard
* can rule it out.
*/
pragma[noinline]
private Sign unguardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
result = semSsaDefSign(v) and
pos.hasReadOfVar(v) and
not hasGuard(v, pos, result)
}
/**
* Gets a possible sign of `v` at read position `pos`, where a guard could have
* ruled out the sign but does not.
* This does not check that the definition of `v` also allows the sign.
*/
private Sign guardedSsaSignOk(SemSsaVariable v, SemSsaReadPosition pos) {
result = TPos() and
forex(SemExpr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
or
result = TNeg() and
forex(SemExpr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
or
result = TZero() and
forex(SemExpr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
}
/** Gets a possible sign for `v` at `pos`. */
private Sign semSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
result = unguardedSsaSign(v, pos)
or
result = guardedSsaSign(v, pos) and
result = guardedSsaSignOk(v, pos)
}
/** Gets a possible sign for `v`. */
pragma[nomagic]
Sign semSsaDefSign(SemSsaVariable v) { result = v.(SignDef).getSign() }
/** Gets a possible sign for `e`. */
cached
Sign semExprSign(SemExpr e) {
exists(Sign s | s = e.(SignExpr).getSign() |
if
getTrackedType(e) instanceof SemUnsignedIntegerType and
s = TNeg() and
not Specific::ignoreTypeRestrictions(e)
then result = TPos()
else result = s
)
}
/**
* Dummy predicate that holds for any sign. This is added to improve readability
* of cases where the sign is unrestricted.
*/
predicate semAnySign(Sign s) { any() }
/** Holds if `e` can be positive and cannot be negative. */
predicate semPositive(SemExpr e) {
semExprSign(e) = TPos() and
not semExprSign(e) = TNeg()
}
/** Holds if `e` can be negative and cannot be positive. */
predicate semNegative(SemExpr e) {
semExprSign(e) = TNeg() and
not semExprSign(e) = TPos()
}
/** Holds if `e` is strictly positive. */
predicate semStrictlyPositive(SemExpr e) {
semExprSign(e) = TPos() and
not semExprSign(e) = TNeg() and
not semExprSign(e) = TZero()
}
/** Holds if `e` is strictly negative. */
predicate semStrictlyNegative(SemExpr e) {
semExprSign(e) = TNeg() and
not semExprSign(e) = TPos() and
not semExprSign(e) = TZero()
}

View File

@@ -0,0 +1,23 @@
/**
* Provides C++-specific definitions for use in sign analysis.
*/
private import experimental.semmle.code.cpp.semantic.Semantic
/**
* Workaround to allow certain expressions to have a negative sign, even if the type of the
* expression is unsigned.
*/
predicate ignoreTypeRestrictions(SemExpr e) { none() }
/**
* Workaround to track the sign of cetain expressions even if the type of the expression is not
* numeric.
*/
predicate trackUnknownNonNumericExpr(SemExpr e) { none() }
/**
* Workaround to ignore tracking of certain expressions even if the type of the expression is
* numeric.
*/
predicate ignoreExprSign(SemExpr e) { none() }

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 0.0.10-dev
version: 0.1.0-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp

View File

@@ -84,6 +84,7 @@ private int fileHeaderLimit(File f) {
fc = fileFirstComment(f) and
result =
min(int line |
// code ending the initial comments
exists(DeclarationEntry de, Location l |
l = de.getLocation() and
l.getFile() = f and
@@ -105,7 +106,13 @@ private int fileHeaderLimit(File f) {
line > fc
)
or
// end of the file
line = f.getMetrics().getNumberOfLines()
or
// rarely, we've seen extremely long sequences of initial comments
// (and/or limitations in the above constraints) cause an overflow of
// the maximum string length. So don't look past 1000 lines regardless.
line = 1000
)
)
}

View File

@@ -111,24 +111,6 @@ class Class extends UserType {
result = this.getCanonicalMember(index).(TemplateVariable).getAnInstantiation()
}
/**
* DEPRECATED: Use `getCanonicalMember(int)` or `getAMember(int)` instead.
* Gets the `index`th member of this class.
*/
deprecated Declaration getMember(int index) {
member(underlyingElement(this), index, unresolveElement(result))
}
/**
* DEPRECATED: As this includes a somewhat arbitrary number of
* template instantiations, it is unlikely to do what
* you need.
* Gets the number of members that this class has. This includes both
* templates that are in this class, and instantiations of those
* templates.
*/
deprecated int getNumMember() { result = count(this.getAMember()) }
/**
* Gets a private member declared in this class, struct or union.
* For template members, this may be either the template or an
@@ -208,23 +190,6 @@ class Class extends UserType {
*/
deprecated predicate hasCopyConstructor() { this.getAMemberFunction() instanceof CopyConstructor }
/**
* Holds if this class has a copy assignment operator that is either
* explicitly declared (though possibly `= delete`) or is auto-generated,
* non-trivial and called from somewhere.
*
* DEPRECATED: There is more than one reasonable definition of what it means
* to have a copy assignment operator, and we do not want to promote one
* particular definition by naming it with this predicate. Having a copy
* assignment operator could mean that such a member is declared or defined
* in the source or that it is callable by a particular caller. For C++11,
* there's also a question of whether to include members that are defaulted
* or deleted.
*/
deprecated predicate hasCopyAssignmentOperator() {
this.getAMemberFunction() instanceof CopyAssignmentOperator
}
/**
* Like accessOfBaseMember but returns multiple results if there are multiple
* paths to `base` through the inheritance graph.
@@ -286,6 +251,16 @@ class Class extends UserType {
not this.implicitCopyConstructorDeleted() and
forall(CopyConstructor cc | cc = this.getAMemberFunction() |
cc.isCompilerGenerated() and not cc.isDeleted()
) and
(
not this instanceof ClassTemplateInstantiation
or
this.(ClassTemplateInstantiation).getTemplate().hasImplicitCopyConstructor()
) and
(
not this instanceof PartialClassTemplateSpecialization
or
this.(PartialClassTemplateSpecialization).getPrimaryTemplate().hasImplicitCopyConstructor()
)
}
@@ -301,6 +276,18 @@ class Class extends UserType {
not this.implicitCopyAssignmentOperatorDeleted() and
forall(CopyAssignmentOperator ca | ca = this.getAMemberFunction() |
ca.isCompilerGenerated() and not ca.isDeleted()
) and
(
not this instanceof ClassTemplateInstantiation
or
this.(ClassTemplateInstantiation).getTemplate().hasImplicitCopyAssignmentOperator()
) and
(
not this instanceof PartialClassTemplateSpecialization
or
this.(PartialClassTemplateSpecialization)
.getPrimaryTemplate()
.hasImplicitCopyAssignmentOperator()
)
}
@@ -1070,31 +1057,6 @@ class PartialClassTemplateSpecialization extends ClassTemplateSpecialization {
override string getAPrimaryQlClass() { result = "PartialClassTemplateSpecialization" }
}
/**
* An "interface" is a class that only contains pure virtual functions (and contains
* at least one such function). For example:
* ```
* class MyInterfaceClass {
* public:
* virtual void myMethod1() = 0;
* virtual void myMethod2() = 0;
* };
* ```
*
* DEPRECATED: This class is considered to be too specific for general usage.
*/
deprecated class Interface extends Class {
Interface() {
forex(Declaration m |
m.getDeclaringType() = this.getABaseClass*() and not compgenerated(unresolveElement(m))
|
m instanceof PureVirtualFunction
)
}
override string getAPrimaryQlClass() { result = "Interface" }
}
/**
* A class/struct derivation that is virtual. For example the derivation in
* the following code is a `VirtualClassDerivation`:

View File

@@ -55,9 +55,6 @@ class ElementBase extends @element {
cached
string toString() { none() }
/** DEPRECATED: use `getAPrimaryQlClass` instead. */
deprecated string getCanonicalQLClass() { result = this.getAPrimaryQlClass() }
/**
* Gets a comma-separated list of the names of the primary CodeQL classes to which this element belongs.
*/
@@ -91,13 +88,6 @@ class Element extends ElementBase {
*/
predicate fromSource() { this.getFile().fromSource() }
/**
* Holds if this element may be from a library.
*
* DEPRECATED: always true.
*/
deprecated predicate fromLibrary() { this.getFile().fromLibrary() }
/** Gets the primary location of this element. */
Location getLocation() { none() }

View File

@@ -196,31 +196,11 @@ class Folder extends Container, @folder {
*/
deprecated string getName() { folders(underlyingElement(this), result) }
/**
* DEPRECATED: use `getAbsolutePath` instead.
* Holds if this element is named `name`.
*/
deprecated predicate hasName(string name) { name = this.getName() }
/**
* DEPRECATED: use `getAbsolutePath` instead.
* Gets the full name of this folder.
*/
deprecated string getFullName() { result = this.getName() }
/**
* DEPRECATED: use `getBaseName` instead.
* Gets the last part of the folder name.
*/
deprecated string getShortName() { result = this.getBaseName() }
/**
* DEPRECATED: use `getParentContainer` instead.
* Gets the parent folder.
*/
deprecated Folder getParent() {
containerparent(unresolveElement(result), underlyingElement(this))
}
}
/**
@@ -308,13 +288,6 @@ class File extends Container, @file {
*/
override predicate fromSource() { numlines(underlyingElement(this), _, _, _) }
/**
* Holds if this file may be from a library.
*
* DEPRECATED: For historical reasons this is true for any file.
*/
deprecated override predicate fromLibrary() { any() }
/** Gets the metric file. */
MetricFile getMetrics() { result = this }
@@ -428,25 +401,3 @@ class CppFile extends File {
override string getAPrimaryQlClass() { result = "CppFile" }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C source file, as determined by file extension.
*
* For the related notion of whether a file is compiled as Objective C
* code, use `File.compiledAsObjC`.
*/
deprecated class ObjCFile extends File {
ObjCFile() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C++ source file, as determined by file extension.
*
* For the related notion of whether a file is compiled as Objective C++
* code, use `File.compiledAsObjCpp`.
*/
deprecated class ObjCppFile extends File {
ObjCppFile() { none() }
}

View File

@@ -73,8 +73,24 @@ class Location extends @location {
/** Holds if `this` comes on a line strictly before `l`. */
pragma[inline]
predicate isBefore(Location l) {
this.getFile() = l.getFile() and this.getEndLine() < l.getStartLine()
predicate isBefore(Location l) { this.isBefore(l, false) }
/**
* Holds if `this` comes strictly before `l`. The boolean `sameLine` is
* true if `l` is on the same line as `this`, but starts at a later column.
* Otherwise, `sameLine` is false.
*/
pragma[inline]
predicate isBefore(Location l, boolean sameLine) {
this.getFile() = l.getFile() and
(
sameLine = false and
this.getEndLine() < l.getStartLine()
or
sameLine = true and
this.getEndLine() = l.getStartLine() and
this.getEndColumn() < l.getStartColumn()
)
}
/** Holds if location `l` is completely contained within this one. */
@@ -105,25 +121,6 @@ class Location extends @location {
}
}
/**
* DEPRECATED: Use `Location` instead.
* A location of an element. Not used for expressions or statements, which
* instead use LocationExpr and LocationStmt respectively.
*/
deprecated library class LocationDefault extends Location, @location_default { }
/**
* DEPRECATED: Use `Location` instead.
* A location of a statement.
*/
deprecated library class LocationStmt extends Location, @location_stmt { }
/**
* DEPRECATED: Use `Location` instead.
* A location of an expression.
*/
deprecated library class LocationExpr extends Location, @location_expr { }
/**
* Gets the length of the longest line in file `f`.
*/

View File

@@ -30,16 +30,6 @@ class Macro extends PreprocessorDirective, @ppd_define {
else result = "#define " + this.getHead() + " " + this.getBody()
}
/**
* Holds if the body of the macro starts with an unmatched closing
* parenthesis. For example:
*
* #define RPAREN() )
*
* DEPRECATED: This predicate has a misleading name.
*/
deprecated predicate isFunctionLike() { this.getBody().regexpMatch("[^(]*\\).*") }
/**
* Gets the name of the macro. For example, `MAX` in
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
@@ -261,46 +251,6 @@ class MacroInvocation extends MacroAccess {
string getExpandedArgument(int i) { macro_argument_expanded(underlyingElement(this), i, result) }
}
/**
* A top-level expression generated by a macro invocation.
*
* DEPRECATED: Use `MacroInvocation.getExpr()` directly to get an
* expression generated at the top-level of a macro invocation. Use
* `MacroInvocation.getAnAffectedElement()` to get any element generated
* by a macro invocation.
*/
deprecated class MacroInvocationExpr extends Expr {
MacroInvocationExpr() { exists(MacroInvocation i | this = i.getExpr()) }
/**
* Gets the macro invocation of which this is the top-level expression.
*/
MacroInvocation getInvocation() { result.getExpr() = this }
/** Gets the name of the invoked macro. */
string getMacroName() { result = this.getInvocation().getMacroName() }
}
/**
* A top-level statement generated by a macro invocation.
*
* DEPRECATED: Use `MacroInvocation.getStmt()` directly to get a
* statement generated at the top-level of a macro invocation. Use
* `MacroInvocation.getAnAffectedElement()` to get any element generated
* by a macro invocation.
*/
deprecated class MacroInvocationStmt extends Stmt {
MacroInvocationStmt() { exists(MacroInvocation i | this = i.getStmt()) }
/**
* Gets the macro invocation of which this is the top-level statement.
*/
MacroInvocation getInvocation() { result.getStmt() = this }
/** Gets the name of the invoked macro. */
string getMacroName() { result = this.getInvocation().getMacroName() }
}
/** Holds if `l` is the location of a macro. */
predicate macroLocation(Location l) { macrolocationbind(_, l) }

View File

@@ -233,40 +233,6 @@ class ImplicitConversionFunction extends MemberFunction {
Type getDestType() { none() } // overridden in subclasses
}
/**
* DEPRECATED: as of C++11 this class does not correspond perfectly with the
* language definition of a converting constructor.
*
* A C++ constructor that also defines an implicit conversion. For example the
* function `MyClass` in the following code is a `ConversionConstructor`:
* ```
* class MyClass {
* public:
* MyClass(const MyOtherClass &from) {
* ...
* }
* };
* ```
*/
deprecated class ConversionConstructor extends Constructor, ImplicitConversionFunction {
ConversionConstructor() {
strictcount(Parameter p | p = this.getAParameter() and not p.hasInitializer()) = 1 and
not this.hasSpecifier("explicit")
}
override string getAPrimaryQlClass() {
not this instanceof CopyConstructor and
not this instanceof MoveConstructor and
result = "ConversionConstructor"
}
/** Gets the type this `ConversionConstructor` takes as input. */
override Type getSourceType() { result = this.getParameter(0).getType() }
/** Gets the type this `ConversionConstructor` is a constructor of. */
override Type getDestType() { result = this.getDeclaringType() }
}
private predicate hasCopySignature(MemberFunction f) {
f.getParameter(0).getUnspecifiedType().(LValueReferenceType).getBaseType() = f.getDeclaringType()
}

View File

@@ -86,13 +86,6 @@ class Namespace extends NameQualifyingElement, @namespace {
/** Holds if this namespace may be from source. */
override predicate fromSource() { this.getADeclaration().fromSource() }
/**
* Holds if this namespace is in a library.
*
* DEPRECATED: never holds.
*/
deprecated override predicate fromLibrary() { not this.fromSource() }
/** Gets the metric namespace. */
MetricNamespace getMetrics() { result = this }
@@ -233,11 +226,6 @@ class GlobalNamespace extends Namespace {
override Namespace getParentNamespace() { none() }
/**
* DEPRECATED: use `getName()`.
*/
deprecated string getFullName() { result = this.getName() }
override string getFriendlyName() { result = "(global namespace)" }
}

View File

@@ -1,196 +0,0 @@
/**
* DEPRECATED: Objective-C is no longer supported.
*/
import semmle.code.cpp.Class
private import semmle.code.cpp.internal.ResolveClass
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C class.
*/
deprecated class ObjectiveClass extends Class {
ObjectiveClass() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C protocol.
*/
deprecated class Protocol extends Class {
Protocol() { none() }
/**
* Holds if the type implements the protocol, either because the type
* itself does, or because it is a type conforming to the protocol.
*/
predicate isImplementedBy(Type t) { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* A type which conforms to a protocol. Use `getAProtocol` to get a
* protocol that this type conforms to.
*/
deprecated class TypeConformingToProtocol extends DerivedType {
TypeConformingToProtocol() { none() }
/** Gets a protocol that this type conforms to. */
Protocol getAProtocol() { none() }
/** Gets the size of this type. */
override int getSize() { none() }
override int getAlignment() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C `@autoreleasepool` statement, for example
* `@autoreleasepool { int x; int y; }`.
*/
deprecated class AutoReleasePoolStmt extends Stmt {
AutoReleasePoolStmt() { none() }
override string toString() { none() }
/** Gets the body statement of this `@autoreleasepool` statement. */
Stmt getStmt() { none() }
override predicate mayBeImpure() { none() }
override predicate mayBeGloballyImpure() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C `@synchronized statement`, for example
* `@synchronized (x) { [x complicationOperation]; }`.
*/
deprecated class SynchronizedStmt extends Stmt {
SynchronizedStmt() { none() }
override string toString() { none() }
/** Gets the expression which gives the object to be locked. */
Expr getLockedObject() { none() }
/** Gets the body statement of this `@synchronized` statement. */
Stmt getStmt() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C for-in statement.
*/
deprecated class ForInStmt extends Loop {
ForInStmt() { none() }
/**
* Gets the condition expression of the `while` statement that the
* `for...in` statement desugars into.
*/
override Expr getCondition() { none() }
override Expr getControllingExpr() { none() }
/** Gets the collection that the loop iterates over. */
Expr getCollection() { none() }
/** Gets the body of the loop. */
override Stmt getStmt() { none() }
override string toString() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C category or class extension.
*/
deprecated class Category extends Class {
Category() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C class extension.
*/
deprecated class ClassExtension extends Category {
ClassExtension() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C try statement.
*/
deprecated class ObjcTryStmt extends TryStmt {
ObjcTryStmt() { none() }
override string toString() { none() }
/** Gets the finally clause of this try statement, if any. */
FinallyBlock getFinallyClause() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C `@finally` block.
*/
deprecated class FinallyBlock extends BlockStmt {
FinallyBlock() { none() }
/** Gets the try statement corresponding to this finally block. */
ObjcTryStmt getTryStmt() { none() }
}
/**
* DEPRECATED: Objective-C is no longer supported.
* An Objective C `@property`.
*/
deprecated class Property extends Declaration {
Property() { none() }
/** Gets the name of this property. */
override string getName() { none() }
/**
* Gets nothing (provided for compatibility with Declaration).
*
* For the attribute list following the `@property` keyword, use
* `getAnAttribute()`.
*/
override Specifier getASpecifier() { none() }
/**
* Gets an attribute of this property (such as `readonly`, `nonatomic`,
* or `getter=isEnabled`).
*/
Attribute getAnAttribute() { none() }
override Location getADeclarationLocation() { result = getLocation() }
override Location getDefinitionLocation() { result = getLocation() }
override Location getLocation() { none() }
/** Gets the type of this property. */
Type getType() { none() }
/**
* Gets the instance method which is called to get the value of this
* property.
*/
MemberFunction getGetter() { none() }
/**
* Gets the instance method which is called to set the value of this
* property (if it is a writable property).
*/
MemberFunction getSetter() { none() }
/**
* Gets the instance variable which stores the property value (if this
* property was explicitly or automatically `@synthesize`d).
*/
MemberVariable getInstanceVariable() { none() }
}

View File

@@ -95,22 +95,6 @@ class Parameter extends LocalScopeVariable, @parameter {
else result = this.getADeclarationEntry()
}
/**
* Gets the name of this parameter in the given block (which should be
* the body of a function with which the parameter is associated).
*
* DEPRECATED: this method was used in a previous implementation of
* getName, but is no longer in use.
*/
deprecated string getNameInBlock(BlockStmt b) {
exists(ParameterDeclarationEntry pde |
pde.getFunctionDeclarationEntry().getBlock() = b and
this.getFunction().getBlock() = b and
pde.getVariable() = this and
result = pde.getName()
)
}
/**
* Holds if this parameter has a name.
*

View File

@@ -8,9 +8,9 @@ private import PrintAST
private predicate shouldPrintDeclaration(Declaration decl) {
not decl instanceof Function
or
not exists(PrintASTConfiguration c)
not exists(PrintAstConfiguration c)
or
exists(PrintASTConfiguration config | config.shouldPrintFunction(decl))
exists(PrintAstConfiguration config | config.shouldPrintFunction(decl))
}
/**

View File

@@ -12,7 +12,7 @@ import PrintAST
* Temporarily tweak this class or make a copy to control which functions are
* printed.
*/
class Cfg extends PrintASTConfiguration {
class Cfg extends PrintAstConfiguration {
/**
* TWEAK THIS PREDICATE AS NEEDED.
* Holds if the AST for `func` should be printed.

View File

@@ -9,12 +9,12 @@
import cpp
private import semmle.code.cpp.Print
private newtype TPrintASTConfiguration = MkPrintASTConfiguration()
private newtype TPrintAstConfiguration = MkPrintAstConfiguration()
/**
* The query can extend this class to control which functions are printed.
*/
class PrintASTConfiguration extends TPrintASTConfiguration {
class PrintAstConfiguration extends TPrintAstConfiguration {
/**
* Gets a textual representation of this `PrintASTConfiguration`.
*/
@@ -27,8 +27,11 @@ class PrintASTConfiguration extends TPrintASTConfiguration {
predicate shouldPrintFunction(Function func) { any() }
}
/** DEPRECATED: Alias for PrintAstConfiguration */
deprecated class PrintASTConfiguration = PrintAstConfiguration;
private predicate shouldPrintFunction(Function func) {
exists(PrintASTConfiguration config | config.shouldPrintFunction(func))
exists(PrintAstConfiguration config | config.shouldPrintFunction(func))
}
bindingset[s]
@@ -85,8 +88,8 @@ private Function getEnclosingFunction(Locatable ast) {
* Most nodes are just a wrapper around `Locatable`, but we do synthesize new
* nodes for things like parameter lists and constructor init lists.
*/
private newtype TPrintASTNode =
TASTNode(Locatable ast) { shouldPrintFunction(getEnclosingFunction(ast)) } or
private newtype TPrintAstNode =
TAstNode(Locatable ast) { shouldPrintFunction(getEnclosingFunction(ast)) } or
TDeclarationEntryNode(DeclStmt stmt, DeclarationEntry entry) {
// We create a unique node for each pair of (stmt, entry), to avoid having one node with
// multiple parents due to extractor bug CPP-413.
@@ -106,7 +109,7 @@ private newtype TPrintASTNode =
/**
* A node in the output tree.
*/
class PrintASTNode extends TPrintASTNode {
class PrintAstNode extends TPrintAstNode {
/**
* Gets a textual representation of this node in the PrintAST output tree.
*/
@@ -116,17 +119,17 @@ class PrintASTNode extends TPrintASTNode {
* Gets the child node at index `childIndex`. Child indices must be unique,
* but need not be contiguous.
*/
abstract PrintASTNode getChildInternal(int childIndex);
abstract PrintAstNode getChildInternal(int childIndex);
/**
* Gets the child node at index `childIndex`.
* Adds edges to fully converted expressions, that are not part of the
* regular parent/child relation traversal.
*/
final PrintASTNode getChild(int childIndex) {
final PrintAstNode getChild(int childIndex) {
// The exact value of `childIndex` doesn't matter, as long as we preserve the correct order.
result =
rank[childIndex](PrintASTNode child, int nonConvertedIndex, boolean isConverted |
rank[childIndex](PrintAstNode child, int nonConvertedIndex, boolean isConverted |
childAndAccessorPredicate(child, _, nonConvertedIndex, isConverted)
|
// Unconverted children come first, then sort by original child index within each group.
@@ -138,11 +141,11 @@ class PrintASTNode extends TPrintASTNode {
* Gets the node for the `.getFullyConverted()` version of the child originally at index
* `childIndex`, if that node has any conversions.
*/
private PrintASTNode getConvertedChild(int childIndex) {
private PrintAstNode getConvertedChild(int childIndex) {
exists(Expr expr |
expr = getChildInternal(childIndex).(ASTNode).getAST() and
expr = getChildInternal(childIndex).(AstNode).getAst() and
expr.getFullyConverted() instanceof Conversion and
result.(ASTNode).getAST() = expr.getFullyConverted() and
result.(AstNode).getAst() = expr.getFullyConverted() and
not expr instanceof Conversion
)
}
@@ -166,12 +169,12 @@ class PrintASTNode extends TPrintASTNode {
/**
* Gets the children of this node.
*/
final PrintASTNode getAChild() { result = getChild(_) }
final PrintAstNode getAChild() { result = getChild(_) }
/**
* Gets the parent of this node, if any.
*/
final PrintASTNode getParent() { result.getAChild() = this }
final PrintAstNode getParent() { result.getAChild() = this }
/**
* Gets the location of this node in the source code.
@@ -196,7 +199,7 @@ class PrintASTNode extends TPrintASTNode {
* one result tuple, with `isConverted = false`.
*/
private predicate childAndAccessorPredicate(
PrintASTNode child, string childPredicate, int nonConvertedIndex, boolean isConverted
PrintAstNode child, string childPredicate, int nonConvertedIndex, boolean isConverted
) {
child = getChildInternal(nonConvertedIndex) and
childPredicate = getChildAccessorPredicateInternal(nonConvertedIndex) and
@@ -234,12 +237,15 @@ class PrintASTNode extends TPrintASTNode {
private Function getEnclosingFunction() { result = getParent*().(FunctionNode).getFunction() }
}
/** DEPRECATED: Alias for PrintAstNode */
deprecated class PrintASTNode = PrintAstNode;
/**
* Class that restricts the elements that we compute `qlClass` for.
*/
private class PrintableElement extends Element {
PrintableElement() {
exists(TASTNode(this))
exists(TAstNode(this))
or
exists(TDeclarationEntryNode(_, this))
or
@@ -262,7 +268,7 @@ private string qlClass(PrintableElement el) {
/**
* A node representing an AST node.
*/
abstract class BaseASTNode extends PrintASTNode {
abstract class BaseAstNode extends PrintAstNode {
Locatable ast;
override string toString() { result = qlClass(ast) + ast.toString() }
@@ -272,25 +278,34 @@ abstract class BaseASTNode extends PrintASTNode {
/**
* Gets the AST represented by this node.
*/
final Locatable getAST() { result = ast }
final Locatable getAst() { result = ast }
/** DEPRECATED: Alias for getAst */
deprecated Locatable getAST() { result = getAst() }
}
/** DEPRECATED: Alias for BaseAstNode */
deprecated class BaseASTNode = BaseAstNode;
/**
* A node representing an AST node other than a `DeclarationEntry`.
*/
abstract class ASTNode extends BaseASTNode, TASTNode {
ASTNode() { this = TASTNode(ast) }
abstract class AstNode extends BaseAstNode, TAstNode {
AstNode() { this = TAstNode(ast) }
}
/** DEPRECATED: Alias for AstNode */
deprecated class ASTNode = AstNode;
/**
* A node representing an `Expr`.
*/
class ExprNode extends ASTNode {
class ExprNode extends AstNode {
Expr expr;
ExprNode() { expr = ast }
override ASTNode getChildInternal(int childIndex) { result.getAST() = expr.getChild(childIndex) }
override AstNode getChildInternal(int childIndex) { result.getAst() = expr.getChild(childIndex) }
override string getProperty(string key) {
result = super.getProperty(key)
@@ -306,7 +321,7 @@ class ExprNode extends ASTNode {
}
override string getChildAccessorPredicateInternal(int childIndex) {
result = getChildAccessorWithoutConversions(ast, getChildInternal(childIndex).getAST())
result = getChildAccessorWithoutConversions(ast, getChildInternal(childIndex).getAst())
}
/**
@@ -334,9 +349,9 @@ class ConversionNode extends ExprNode {
ConversionNode() { conv = expr }
override ASTNode getChildInternal(int childIndex) {
override AstNode getChildInternal(int childIndex) {
childIndex = 0 and
result.getAST() = conv.getExpr() and
result.getAst() = conv.getExpr() and
conv.getExpr() instanceof Conversion
}
}
@@ -363,27 +378,27 @@ class CastNode extends ConversionNode {
class StmtExprNode extends ExprNode {
override StmtExpr expr;
override ASTNode getChildInternal(int childIndex) {
override AstNode getChildInternal(int childIndex) {
childIndex = 0 and
result.getAST() = expr.getStmt()
result.getAst() = expr.getStmt()
}
}
/**
* A node representing a `DeclarationEntry`.
*/
class DeclarationEntryNode extends BaseASTNode, TDeclarationEntryNode {
class DeclarationEntryNode extends BaseAstNode, TDeclarationEntryNode {
override DeclarationEntry ast;
DeclStmt declStmt;
DeclarationEntryNode() { this = TDeclarationEntryNode(declStmt, ast) }
override PrintASTNode getChildInternal(int childIndex) { none() }
override PrintAstNode getChildInternal(int childIndex) { none() }
override string getChildAccessorPredicateInternal(int childIndex) { none() }
override string getProperty(string key) {
result = BaseASTNode.super.getProperty(key)
result = BaseAstNode.super.getProperty(key)
or
key = "Type" and
result = qlClass(ast.getType()) + ast.getType().toString()
@@ -396,9 +411,9 @@ class DeclarationEntryNode extends BaseASTNode, TDeclarationEntryNode {
class VariableDeclarationEntryNode extends DeclarationEntryNode {
override VariableDeclarationEntry ast;
override ASTNode getChildInternal(int childIndex) {
override AstNode getChildInternal(int childIndex) {
childIndex = 0 and
result.getAST() = ast.getVariable().getInitializer()
result.getAst() = ast.getVariable().getInitializer()
}
override string getChildAccessorPredicateInternal(int childIndex) {
@@ -410,23 +425,23 @@ class VariableDeclarationEntryNode extends DeclarationEntryNode {
/**
* A node representing a `Stmt`.
*/
class StmtNode extends ASTNode {
class StmtNode extends AstNode {
Stmt stmt;
StmtNode() { stmt = ast }
override BaseASTNode getChildInternal(int childIndex) {
override BaseAstNode getChildInternal(int childIndex) {
exists(Locatable child |
child = stmt.getChild(childIndex) and
(
result.getAST() = child.(Expr) or
result.getAST() = child.(Stmt)
result.getAst() = child.(Expr) or
result.getAst() = child.(Stmt)
)
)
}
override string getChildAccessorPredicateInternal(int childIndex) {
result = getChildAccessorWithoutConversions(ast, getChildInternal(childIndex).getAST())
result = getChildAccessorWithoutConversions(ast, getChildInternal(childIndex).getAst())
}
}
@@ -449,12 +464,12 @@ class DeclStmtNode extends StmtNode {
/**
* A node representing a `Parameter`.
*/
class ParameterNode extends ASTNode {
class ParameterNode extends AstNode {
Parameter param;
ParameterNode() { param = ast }
final override PrintASTNode getChildInternal(int childIndex) { none() }
final override PrintAstNode getChildInternal(int childIndex) { none() }
final override string getChildAccessorPredicateInternal(int childIndex) { none() }
@@ -469,14 +484,14 @@ class ParameterNode extends ASTNode {
/**
* A node representing an `Initializer`.
*/
class InitializerNode extends ASTNode {
class InitializerNode extends AstNode {
Initializer init;
InitializerNode() { init = ast }
override ASTNode getChildInternal(int childIndex) {
override AstNode getChildInternal(int childIndex) {
childIndex = 0 and
result.getAST() = init.getExpr()
result.getAst() = init.getExpr()
}
override string getChildAccessorPredicateInternal(int childIndex) {
@@ -488,7 +503,7 @@ class InitializerNode extends ASTNode {
/**
* A node representing the parameters of a `Function`.
*/
class ParametersNode extends PrintASTNode, TParametersNode {
class ParametersNode extends PrintAstNode, TParametersNode {
Function func;
ParametersNode() { this = TParametersNode(func) }
@@ -497,8 +512,8 @@ class ParametersNode extends PrintASTNode, TParametersNode {
final override Location getLocation() { result = getRepresentativeLocation(func) }
override ASTNode getChildInternal(int childIndex) {
result.getAST() = func.getParameter(childIndex)
override AstNode getChildInternal(int childIndex) {
result.getAst() = func.getParameter(childIndex)
}
override string getChildAccessorPredicateInternal(int childIndex) {
@@ -515,7 +530,7 @@ class ParametersNode extends PrintASTNode, TParametersNode {
/**
* A node representing the initializer list of a `Constructor`.
*/
class ConstructorInitializersNode extends PrintASTNode, TConstructorInitializersNode {
class ConstructorInitializersNode extends PrintAstNode, TConstructorInitializersNode {
Constructor ctor;
ConstructorInitializersNode() { this = TConstructorInitializersNode(ctor) }
@@ -524,8 +539,8 @@ class ConstructorInitializersNode extends PrintASTNode, TConstructorInitializers
final override Location getLocation() { result = getRepresentativeLocation(ctor) }
final override ASTNode getChildInternal(int childIndex) {
result.getAST() = ctor.getInitializer(childIndex)
final override AstNode getChildInternal(int childIndex) {
result.getAst() = ctor.getInitializer(childIndex)
}
final override string getChildAccessorPredicateInternal(int childIndex) {
@@ -542,7 +557,7 @@ class ConstructorInitializersNode extends PrintASTNode, TConstructorInitializers
/**
* A node representing the destruction list of a `Destructor`.
*/
class DestructorDestructionsNode extends PrintASTNode, TDestructorDestructionsNode {
class DestructorDestructionsNode extends PrintAstNode, TDestructorDestructionsNode {
Destructor dtor;
DestructorDestructionsNode() { this = TDestructorDestructionsNode(dtor) }
@@ -551,8 +566,8 @@ class DestructorDestructionsNode extends PrintASTNode, TDestructorDestructionsNo
final override Location getLocation() { result = getRepresentativeLocation(dtor) }
final override ASTNode getChildInternal(int childIndex) {
result.getAST() = dtor.getDestruction(childIndex)
final override AstNode getChildInternal(int childIndex) {
result.getAst() = dtor.getDestruction(childIndex)
}
final override string getChildAccessorPredicateInternal(int childIndex) {
@@ -569,14 +584,14 @@ class DestructorDestructionsNode extends PrintASTNode, TDestructorDestructionsNo
/**
* A node representing a `Function`.
*/
class FunctionNode extends ASTNode {
class FunctionNode extends AstNode {
Function func;
FunctionNode() { func = ast }
override string toString() { result = qlClass(func) + getIdentityString(func) }
override PrintASTNode getChildInternal(int childIndex) {
override PrintAstNode getChildInternal(int childIndex) {
childIndex = 0 and
result.(ParametersNode).getFunction() = func
or
@@ -584,7 +599,7 @@ class FunctionNode extends ASTNode {
result.(ConstructorInitializersNode).getConstructor() = func
or
childIndex = 2 and
result.(ASTNode).getAST() = func.getEntryPoint()
result.(AstNode).getAst() = func.getEntryPoint()
or
childIndex = 3 and
result.(DestructorDestructionsNode).getDestructor() = func
@@ -603,7 +618,7 @@ class FunctionNode extends ASTNode {
private int getOrder() {
this =
rank[result](FunctionNode node, Function function, string file, int line, int column |
node.getAST() = function and
node.getAst() = function and
locationSortKeys(function, file, line, column)
|
node order by file, line, column, getIdentityString(function)
@@ -856,7 +871,7 @@ private predicate namedExprChildPredicates(Expr expr, Element ele, string pred)
}
/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */
query predicate nodes(PrintASTNode node, string key, string value) {
query predicate nodes(PrintAstNode node, string key, string value) {
node.shouldPrint() and
value = node.getProperty(key)
}
@@ -865,7 +880,7 @@ query predicate nodes(PrintASTNode node, string key, string value) {
* Holds if `target` is a child of `source` in the AST, and property `key` of the edge has the
* given `value`.
*/
query predicate edges(PrintASTNode source, PrintASTNode target, string key, string value) {
query predicate edges(PrintAstNode source, PrintAstNode target, string key, string value) {
exists(int childIndex |
source.shouldPrint() and
target.shouldPrint() and

View File

@@ -38,7 +38,7 @@ class FunctionSpecifier extends Specifier {
/**
* A C/C++ storage class specifier: `auto`, `register`, `static`, `extern`,
* or `mutable".
* or `mutable`.
*/
class StorageClassSpecifier extends Specifier {
StorageClassSpecifier() { this.hasName(["auto", "register", "static", "extern", "mutable"]) }

View File

@@ -94,6 +94,7 @@ class Type extends Locatable, @type {
* The result of this predicate will be the type itself, except in the case of a TypedefType or a Decltype,
* in which case the result will be type which results from (possibly recursively) resolving typedefs.
*/
pragma[nomagic]
Type getUnderlyingType() { result = this }
/**
@@ -1085,50 +1086,6 @@ class DerivedType extends Type, @derivedtype {
override predicate involvesTemplateParameter() { this.getBaseType().involvesTemplateParameter() }
override Type stripType() { result = this.getBaseType().stripType() }
/**
* Holds if this type has the `__autoreleasing` specifier or if it points to
* a type with the `__autoreleasing` specifier.
*
* DEPRECATED: use `hasSpecifier` directly instead.
*/
deprecated predicate isAutoReleasing() {
this.hasSpecifier("__autoreleasing") or
this.(PointerType).getBaseType().hasSpecifier("__autoreleasing")
}
/**
* Holds if this type has the `__strong` specifier or if it points to
* a type with the `__strong` specifier.
*
* DEPRECATED: use `hasSpecifier` directly instead.
*/
deprecated predicate isStrong() {
this.hasSpecifier("__strong") or
this.(PointerType).getBaseType().hasSpecifier("__strong")
}
/**
* Holds if this type has the `__unsafe_unretained` specifier or if it points
* to a type with the `__unsafe_unretained` specifier.
*
* DEPRECATED: use `hasSpecifier` directly instead.
*/
deprecated predicate isUnsafeRetained() {
this.hasSpecifier("__unsafe_unretained") or
this.(PointerType).getBaseType().hasSpecifier("__unsafe_unretained")
}
/**
* Holds if this type has the `__weak` specifier or if it points to
* a type with the `__weak` specifier.
*
* DEPRECATED: use `hasSpecifier` directly instead.
*/
deprecated predicate isWeak() {
this.hasSpecifier("__weak") or
this.(PointerType).getBaseType().hasSpecifier("__weak")
}
}
/**

View File

@@ -106,25 +106,4 @@ class NestedTypedefType extends TypedefType {
NestedTypedefType() { this.isMember() }
override string getAPrimaryQlClass() { result = "NestedTypedefType" }
/**
* DEPRECATED: use `.hasSpecifier("private")` instead.
*
* Holds if this member is private.
*/
deprecated predicate isPrivate() { this.hasSpecifier("private") }
/**
* DEPRECATED: `.hasSpecifier("protected")` instead.
*
* Holds if this member is protected.
*/
deprecated predicate isProtected() { this.hasSpecifier("protected") }
/**
* DEPRECATED: use `.hasSpecifier("public")` instead.
*
* Holds if this member is public.
*/
deprecated predicate isPublic() { this.hasSpecifier("public") }
}

View File

@@ -556,24 +556,6 @@ class MemberVariable extends Variable, @membervariable {
private Type getAType() { membervariables(underlyingElement(this), unresolveElement(result), _) }
}
/**
* A C/C++ function pointer variable.
*
* DEPRECATED: use `Variable.getType() instanceof FunctionPointerType` instead.
*/
deprecated class FunctionPointerVariable extends Variable {
FunctionPointerVariable() { this.getType() instanceof FunctionPointerType }
}
/**
* A C/C++ function pointer member variable.
*
* DEPRECATED: use `MemberVariable.getType() instanceof FunctionPointerType` instead.
*/
deprecated class FunctionPointerMemberVariable extends MemberVariable {
FunctionPointerMemberVariable() { this instanceof FunctionPointerVariable }
}
/**
* A C++14 variable template. For example, in the following code the variable
* template `v` defines a family of variables:

View File

@@ -4,21 +4,14 @@
import semmle.files.FileSystem
private class TXMLLocatable =
private class TXmlLocatable =
@xmldtd or @xmlelement or @xmlattribute or @xmlnamespace or @xmlcomment or @xmlcharacters;
/** An XML element that has a location. */
class XMLLocatable extends @xmllocatable, TXMLLocatable {
class XMLLocatable extends @xmllocatable, TXmlLocatable {
/** Gets the source location for this element. */
Location getLocation() { xmllocations(this, result) }
/**
* DEPRECATED: Use `getLocation()` instead.
*
* Gets the source location for this element.
*/
deprecated Location getALocation() { result = this.getLocation() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
@@ -83,21 +76,6 @@ class XMLParent extends @xmlparent {
/** Gets the number of places in the body of this XML parent where text occurs. */
int getNumberOfCharacterSets() { result = count(int pos | xmlChars(_, _, this, pos, _, _)) }
/**
* DEPRECATED: Internal.
*
* Append the character sequences of this XML parent from left to right, separated by a space,
* up to a specified (zero-based) index.
*/
deprecated string charsSetUpTo(int n) {
n = 0 and xmlChars(_, result, this, 0, _, _)
or
n > 0 and
exists(string chars | xmlChars(_, chars, this, n, _, _) |
result = this.charsSetUpTo(n - 1) + " " + chars
)
}
/**
* Gets the result of appending all the character sequences of this XML parent from
* left to right, separated by a space.

View File

@@ -2,20 +2,6 @@ import cpp
import semmle.code.cpp.models.interfaces.Allocation
import semmle.code.cpp.models.interfaces.Deallocation
/**
* A library routine that allocates memory.
*
* DEPRECATED: Use the `AllocationFunction` class instead of this predicate.
*/
deprecated predicate allocationFunction(Function f) { f instanceof AllocationFunction }
/**
* A call to a library routine that allocates memory.
*
* DEPRECATED: Use `AllocationExpr` instead (this also includes `new` expressions).
*/
deprecated predicate allocationCall(FunctionCall fc) { fc instanceof AllocationExpr }
/**
* A library routine that frees memory.
*/
@@ -33,13 +19,6 @@ predicate freeCall(FunctionCall fc, Expr arg) { arg = fc.(DeallocationExpr).getF
*/
predicate isMemoryManagementExpr(Expr e) { isAllocationExpr(e) or e instanceof DeallocationExpr }
/**
* Is e an allocation from stdlib.h (`malloc`, `realloc` etc)?
*
* DEPRECATED: Use `AllocationExpr` instead (this also includes `new` expressions).
*/
deprecated predicate isStdLibAllocationExpr(Expr e) { allocationCall(e) }
/**
* Is e some kind of allocation (`new`, `alloc`, `realloc` etc)?
*/
@@ -48,19 +27,3 @@ predicate isAllocationExpr(Expr e) {
or
e = any(NewOrNewArrayExpr new | not exists(new.getPlacementPointer()))
}
/**
* Is e some kind of allocation (`new`, `alloc`, `realloc` etc) with a fixed size?
*
* DEPRECATED: Use `AllocationExpr.getSizeBytes()` instead.
*/
deprecated predicate isFixedSizeAllocationExpr(Expr allocExpr, int size) {
size = allocExpr.(AllocationExpr).getSizeBytes()
}
/**
* Is e some kind of deallocation (`delete`, `free`, `realloc` etc)?
*
* DEPRECATED: Use `DeallocationExpr` instead.
*/
deprecated predicate isDeallocationExpr(Expr e) { e instanceof DeallocationExpr }

View File

@@ -207,26 +207,6 @@ predicate variadicFormatter(Function f, string type, int formatParamIndex, int o
callsVariadicFormatter(f, type, formatParamIndex, outputParamIndex)
}
/**
* A standard function such as `vprintf` that has a format parameter
* and a variable argument list of type `va_arg`.
*
* DEPRECATED: Use the four argument version instead.
*/
deprecated predicate primitiveVariadicFormatter(TopLevelFunction f, int formatParamIndex) {
primitiveVariadicFormatter(f, _, formatParamIndex, _)
}
/**
* Holds if `f` is a function such as `vprintf` that has a format parameter
* (at `formatParamIndex`) and a variable argument list of type `va_arg`.
*
* DEPRECATED: Use the four argument version instead.
*/
deprecated predicate variadicFormatter(Function f, int formatParamIndex) {
variadicFormatter(f, _, formatParamIndex, _)
}
/**
* A function not in the standard library which takes a `printf`-like formatting
* string and a variable number of arguments.
@@ -428,13 +408,6 @@ class FormatLiteral extends Literal {
*/
FormattingFunctionCall getUse() { result.getFormat() = this }
/**
* Holds if the default meaning of `%s` is a `wchar_t *`, rather than
* a `char *` (either way, `%S` will have the opposite meaning).
* DEPRECATED: Use getDefaultCharType() instead.
*/
deprecated predicate isWideCharDefault() { this.getUse().getTarget().isWideCharDefault() }
/**
* Gets the default character type expected for `%s` by this format literal. Typically
* `char` or `wchar_t`.

View File

@@ -223,20 +223,6 @@ class BasicBlock extends ControlFlowNodeBase {
*/
predicate inLoop() { this.getASuccessor+() = this }
/**
* DEPRECATED since version 1.11: this predicate does not match the standard
* definition of _loop header_.
*
* Holds if this basic block is in a loop of the control-flow graph and
* additionally has an incoming edge that is not part of any loop containing
* this basic block. A typical example would be the basic block that computes
* `x > 0` in an outermost loop `while (x > 0) { ... }`.
*/
deprecated predicate isLoopHeader() {
this.inLoop() and
exists(BasicBlock pred | pred = this.getAPredecessor() and not pred = this.getASuccessor+())
}
/**
* Holds if control flow may reach this basic block from a function entry
* point or any handler of a reachable `try` statement.

View File

@@ -65,7 +65,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
* taken when this expression is true.
*/
ControlFlowNode getATrueSuccessor() {
qlCFGTrueSuccessor(this, result) and
qlCfgTrueSuccessor(this, result) and
result = this.getASuccessor()
}
@@ -74,7 +74,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase {
* taken when this expression is false.
*/
ControlFlowNode getAFalseSuccessor() {
qlCFGFalseSuccessor(this, result) and
qlCfgFalseSuccessor(this, result) and
result = this.getASuccessor()
}
@@ -94,24 +94,6 @@ import ControlFlowGraphPublic
*/
class ControlFlowNodeBase extends ElementBase, @cfgnode { }
/**
* DEPRECATED: Use `ControlFlowNode.getATrueSuccessor()` instead.
* Holds when `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is true.
*/
deprecated predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGTrueSuccessor(n1, n2)
}
/**
* DEPRECATED: Use `ControlFlowNode.getAFalseSuccessor()` instead.
* Holds when `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is false.
*/
deprecated predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) {
qlCFGFalseSuccessor(n1, n2)
}
/**
* An abstract class that can be extended to add additional edges to the
* control-flow graph. Instances of this class correspond to the source nodes
@@ -139,7 +121,7 @@ abstract class AdditionalControlFlowEdge extends ControlFlowNodeBase {
* `AdditionalControlFlowEdge`. Use this relation instead of `qlCFGSuccessor`.
*/
predicate successors_extended(ControlFlowNodeBase source, ControlFlowNodeBase target) {
qlCFGSuccessor(source, target)
qlCfgSuccessor(source, target)
or
source.(AdditionalControlFlowEdge).getAnEdgeTarget() = target
}

View File

@@ -33,8 +33,8 @@ class GuardCondition extends Expr {
or
// the IR short-circuits if(!x)
// don't produce a guard condition for `y = !x` and other non-short-circuited cases
not exists(Instruction inst | this.getFullyConverted() = inst.getAST()) and
exists(IRGuardCondition ir | this.(NotExpr).getOperand() = ir.getAST())
not exists(Instruction inst | this.getFullyConverted() = inst.getAst()) and
exists(IRGuardCondition ir | this.(NotExpr).getOperand() = ir.getAst())
}
/**
@@ -146,8 +146,8 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
*/
private class GuardConditionFromShortCircuitNot extends GuardCondition, NotExpr {
GuardConditionFromShortCircuitNot() {
not exists(Instruction inst | this.getFullyConverted() = inst.getAST()) and
exists(IRGuardCondition ir | this.getOperand() = ir.getAST())
not exists(Instruction inst | this.getFullyConverted() = inst.getAst()) and
exists(IRGuardCondition ir | this.getOperand() = ir.getAst())
}
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
@@ -241,7 +241,7 @@ private class GuardConditionFromIR extends GuardCondition {
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
exists(IRBlock irb |
forex(IRGuardCondition inst | inst = ir | inst.controls(irb, testIsTrue)) and
irb.getAnInstruction().getAST().(ControlFlowNode).getBasicBlock() = controlled and
irb.getAnInstruction().getAst().(ControlFlowNode).getBasicBlock() = controlled and
not isUnreachedBlock(irb)
)
}

View File

@@ -1,393 +0,0 @@
/**
* DEPRECATED: Use `StackVariableReachability` instead.
*/
import cpp
/**
* DEPRECATED: Use `StackVariableReachability` instead.
*
* A reachability analysis for control-flow nodes involving stack variables.
* This defines sources, sinks, and any other configurable aspect of the
* analysis. Multiple analyses can coexist. To create an analysis, extend this
* class with a subclass whose characteristic predicate is a unique singleton
* string. For example, write
*
* ```
* class MyAnalysisConfiguration extends LocalScopeVariableReachability {
* MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" }
* // Override `isSource` and `isSink`.
* // Override `isBarrier`.
* }
* ```
*
* Then, to query whether there is flow between some source and sink, call the
* `reaches` predicate on an instance of `MyAnalysisConfiguration`.
*/
abstract deprecated class LocalScopeVariableReachability extends string {
bindingset[this]
LocalScopeVariableReachability() { length() >= 0 }
/** Holds if `node` is a source for the reachability analysis using variable `v`. */
abstract predicate isSource(ControlFlowNode node, LocalScopeVariable v);
/** Holds if `sink` is a (potential) sink for the reachability analysis using variable `v`. */
abstract predicate isSink(ControlFlowNode node, LocalScopeVariable v);
/** Holds if `node` is a barrier for the reachability analysis using variable `v`. */
abstract predicate isBarrier(ControlFlowNode node, LocalScopeVariable v);
/**
* Holds if the source node `source` can reach the sink `sink` without crossing
* a barrier. This is (almost) equivalent to the following QL predicate but
* uses basic blocks internally for better performance:
*
* ```
* predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* reachesImpl(source, v, sink)
* and
* isSink(sink, v)
* }
*
* predicate reachesImpl(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* sink = source.getASuccessor() and isSource(source, v)
* or
* exists(ControlFlowNode mid | reachesImpl(source, v, mid) |
* not isBarrier(mid, v)
* and
* sink = mid.getASuccessor()
* )
* }
* ```
*
* In addition to using a better performing implementation, this analysis
* accounts for loops where the condition is provably true upon entry.
*/
predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
/*
* Implementation detail: the predicates in this class are a generalization of
* those in DefinitionsAndUses.qll, and should be kept in sync.
*
* Unfortunately, caching of abstract predicates does not work well, so the
* predicates in DefinitionsAndUses.qll cannot use this library.
*/
exists(BasicBlock bb, int i |
this.isSource(source, v) and
bb.getNode(i) = source and
not bb.isUnreachable()
|
exists(int j |
j > i and
sink = bb.getNode(j) and
this.isSink(sink, v) and
not exists(int k | this.isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1])
)
or
not exists(int k | this.isBarrier(bb.getNode(k), v) | k > i) and
this.bbSuccessorEntryReaches(bb, v, sink, _)
)
}
private predicate bbSuccessorEntryReaches(
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
boolean skipsFirstLoopAlwaysTrueUponEntry
) {
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
succSkipsFirstLoopAlwaysTrueUponEntry)
|
this.bbEntryReachesLocally(succ, v, node) and
succSkipsFirstLoopAlwaysTrueUponEntry = false
or
not this.isBarrier(succ.getNode(_), v) and
this.bbSuccessorEntryReaches(succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
)
}
private predicate bbEntryReachesLocally(
BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
) {
exists(int n |
node = bb.getNode(n) and
this.isSink(node, v)
|
not exists(this.firstBarrierIndexIn(bb, v))
or
n <= this.firstBarrierIndexIn(bb, v)
)
}
private int firstBarrierIndexIn(BasicBlock bb, SemanticStackVariable v) {
result = min(int m | this.isBarrier(bb.getNode(m), v))
}
}
/**
* Holds if `bb` contains the entry point `loop` for a loop at position `i`.
* The condition of that loop is provably true upon entry but not provably
* true in general (if it were, the false-successor had already been removed
* from the CFG).
*
* Examples:
* ```
* for (int i = 0; i < 2; i++) { } // always true upon entry
* for (int i = 0; true; i++) { } // always true
* ```
*/
private predicate bbLoopEntryConditionAlwaysTrueAt(BasicBlock bb, int i, ControlFlowNode loop) {
exists(Expr condition |
loopConditionAlwaysTrueUponEntry(loop, condition) and
not conditionAlwaysTrue(condition) and
bb.getNode(i) = loop
)
}
/**
* Basic block `pred` contains all or part of the condition belonging to a loop,
* and there is an edge from `pred` to `succ` that concludes the condition.
* If the edge corrseponds with the loop condition being found to be `true`, then
* `skipsLoop` is `false`. Otherwise the edge corresponds with the loop condition
* being found to be `false` and `skipsLoop` is `true`. Non-concluding edges
* within a complex loop condition are not matched by this predicate.
*/
private predicate bbLoopConditionAlwaysTrueUponEntrySuccessor(
BasicBlock pred, BasicBlock succ, boolean skipsLoop
) {
exists(Expr cond |
loopConditionAlwaysTrueUponEntry(_, cond) and
cond.getAChild*() = pred.getEnd() and
succ = pred.getASuccessor() and
not cond.getAChild*() = succ.getStart() and
(
succ = pred.getAFalseSuccessor() and
skipsLoop = true
or
succ = pred.getATrueSuccessor() and
skipsLoop = false
)
)
}
/**
* Loop invariant for `bbSuccessorEntryReaches`:
*
* - `succ` is a successor of `pred`.
* - `predSkipsFirstLoopAlwaysTrueUponEntry`: whether the path from
* `pred` (via `succ`) skips the first loop where the condition is
* provably true upon entry.
* - `succSkipsFirstLoopAlwaysTrueUponEntry`: whether the path from
* `succ` skips the first loop where the condition is provably true
* upon entry.
* - If `pred` contains the entry point of a loop where the condition
* is provably true upon entry, then `succ` is not allowed to skip
* that loop (`succSkipsFirstLoopAlwaysTrueUponEntry = false`).
*/
predicate bbSuccessorEntryReachesLoopInvariant(
BasicBlock pred, BasicBlock succ, boolean predSkipsFirstLoopAlwaysTrueUponEntry,
boolean succSkipsFirstLoopAlwaysTrueUponEntry
) {
succ = pred.getASuccessor() and
(succSkipsFirstLoopAlwaysTrueUponEntry = true or succSkipsFirstLoopAlwaysTrueUponEntry = false) and
(
// The edge from `pred` to `succ` is from a loop condition provably
// true upon entry, so the value of `predSkipsFirstLoopAlwaysTrueUponEntry`
// is determined by whether the true edge or the false edge is chosen,
// regardless of the value of `succSkipsFirstLoopAlwaysTrueUponEntry`.
bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, succ, predSkipsFirstLoopAlwaysTrueUponEntry)
or
// The edge from `pred` to `succ` is _not_ from a loop condition provably
// true upon entry, so the values of `predSkipsFirstLoopAlwaysTrueUponEntry`
// and `succSkipsFirstLoopAlwaysTrueUponEntry` must be the same.
not bbLoopConditionAlwaysTrueUponEntrySuccessor(pred, succ, _) and
succSkipsFirstLoopAlwaysTrueUponEntry = predSkipsFirstLoopAlwaysTrueUponEntry and
// Moreover, if `pred` contains the entry point of a loop where the
// condition is provably true upon entry, then `succ` is not allowed
// to skip that loop, and hence `succSkipsFirstLoopAlwaysTrueUponEntry = false`.
(
bbLoopEntryConditionAlwaysTrueAt(pred, _, _)
implies
succSkipsFirstLoopAlwaysTrueUponEntry = false
)
)
}
/**
* DEPRECATED: Use `StackVariableReachabilityWithReassignment` instead.
*
* Reachability analysis for control-flow nodes involving stack variables.
* Unlike `LocalScopeVariableReachability`, this analysis takes variable
* reassignments into account.
*
* This class is used like `LocalScopeVariableReachability`, except that
* subclasses should override `isSourceActual` and `isSinkActual` instead of
* `isSource` and `isSink`, and that there is a `reachesTo` predicate in
* addition to `reaches`.
*/
abstract deprecated class LocalScopeVariableReachabilityWithReassignment extends LocalScopeVariableReachability {
bindingset[this]
LocalScopeVariableReachabilityWithReassignment() { length() >= 0 }
/** Override this predicate rather than `isSource` (`isSource` is used internally). */
abstract predicate isSourceActual(ControlFlowNode node, LocalScopeVariable v);
/** Override this predicate rather than `isSink` (`isSink` is used internally). */
abstract predicate isSinkActual(ControlFlowNode node, LocalScopeVariable v);
/**
* Holds if the source node `source` can reach the sink `sink` without crossing
* a barrier, taking reassignments into account. This is (almost) equivalent
* to the following QL predicate, but uses basic blocks internally for better
* performance:
*
* ```
* predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* reachesImpl(source, v, sink)
* and
* isSinkActual(sink, v)
* }
*
* predicate reachesImpl(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
* isSourceActual(source, v)
* and
* (
* sink = source.getASuccessor()
* or
* exists(ControlFlowNode mid, SemanticStackVariable v0 | reachesImpl(source, v0, mid) |
* // ordinary successor
* not isBarrier(mid, v) and
* sink = mid.getASuccessor() and
* v = v0
* or
* // reassigned from v0 to v
* exprDefinition(v, mid, v0.getAnAccess()) and
* sink = mid.getASuccessor()
* )
* )
* }
* ```
*
* In addition to using a better performing implementation, this analysis
* accounts for loops where the condition is provably true upon entry.
*/
override predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
this.reachesTo(source, v, sink, _)
}
/**
* As `reaches`, but also specifies the last variable it was reassigned to (`v0`).
*/
predicate reachesTo(
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink, SemanticStackVariable v0
) {
exists(ControlFlowNode def |
this.actualSourceReaches(source, v, def, v0) and
LocalScopeVariableReachability.super.reaches(def, v0, sink) and
this.isSinkActual(sink, v0)
)
}
private predicate actualSourceReaches(
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
) {
this.isSourceActual(source, v) and def = source and v0 = v
or
exists(ControlFlowNode source1, SemanticStackVariable v1 |
this.actualSourceReaches(source, v, source1, v1)
|
this.reassignment(source1, v1, def, v0)
)
}
private predicate reassignment(
ControlFlowNode source, SemanticStackVariable v, ControlFlowNode def, SemanticStackVariable v0
) {
LocalScopeVariableReachability.super.reaches(source, v, def) and
exprDefinition(v0, def, v.getAnAccess())
}
final override predicate isSource(ControlFlowNode node, LocalScopeVariable v) {
this.isSourceActual(node, v)
or
// Reassignment generates a new (non-actual) source
this.reassignment(_, _, node, v)
}
final override predicate isSink(ControlFlowNode node, LocalScopeVariable v) {
this.isSinkActual(node, v)
or
// Reassignment generates a new (non-actual) sink
exprDefinition(_, node, v.getAnAccess())
}
}
/**
* DEPRECATED: Use `StackVariableReachabilityExt` instead.
*
* Same as `LocalScopeVariableReachability`, but `isBarrier` works on control-flow
* edges rather than nodes and is therefore parameterized by the original
* source node as well. Otherwise, this class is used like
* `LocalScopeVariableReachability`.
*/
abstract deprecated class LocalScopeVariableReachabilityExt extends string {
bindingset[this]
LocalScopeVariableReachabilityExt() { length() >= 0 }
/** `node` is a source for the reachability analysis using variable `v`. */
abstract predicate isSource(ControlFlowNode node, LocalScopeVariable v);
/** `sink` is a (potential) sink for the reachability analysis using variable `v`. */
abstract predicate isSink(ControlFlowNode node, LocalScopeVariable v);
/** `node` is a barrier for the reachability analysis using variable `v` and starting from `source`. */
abstract predicate isBarrier(
ControlFlowNode source, ControlFlowNode node, ControlFlowNode next, LocalScopeVariable v
);
/** See `LocalScopeVariableReachability.reaches`. */
predicate reaches(ControlFlowNode source, SemanticStackVariable v, ControlFlowNode sink) {
exists(BasicBlock bb, int i |
this.isSource(source, v) and
bb.getNode(i) = source and
not bb.isUnreachable()
|
exists(int j |
j > i and
sink = bb.getNode(j) and
this.isSink(sink, v) and
not exists(int k | this.isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) |
k in [i .. j - 1]
)
)
or
not exists(int k | this.isBarrier(source, bb.getNode(k), bb.getNode(k + 1), v) | k >= i) and
this.bbSuccessorEntryReaches(source, bb, v, sink, _)
)
}
private predicate bbSuccessorEntryReaches(
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node,
boolean skipsFirstLoopAlwaysTrueUponEntry
) {
exists(BasicBlock succ, boolean succSkipsFirstLoopAlwaysTrueUponEntry |
bbSuccessorEntryReachesLoopInvariant(bb, succ, skipsFirstLoopAlwaysTrueUponEntry,
succSkipsFirstLoopAlwaysTrueUponEntry) and
not this.isBarrier(source, bb.getEnd(), succ.getStart(), v)
|
this.bbEntryReachesLocally(source, succ, v, node) and
succSkipsFirstLoopAlwaysTrueUponEntry = false
or
not exists(int k | this.isBarrier(source, succ.getNode(k), succ.getNode(k + 1), v)) and
this.bbSuccessorEntryReaches(source, succ, v, node, succSkipsFirstLoopAlwaysTrueUponEntry)
)
}
private predicate bbEntryReachesLocally(
ControlFlowNode source, BasicBlock bb, SemanticStackVariable v, ControlFlowNode node
) {
this.isSource(source, v) and
exists(int n | node = bb.getNode(n) and this.isSink(node, v) |
not exists(int m | m < n | this.isBarrier(source, bb.getNode(m), bb.getNode(m + 1), v))
)
}
}

View File

@@ -156,15 +156,6 @@ class AnalysedExpr extends Expr {
this.isValidCheck(v) and result = this.getATrueSuccessor()
}
/**
* DEPRECATED: Use `getNonNullSuccessor` instead, which does the same.
*/
deprecated ControlFlowNode getValidSuccessor(LocalScopeVariable v) {
this.isValidCheck(v) and result = this.getATrueSuccessor()
or
this.isNullCheck(v) and result = this.getAFalseSuccessor()
}
/**
* Holds if this is a `VariableAccess` of `v` nested inside a condition.
*/

View File

@@ -10,10 +10,13 @@ import SSAUtils
* The SSA logic comes in two versions: the standard SSA and range-analysis RangeSSA.
* This class provides the standard SSA logic.
*/
library class StandardSSA extends SSAHelper {
StandardSSA() { this = 0 }
library class StandardSsa extends SsaHelper {
StandardSsa() { this = 0 }
}
/** DEPRECATED: Alias for StandardSsa */
deprecated class StandardSSA = StandardSsa;
/**
* A definition of one or more SSA variables, including phi node definitions.
* An _SSA variable_, as defined in the literature, is effectively the pair of
@@ -27,22 +30,22 @@ library class StandardSSA extends SSAHelper {
* statically seen to be unreachable.
*/
class SsaDefinition extends ControlFlowNodeBase {
SsaDefinition() { exists(StandardSSA x | x.ssa_defn(_, this, _, _)) }
SsaDefinition() { exists(StandardSsa x | x.ssa_defn(_, this, _, _)) }
/**
* Gets a variable corresponding to an SSA StackVariable defined by
* this definition.
*/
StackVariable getAVariable() { exists(StandardSSA x | x.ssa_defn(result, this, _, _)) }
StackVariable getAVariable() { exists(StandardSsa x | x.ssa_defn(result, this, _, _)) }
/**
* Gets a string representation of the SSA variable represented by the pair
* `(this, v)`.
*/
string toString(StackVariable v) { exists(StandardSSA x | result = x.toString(this, v)) }
string toString(StackVariable v) { exists(StandardSsa x | result = x.toString(this, v)) }
/** Gets a use of the SSA variable represented by the pair `(this, v)`. */
VariableAccess getAUse(StackVariable v) { exists(StandardSSA x | result = x.getAUse(this, v)) }
VariableAccess getAUse(StackVariable v) { exists(StandardSsa x | result = x.getAUse(this, v)) }
/**
* Gets the control-flow node for this definition. This will usually be the
@@ -62,7 +65,7 @@ class SsaDefinition extends ControlFlowNodeBase {
BasicBlock getBasicBlock() { result.contains(this.getDefinition()) }
/** Holds if this definition is a phi node for variable `v`. */
predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this)) }
predicate isPhiNode(StackVariable v) { exists(StandardSsa x | x.phi_node(v, this)) }
/** Gets the location of this definition. */
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
@@ -124,7 +127,7 @@ class SsaDefinition extends ControlFlowNodeBase {
/** Holds if `(this, v)` reaches the end of basic block `b`. */
predicate reachesEndOfBB(StackVariable v, BasicBlock b) {
exists(StandardSSA x | x.ssaDefinitionReachesEndOfBB(v, this, b))
exists(StandardSsa x | x.ssaDefinitionReachesEndOfBB(v, this, b))
}
/**
@@ -147,15 +150,4 @@ class SsaDefinition extends ControlFlowNodeBase {
Expr getAnUltimateDefiningValue(StackVariable v) {
result = this.getAnUltimateSsaDefinition(v).getDefiningValue(v)
}
/**
* DEPRECATED: this is the old name for `getAnUltimateDefiningValue`. The
* name was confusing as it seemed analogous to `getDefinition` rather than
* `getDefiningValue`. The SSA libraries for other languages use the name
* `getAnUltimateSsaDefinition` to refer to a predicate named
* `getAnUltimateSsaDefinition` in this class.
*/
deprecated Expr getAnUltimateDefinition(StackVariable v) {
result = this.getAnUltimateDefiningValue(v)
}
}

View File

@@ -114,10 +114,10 @@ private predicate live_at_exit_of_bb(StackVariable v, BasicBlock b) {
/** Common SSA logic for standard SSA and range-analysis SSA. */
cached
library class SSAHelper extends int {
library class SsaHelper extends int {
/* 0 = StandardSSA, 1 = RangeSSA */
cached
SSAHelper() { this in [0 .. 1] }
SsaHelper() { this in [0 .. 1] }
/**
* Override to insert a custom phi node for variable `v` at the start of
@@ -311,3 +311,6 @@ library class SSAHelper extends int {
ssa_use(v, result, _, _)
}
}
/** DEPRECATED: Alias for SsaHelper */
deprecated class SSAHelper = SsaHelper;

View File

@@ -80,7 +80,11 @@ abstract class StackVariableReachability extends string {
j > i and
sink = bb.getNode(j) and
this.isSink(sink, v) and
not exists(int k | this.isBarrier(bb.getNode(k), v) | k in [i + 1 .. j - 1])
not exists(int k, ControlFlowNode node |
node = bb.getNode(k) and this.isBarrier(pragma[only_bind_into](node), v)
|
k in [i + 1 .. j - 1]
)
)
or
not exists(int k | this.isBarrier(bb.getNode(k), v) | k > i) and

View File

@@ -447,26 +447,6 @@ private predicate skipInitializer(Initializer init) {
)
}
/**
* 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" in
* order to avoid recursion through forall.
*/
private predicate runtimeExprInStaticInitializer(Expr e) {
inStaticInitializer(e) and
if e instanceof AggregateLiteral
then runtimeExprInStaticInitializer(e.getAChild())
else not e.getFullyConverted().isConstant()
}
/** Holds if `e` is part of the initializer of a local static variable. */
private predicate inStaticInitializer(Expr e) {
exists(LocalVariable local |
local.isStatic() and
e.getParent+() = local.getInitializer()
)
}
/**
* Gets the `i`th child of `n` in control-flow order, where the `i`-indexes are
* contiguous, and the first index is 0.
@@ -1379,7 +1359,7 @@ private module Cached {
* true-successors and false-successors.
*/
cached
predicate qlCFGSuccessor(Node n1, Node n2) {
predicate qlCfgSuccessor(Node n1, Node n2) {
exists(Node memberNode, Pos memberPos |
subEdgeIncludingDestructors(any(Pos at | at.isAt()), n1, memberNode, memberPos) and
normalGroupMember(memberNode, memberPos, n2)
@@ -1388,23 +1368,32 @@ private module Cached {
conditionalSuccessor(n1, _, n2)
}
/** DEPRECATED: Alias for qlCfgSuccessor */
deprecated predicate qlCFGSuccessor = qlCfgSuccessor/2;
/**
* Holds if `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is true.
*/
cached
predicate qlCFGTrueSuccessor(Node n1, Node n2) {
predicate qlCfgTrueSuccessor(Node n1, Node n2) {
conditionalSuccessor(n1, true, n2) and
not conditionalSuccessor(n1, false, n2)
}
/** DEPRECATED: Alias for qlCfgTrueSuccessor */
deprecated predicate qlCFGTrueSuccessor = qlCfgTrueSuccessor/2;
/**
* Holds if `n2` is a control-flow node such that the control-flow
* edge `(n1, n2)` may be taken when `n1` is an expression that is false.
*/
cached
predicate qlCFGFalseSuccessor(Node n1, Node n2) {
predicate qlCfgFalseSuccessor(Node n1, Node n2) {
conditionalSuccessor(n1, false, n2) and
not conditionalSuccessor(n1, true, n2)
}
/** DEPRECATED: Alias for qlCfgFalseSuccessor */
deprecated predicate qlCFGFalseSuccessor = qlCfgFalseSuccessor/2;
}

View File

@@ -188,8 +188,8 @@ private predicate nonAnalyzableFunction(Function f) {
*/
private predicate impossibleFalseEdge(Expr condition, Node succ) {
conditionAlwaysTrue(condition) and
qlCFGFalseSuccessor(condition, succ) and
not qlCFGTrueSuccessor(condition, succ)
qlCfgFalseSuccessor(condition, succ) and
not qlCfgTrueSuccessor(condition, succ)
}
/**
@@ -197,8 +197,8 @@ private predicate impossibleFalseEdge(Expr condition, Node succ) {
*/
private predicate impossibleTrueEdge(Expr condition, Node succ) {
conditionAlwaysFalse(condition) and
qlCFGTrueSuccessor(condition, succ) and
not qlCFGFalseSuccessor(condition, succ)
qlCfgTrueSuccessor(condition, succ) and
not qlCfgFalseSuccessor(condition, succ)
}
/**
@@ -960,9 +960,9 @@ library class ConditionEvaluator extends ExprEvaluator {
ConditionEvaluator() { this = 0 }
override predicate interesting(Expr e) {
qlCFGFalseSuccessor(e, _)
qlCfgFalseSuccessor(e, _)
or
qlCFGTrueSuccessor(e, _)
qlCfgTrueSuccessor(e, _)
}
}

View File

@@ -20,10 +20,4 @@ import semmle.code.cpp.dataflow.DataFlow2
module TaintTracking {
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
private import semmle.code.cpp.dataflow.TaintTracking2
/**
* DEPRECATED: Use TaintTracking2::Configuration instead.
*/
deprecated class Configuration2 = TaintTracking2::Configuration;
}

View File

@@ -94,15 +94,19 @@ abstract class Configuration extends string {
predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis. This step is only applicable in `state1` and
* updates the flow state to `state2`.
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
@@ -345,9 +349,13 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
pragma[nomagic]
private predicate stateBarrier(NodeEx node, FlowState state, Configuration config) {
exists(Node n |
node.asNode() = n and
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
exists(BarrierGuard g |
config.isBarrierGuard(g, state) and
n = g.getAGuardedNode()
)
)
}
@@ -1110,8 +1118,8 @@ private module Stage2 {
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
bindingset[node1, state1, config]
bindingset[node2, state2, config]
@@ -1198,7 +1206,7 @@ private module Stage2 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -1706,18 +1714,31 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) {
private predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) {
Stage2::revFlow(node, state, config) and
(
sourceNode(node, state, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
additionalJumpStateStep(_, _, node, state, config) or
node instanceof ParamNodeEx or
node.asNode() instanceof OutNodeExt or
store(_, _, node, _, config) or
read(_, _, node, config) or
sourceNode(node, state, config)
or
jumpStep(_, node, config)
or
additionalJumpStep(_, node, config)
or
additionalJumpStateStep(_, _, node, state, config)
or
node instanceof ParamNodeEx
or
node.asNode() instanceof OutNodeExt
or
store(_, _, node, _, config)
or
read(_, _, node, config)
or
node instanceof FlowCheckNode
or
exists(FlowState s |
additionalLocalStateStep(_, s, node, state, config) and
s != state
)
)
}
@@ -1737,6 +1758,9 @@ private module LocalFlowBigStep {
or
exists(NodeEx next, FlowState s | Stage2::revFlow(next, s, config) |
additionalJumpStateStep(node, state, next, s, config)
or
additionalLocalStateStep(node, state, next, s, config) and
s != state
)
or
Stage2::revFlow(node, state, config) and
@@ -1770,42 +1794,40 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, Configuration config, LocalCallContext cc
NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t,
Configuration config, LocalCallContext cc
) {
not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](state1), pragma[only_bind_into](config)) and
localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
preservesValue = true and
t = node1.getDataFlowType() // irrelevant dummy value
t = node1.getDataFlowType() and // irrelevant dummy value
Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config))
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
additionalLocalFlowStepNodeCand2(node1, state, node2, state, config) and
preservesValue = false and
t = node2.getDataFlowType()
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](state2), pragma[only_bind_into](config))
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall())
or
exists(NodeEx mid |
localFlowStepPlus(node1, state1, mid, pragma[only_bind_into](state2), preservesValue, t,
localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t,
pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](state2), pragma[only_bind_into](config))
Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config))
)
or
exists(NodeEx mid, FlowState st |
localFlowStepPlus(node1, state1, mid, st, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, st, node2, state2, config) and
exists(NodeEx mid |
localFlowStepPlus(node1, state, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, state, node2, state, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
t = node2.getDataFlowType() and
Stage2::revFlow(node2, state2, pragma[only_bind_into](config))
t = node2.getDataFlowType()
)
)
}
@@ -1819,9 +1841,19 @@ private module LocalFlowBigStep {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, state2, preservesValue, apf.getType(), config,
callContext) and
localFlowExit(node2, state2, config)
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
isUnreachableInCallCached(node2.asNode(), call)
)
}
}
@@ -1879,8 +1911,8 @@ private module Stage3 {
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
private predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
@@ -1963,7 +1995,7 @@ private module Stage3 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -2693,9 +2725,8 @@ private module Stage4 {
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, _, config) and
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) {
result =
getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
node.getEnclosingCallable())
@@ -2791,7 +2822,7 @@ private module Stage4 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -4976,6 +5007,7 @@ private module FlowExploration {
)
}
pragma[nomagic]
private predicate revPartialPathStep(
PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
TRevSummaryCtx3 sc3, RevPartialAccessPath ap, Configuration config

View File

@@ -94,15 +94,19 @@ abstract class Configuration extends string {
predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis. This step is only applicable in `state1` and
* updates the flow state to `state2`.
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
@@ -345,9 +349,13 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
pragma[nomagic]
private predicate stateBarrier(NodeEx node, FlowState state, Configuration config) {
exists(Node n |
node.asNode() = n and
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
exists(BarrierGuard g |
config.isBarrierGuard(g, state) and
n = g.getAGuardedNode()
)
)
}
@@ -1110,8 +1118,8 @@ private module Stage2 {
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
bindingset[node1, state1, config]
bindingset[node2, state2, config]
@@ -1198,7 +1206,7 @@ private module Stage2 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -1706,18 +1714,31 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) {
private predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) {
Stage2::revFlow(node, state, config) and
(
sourceNode(node, state, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
additionalJumpStateStep(_, _, node, state, config) or
node instanceof ParamNodeEx or
node.asNode() instanceof OutNodeExt or
store(_, _, node, _, config) or
read(_, _, node, config) or
sourceNode(node, state, config)
or
jumpStep(_, node, config)
or
additionalJumpStep(_, node, config)
or
additionalJumpStateStep(_, _, node, state, config)
or
node instanceof ParamNodeEx
or
node.asNode() instanceof OutNodeExt
or
store(_, _, node, _, config)
or
read(_, _, node, config)
or
node instanceof FlowCheckNode
or
exists(FlowState s |
additionalLocalStateStep(_, s, node, state, config) and
s != state
)
)
}
@@ -1737,6 +1758,9 @@ private module LocalFlowBigStep {
or
exists(NodeEx next, FlowState s | Stage2::revFlow(next, s, config) |
additionalJumpStateStep(node, state, next, s, config)
or
additionalLocalStateStep(node, state, next, s, config) and
s != state
)
or
Stage2::revFlow(node, state, config) and
@@ -1770,42 +1794,40 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, Configuration config, LocalCallContext cc
NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t,
Configuration config, LocalCallContext cc
) {
not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](state1), pragma[only_bind_into](config)) and
localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
preservesValue = true and
t = node1.getDataFlowType() // irrelevant dummy value
t = node1.getDataFlowType() and // irrelevant dummy value
Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config))
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
additionalLocalFlowStepNodeCand2(node1, state, node2, state, config) and
preservesValue = false and
t = node2.getDataFlowType()
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](state2), pragma[only_bind_into](config))
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall())
or
exists(NodeEx mid |
localFlowStepPlus(node1, state1, mid, pragma[only_bind_into](state2), preservesValue, t,
localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t,
pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](state2), pragma[only_bind_into](config))
Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config))
)
or
exists(NodeEx mid, FlowState st |
localFlowStepPlus(node1, state1, mid, st, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, st, node2, state2, config) and
exists(NodeEx mid |
localFlowStepPlus(node1, state, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, state, node2, state, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
t = node2.getDataFlowType() and
Stage2::revFlow(node2, state2, pragma[only_bind_into](config))
t = node2.getDataFlowType()
)
)
}
@@ -1819,9 +1841,19 @@ private module LocalFlowBigStep {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, state2, preservesValue, apf.getType(), config,
callContext) and
localFlowExit(node2, state2, config)
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
isUnreachableInCallCached(node2.asNode(), call)
)
}
}
@@ -1879,8 +1911,8 @@ private module Stage3 {
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
private predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
@@ -1963,7 +1995,7 @@ private module Stage3 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -2693,9 +2725,8 @@ private module Stage4 {
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, _, config) and
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) {
result =
getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
node.getEnclosingCallable())
@@ -2791,7 +2822,7 @@ private module Stage4 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -4976,6 +5007,7 @@ private module FlowExploration {
)
}
pragma[nomagic]
private predicate revPartialPathStep(
PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
TRevSummaryCtx3 sc3, RevPartialAccessPath ap, Configuration config

View File

@@ -94,15 +94,19 @@ abstract class Configuration extends string {
predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis. This step is only applicable in `state1` and
* updates the flow state to `state2`.
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
@@ -345,9 +349,13 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
pragma[nomagic]
private predicate stateBarrier(NodeEx node, FlowState state, Configuration config) {
exists(Node n |
node.asNode() = n and
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
exists(BarrierGuard g |
config.isBarrierGuard(g, state) and
n = g.getAGuardedNode()
)
)
}
@@ -1110,8 +1118,8 @@ private module Stage2 {
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
bindingset[node1, state1, config]
bindingset[node2, state2, config]
@@ -1198,7 +1206,7 @@ private module Stage2 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -1706,18 +1714,31 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) {
private predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) {
Stage2::revFlow(node, state, config) and
(
sourceNode(node, state, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
additionalJumpStateStep(_, _, node, state, config) or
node instanceof ParamNodeEx or
node.asNode() instanceof OutNodeExt or
store(_, _, node, _, config) or
read(_, _, node, config) or
sourceNode(node, state, config)
or
jumpStep(_, node, config)
or
additionalJumpStep(_, node, config)
or
additionalJumpStateStep(_, _, node, state, config)
or
node instanceof ParamNodeEx
or
node.asNode() instanceof OutNodeExt
or
store(_, _, node, _, config)
or
read(_, _, node, config)
or
node instanceof FlowCheckNode
or
exists(FlowState s |
additionalLocalStateStep(_, s, node, state, config) and
s != state
)
)
}
@@ -1737,6 +1758,9 @@ private module LocalFlowBigStep {
or
exists(NodeEx next, FlowState s | Stage2::revFlow(next, s, config) |
additionalJumpStateStep(node, state, next, s, config)
or
additionalLocalStateStep(node, state, next, s, config) and
s != state
)
or
Stage2::revFlow(node, state, config) and
@@ -1770,42 +1794,40 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, Configuration config, LocalCallContext cc
NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t,
Configuration config, LocalCallContext cc
) {
not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](state1), pragma[only_bind_into](config)) and
localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
preservesValue = true and
t = node1.getDataFlowType() // irrelevant dummy value
t = node1.getDataFlowType() and // irrelevant dummy value
Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config))
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
additionalLocalFlowStepNodeCand2(node1, state, node2, state, config) and
preservesValue = false and
t = node2.getDataFlowType()
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](state2), pragma[only_bind_into](config))
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall())
or
exists(NodeEx mid |
localFlowStepPlus(node1, state1, mid, pragma[only_bind_into](state2), preservesValue, t,
localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t,
pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](state2), pragma[only_bind_into](config))
Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config))
)
or
exists(NodeEx mid, FlowState st |
localFlowStepPlus(node1, state1, mid, st, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, st, node2, state2, config) and
exists(NodeEx mid |
localFlowStepPlus(node1, state, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, state, node2, state, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
t = node2.getDataFlowType() and
Stage2::revFlow(node2, state2, pragma[only_bind_into](config))
t = node2.getDataFlowType()
)
)
}
@@ -1819,9 +1841,19 @@ private module LocalFlowBigStep {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, state2, preservesValue, apf.getType(), config,
callContext) and
localFlowExit(node2, state2, config)
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
isUnreachableInCallCached(node2.asNode(), call)
)
}
}
@@ -1879,8 +1911,8 @@ private module Stage3 {
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
private predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
@@ -1963,7 +1995,7 @@ private module Stage3 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -2693,9 +2725,8 @@ private module Stage4 {
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, _, config) and
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) {
result =
getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
node.getEnclosingCallable())
@@ -2791,7 +2822,7 @@ private module Stage4 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -4976,6 +5007,7 @@ private module FlowExploration {
)
}
pragma[nomagic]
private predicate revPartialPathStep(
PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
TRevSummaryCtx3 sc3, RevPartialAccessPath ap, Configuration config

View File

@@ -94,15 +94,19 @@ abstract class Configuration extends string {
predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis. This step is only applicable in `state1` and
* updates the flow state to `state2`.
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
@@ -345,9 +349,13 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
pragma[nomagic]
private predicate stateBarrier(NodeEx node, FlowState state, Configuration config) {
exists(Node n |
node.asNode() = n and
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
exists(BarrierGuard g |
config.isBarrierGuard(g, state) and
n = g.getAGuardedNode()
)
)
}
@@ -1110,8 +1118,8 @@ private module Stage2 {
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
bindingset[node1, state1, config]
bindingset[node2, state2, config]
@@ -1198,7 +1206,7 @@ private module Stage2 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -1706,18 +1714,31 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) {
private predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) {
Stage2::revFlow(node, state, config) and
(
sourceNode(node, state, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
additionalJumpStateStep(_, _, node, state, config) or
node instanceof ParamNodeEx or
node.asNode() instanceof OutNodeExt or
store(_, _, node, _, config) or
read(_, _, node, config) or
sourceNode(node, state, config)
or
jumpStep(_, node, config)
or
additionalJumpStep(_, node, config)
or
additionalJumpStateStep(_, _, node, state, config)
or
node instanceof ParamNodeEx
or
node.asNode() instanceof OutNodeExt
or
store(_, _, node, _, config)
or
read(_, _, node, config)
or
node instanceof FlowCheckNode
or
exists(FlowState s |
additionalLocalStateStep(_, s, node, state, config) and
s != state
)
)
}
@@ -1737,6 +1758,9 @@ private module LocalFlowBigStep {
or
exists(NodeEx next, FlowState s | Stage2::revFlow(next, s, config) |
additionalJumpStateStep(node, state, next, s, config)
or
additionalLocalStateStep(node, state, next, s, config) and
s != state
)
or
Stage2::revFlow(node, state, config) and
@@ -1770,42 +1794,40 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, Configuration config, LocalCallContext cc
NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t,
Configuration config, LocalCallContext cc
) {
not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](state1), pragma[only_bind_into](config)) and
localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
preservesValue = true and
t = node1.getDataFlowType() // irrelevant dummy value
t = node1.getDataFlowType() and // irrelevant dummy value
Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config))
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
additionalLocalFlowStepNodeCand2(node1, state, node2, state, config) and
preservesValue = false and
t = node2.getDataFlowType()
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](state2), pragma[only_bind_into](config))
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall())
or
exists(NodeEx mid |
localFlowStepPlus(node1, state1, mid, pragma[only_bind_into](state2), preservesValue, t,
localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t,
pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](state2), pragma[only_bind_into](config))
Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config))
)
or
exists(NodeEx mid, FlowState st |
localFlowStepPlus(node1, state1, mid, st, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, st, node2, state2, config) and
exists(NodeEx mid |
localFlowStepPlus(node1, state, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, state, node2, state, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
t = node2.getDataFlowType() and
Stage2::revFlow(node2, state2, pragma[only_bind_into](config))
t = node2.getDataFlowType()
)
)
}
@@ -1819,9 +1841,19 @@ private module LocalFlowBigStep {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, state2, preservesValue, apf.getType(), config,
callContext) and
localFlowExit(node2, state2, config)
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
isUnreachableInCallCached(node2.asNode(), call)
)
}
}
@@ -1879,8 +1911,8 @@ private module Stage3 {
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
private predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
@@ -1963,7 +1995,7 @@ private module Stage3 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -2693,9 +2725,8 @@ private module Stage4 {
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, _, config) and
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) {
result =
getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
node.getEnclosingCallable())
@@ -2791,7 +2822,7 @@ private module Stage4 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -4976,6 +5007,7 @@ private module FlowExploration {
)
}
pragma[nomagic]
private predicate revPartialPathStep(
PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
TRevSummaryCtx3 sc3, RevPartialAccessPath ap, Configuration config

View File

@@ -94,15 +94,19 @@ abstract class Configuration extends string {
predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis.
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
*/
predicate isAdditionalFlowStep(Node node1, Node node2) { none() }
/**
* Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis. This step is only applicable in `state1` and
* updates the flow state to `state2`.
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) {
none()
@@ -345,9 +349,13 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
pragma[nomagic]
private predicate stateBarrier(NodeEx node, FlowState state, Configuration config) {
exists(Node n |
node.asNode() = n and
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
exists(BarrierGuard g |
config.isBarrierGuard(g, state) and
n = g.getAGuardedNode()
)
)
}
@@ -1110,8 +1118,8 @@ private module Stage2 {
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
bindingset[node1, state1, config]
bindingset[node2, state2, config]
@@ -1198,7 +1206,7 @@ private module Stage2 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -1706,18 +1714,31 @@ private module LocalFlowBigStep {
* Holds if `node` can be the first node in a maximal subsequence of local
* flow steps in a dataflow path.
*/
predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) {
private predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) {
Stage2::revFlow(node, state, config) and
(
sourceNode(node, state, config) or
jumpStep(_, node, config) or
additionalJumpStep(_, node, config) or
additionalJumpStateStep(_, _, node, state, config) or
node instanceof ParamNodeEx or
node.asNode() instanceof OutNodeExt or
store(_, _, node, _, config) or
read(_, _, node, config) or
sourceNode(node, state, config)
or
jumpStep(_, node, config)
or
additionalJumpStep(_, node, config)
or
additionalJumpStateStep(_, _, node, state, config)
or
node instanceof ParamNodeEx
or
node.asNode() instanceof OutNodeExt
or
store(_, _, node, _, config)
or
read(_, _, node, config)
or
node instanceof FlowCheckNode
or
exists(FlowState s |
additionalLocalStateStep(_, s, node, state, config) and
s != state
)
)
}
@@ -1737,6 +1758,9 @@ private module LocalFlowBigStep {
or
exists(NodeEx next, FlowState s | Stage2::revFlow(next, s, config) |
additionalJumpStateStep(node, state, next, s, config)
or
additionalLocalStateStep(node, state, next, s, config) and
s != state
)
or
Stage2::revFlow(node, state, config) and
@@ -1770,42 +1794,40 @@ private module LocalFlowBigStep {
*/
pragma[nomagic]
private predicate localFlowStepPlus(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
DataFlowType t, Configuration config, LocalCallContext cc
NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t,
Configuration config, LocalCallContext cc
) {
not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
(
localFlowEntry(node1, pragma[only_bind_into](state1), pragma[only_bind_into](config)) and
localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](config)) and
(
localFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
preservesValue = true and
t = node1.getDataFlowType() // irrelevant dummy value
t = node1.getDataFlowType() and // irrelevant dummy value
Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config))
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
additionalLocalFlowStepNodeCand2(node1, state, node2, state, config) and
preservesValue = false and
t = node2.getDataFlowType()
) and
node1 != node2 and
cc.relevantFor(node1.getEnclosingCallable()) and
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and
Stage2::revFlow(node2, pragma[only_bind_into](state2), pragma[only_bind_into](config))
not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall())
or
exists(NodeEx mid |
localFlowStepPlus(node1, state1, mid, pragma[only_bind_into](state2), preservesValue, t,
localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t,
pragma[only_bind_into](config), cc) and
localFlowStepNodeCand1(mid, node2, config) and
not mid instanceof FlowCheckNode and
Stage2::revFlow(node2, pragma[only_bind_into](state2), pragma[only_bind_into](config))
Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config))
)
or
exists(NodeEx mid, FlowState st |
localFlowStepPlus(node1, state1, mid, st, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, st, node2, state2, config) and
exists(NodeEx mid |
localFlowStepPlus(node1, state, mid, _, _, pragma[only_bind_into](config), cc) and
additionalLocalFlowStepNodeCand2(mid, state, node2, state, config) and
not mid instanceof FlowCheckNode and
preservesValue = false and
t = node2.getDataFlowType() and
Stage2::revFlow(node2, state2, pragma[only_bind_into](config))
t = node2.getDataFlowType()
)
)
}
@@ -1819,9 +1841,19 @@ private module LocalFlowBigStep {
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
AccessPathFrontNil apf, Configuration config, LocalCallContext callContext
) {
localFlowStepPlus(node1, state1, node2, state2, preservesValue, apf.getType(), config,
callContext) and
localFlowExit(node2, state2, config)
localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and
localFlowExit(node2, state1, config) and
state1 = state2
or
additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and
state1 != state2 and
preservesValue = false and
apf = TFrontNil(node2.getDataFlowType()) and
callContext.relevantFor(node1.getEnclosingCallable()) and
not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() |
isUnreachableInCallCached(node1.asNode(), call) or
isUnreachableInCallCached(node2.asNode(), call)
)
}
}
@@ -1879,8 +1911,8 @@ private module Stage3 {
bindingset[call, c, innercc]
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
private predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
@@ -1963,7 +1995,7 @@ private module Stage3 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -2693,9 +2725,8 @@ private module Stage4 {
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
bindingset[node, cc, config]
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
localFlowEntry(node, _, config) and
bindingset[node, cc]
private LocalCc getLocalCc(NodeEx node, Cc cc) {
result =
getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
node.getEnclosingCallable())
@@ -2791,7 +2822,7 @@ private module Stage4 {
or
exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
fwdFlow(mid, state0, cc, argAp, ap0, config) and
localCc = getLocalCc(mid, cc, config)
localCc = getLocalCc(mid, cc)
|
localStep(mid, state0, node, state, true, _, config, localCc) and
ap = ap0
@@ -4976,6 +5007,7 @@ private module FlowExploration {
)
}
pragma[nomagic]
private predicate revPartialPathStep(
PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2,
TRevSummaryCtx3 sc3, RevPartialAccessPath ap, Configuration config

View File

@@ -113,10 +113,6 @@ private module PartialDefinitions {
abstract class PartialDefinition extends Expr {
ControlFlowNode node;
abstract deprecated predicate partiallyDefines(Variable v);
abstract deprecated predicate partiallyDefinesThis(ThisExpr e);
/**
* Gets the subBasicBlock where this `PartialDefinition` is defined.
*/
@@ -189,10 +185,6 @@ private module PartialDefinitions {
)
}
deprecated override predicate partiallyDefines(Variable v) { v = collection }
deprecated override predicate partiallyDefinesThis(ThisExpr e) { none() }
override predicate definesExpressions(Expr inner, Expr outer) {
inner = innerDefinedExpr and
outer = this
@@ -217,12 +209,6 @@ private module PartialDefinitions {
VariablePartialDefinition() { innerDefinedExpr = getInnerDefinedExpr(this, node) }
deprecated override predicate partiallyDefines(Variable v) {
innerDefinedExpr = v.getAnAccess()
}
deprecated override predicate partiallyDefinesThis(ThisExpr e) { innerDefinedExpr = e }
/**
* Holds if this partial definition may modify `inner` (or what it points
* to) through `outer`. These expressions will never be `Conversion`s.

View File

@@ -64,13 +64,30 @@ abstract class Configuration extends DataFlow::Configuration {
override predicate isSource(DataFlow::Node source) { none() }
/**
* Holds if `sink` is a relevant taint sink.
* Holds if `source` is a relevant taint source with the given initial
* `state`.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
/**
* Holds if `sink` is a relevant taint sink
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
override predicate isSink(DataFlow::Node sink) { none() }
/**
* Holds if `sink` is a relevant taint sink accepting `state`.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
/** Holds if the node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
@@ -79,6 +96,16 @@ abstract class Configuration extends DataFlow::Configuration {
defaultTaintSanitizer(node)
}
/**
* Holds if the node `node` is a taint sanitizer when the flow state is
* `state`.
*/
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
this.isSanitizer(node, state)
}
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
@@ -97,8 +124,17 @@ abstract class Configuration extends DataFlow::Configuration {
}
/**
* Holds if the additional taint propagation step from `node1` to `node2`
* must be taken into account in the analysis.
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
this.isSanitizerGuard(guard, state)
}
/**
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
*/
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
@@ -107,6 +143,24 @@ abstract class Configuration extends DataFlow::Configuration {
defaultAdditionalTaintStep(node1, node2)
}
/**
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalTaintStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
none()
}
final override predicate isAdditionalFlowStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
this.isAdditionalTaintStep(node1, state1, node2, state2)
}
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
(this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
defaultImplicitTaintRead(node, c)

View File

@@ -64,13 +64,30 @@ abstract class Configuration extends DataFlow::Configuration {
override predicate isSource(DataFlow::Node source) { none() }
/**
* Holds if `sink` is a relevant taint sink.
* Holds if `source` is a relevant taint source with the given initial
* `state`.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { none() }
/**
* Holds if `sink` is a relevant taint sink
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
override predicate isSink(DataFlow::Node sink) { none() }
/**
* Holds if `sink` is a relevant taint sink accepting `state`.
*
* The smaller this predicate is, the faster `hasFlow()` will converge.
*/
// overridden to provide taint-tracking specific qldoc
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { none() }
/** Holds if the node `node` is a taint sanitizer. */
predicate isSanitizer(DataFlow::Node node) { none() }
@@ -79,6 +96,16 @@ abstract class Configuration extends DataFlow::Configuration {
defaultTaintSanitizer(node)
}
/**
* Holds if the node `node` is a taint sanitizer when the flow state is
* `state`.
*/
predicate isSanitizer(DataFlow::Node node, DataFlow::FlowState state) { none() }
final override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
this.isSanitizer(node, state)
}
/** Holds if taint propagation into `node` is prohibited. */
predicate isSanitizerIn(DataFlow::Node node) { none() }
@@ -97,8 +124,17 @@ abstract class Configuration extends DataFlow::Configuration {
}
/**
* Holds if the additional taint propagation step from `node1` to `node2`
* must be taken into account in the analysis.
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
this.isSanitizerGuard(guard, state)
}
/**
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
*/
predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
@@ -107,6 +143,24 @@ abstract class Configuration extends DataFlow::Configuration {
defaultAdditionalTaintStep(node1, node2)
}
/**
* Holds if taint may propagate from `node1` to `node2` in addition to the normal data-flow and taint steps.
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalTaintStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
none()
}
final override predicate isAdditionalFlowStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
this.isAdditionalTaintStep(node1, state1, node2, state2)
}
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::Content c) {
(this.isSink(node) or this.isAdditionalTaintStep(node, _)) and
defaultImplicitTaintRead(node, c)

View File

@@ -226,13 +226,6 @@ class AssignPointerSubExpr extends AssignOperation, @assignpsubexpr {
* ```
*/
class ConditionDeclExpr extends Expr, @condition_decl {
/**
* DEPRECATED: Use `getVariableAccess()` or `getInitializingExpr()` instead.
*
* Gets the access using the condition for this declaration.
*/
deprecated Expr getExpr() { result = this.getChild(0) }
override string getAPrimaryQlClass() { result = "ConditionDeclExpr" }
/**

View File

@@ -118,11 +118,6 @@ class BuiltInNoOp extends BuiltInOperation, @noopexpr {
override string getAPrimaryQlClass() { result = "BuiltInNoOp" }
}
/**
* DEPRECATED: Use `BuiltInOperationBuiltInOffsetOf` instead.
*/
deprecated class BuiltInOperationOffsetOf = BuiltInOperationBuiltInOffsetOf;
/**
* A C/C++ `__builtin_offsetof` built-in operation (used by some implementations
* of `offsetof`). The operation retains its semantics even in the presence
@@ -465,11 +460,6 @@ class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr {
override string getAPrimaryQlClass() { result = "BuiltInOperationIsUnion" }
}
/**
* DEPRECATED: Use `BuiltInOperationBuiltInTypesCompatibleP` instead.
*/
deprecated class BuiltInOperationBuiltInTypes = BuiltInOperationBuiltInTypesCompatibleP;
/**
* A C++ `__builtin_types_compatible_p` built-in operation (used by some
* implementations of the `<type_traits>` header).

View File

@@ -666,13 +666,6 @@ class TypeidOperator extends Expr, @type_id {
*/
Type getResultType() { typeid_bind(underlyingElement(this), unresolveElement(result)) }
/**
* DEPRECATED: Use `getResultType()` instead.
*
* Gets the type that is returned by this typeid expression.
*/
deprecated Type getSpecifiedType() { result = this.getResultType() }
override string getAPrimaryQlClass() { result = "TypeidOperator" }
/**
@@ -731,13 +724,6 @@ class SizeofExprOperator extends SizeofOperator {
/** Gets the contained expression. */
Expr getExprOperand() { result = this.getChild(0) }
/**
* DEPRECATED: Use `getExprOperand()` instead
*
* Gets the contained expression.
*/
deprecated Expr getExpr() { result = this.getExprOperand() }
override string toString() { result = "sizeof(<expr>)" }
override predicate mayBeImpure() { this.getExprOperand().mayBeImpure() }
@@ -759,13 +745,6 @@ class SizeofTypeOperator extends SizeofOperator {
/** Gets the contained type. */
Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
/**
* DEPRECATED: Use `getTypeOperand()` instead
*
* Gets the contained type.
*/
deprecated Type getSpecifiedType() { result = this.getTypeOperand() }
override string toString() { result = "sizeof(" + this.getTypeOperand().getName() + ")" }
override predicate mayBeImpure() { none() }
@@ -794,11 +773,6 @@ class AlignofExprOperator extends AlignofOperator {
*/
Expr getExprOperand() { result = this.getChild(0) }
/**
* DEPRECATED: Use `getExprOperand()` instead.
*/
deprecated Expr getExpr() { result = this.getExprOperand() }
override string toString() { result = "alignof(<expr>)" }
}
@@ -814,11 +788,6 @@ class AlignofTypeOperator extends AlignofOperator {
/** Gets the contained type. */
Type getTypeOperand() { sizeof_bind(underlyingElement(this), unresolveElement(result)) }
/**
* DEPRECATED: Use `getTypeOperand()` instead.
*/
deprecated Type getSpecifiedType() { result = this.getTypeOperand() }
override string toString() { result = "alignof(" + this.getTypeOperand().getName() + ")" }
}

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