Compare commits

..

1 Commits

Author SHA1 Message Date
Mariusz Kliber
fc7736c324 Empty commit 2023-03-23 15:39:30 +00:00
5569 changed files with 213971 additions and 407240 deletions

View File

@@ -1,9 +1,3 @@
common --enable_platform_specific_config
build --repo_env=CC=clang --repo_env=CXX=clang++
build:linux --cxxopt=-std=c++20
build:macos --cxxopt=-std=c++20 --cpu=darwin_x86_64
build:windows --cxxopt=/std:c++20 --cxxopt=/Zc:preprocessor
build --repo_env=CC=clang --repo_env=CXX=clang++ --cxxopt="-std=c++17"
try-import %workspace%/local.bazelrc

View File

@@ -1 +1 @@
6.1.2
5.0.0

View File

@@ -1,6 +1,6 @@
{
"extensions": [
"rust-lang.rust-analyzer",
"rust-lang.rust",
"bungcip.better-toml",
"github.vscode-codeql",
"hbenl.vscode-test-explorer",

3
.github/labeler.yml vendored
View File

@@ -11,7 +11,7 @@ Go:
- change-notes/**/*go.*
Java:
- any: [ 'java/**/*', '!java/kotlin-extractor/**/*', '!java/ql/test/kotlin/**/*' ]
- any: [ 'java/**/*', '!java/kotlin-extractor/**/*', '!java/kotlin-explorer/**/*', '!java/ql/test/kotlin/**/*' ]
- change-notes/**/*java.*
JS:
@@ -20,6 +20,7 @@ JS:
Kotlin:
- java/kotlin-extractor/**/*
- java/kotlin-explorer/**/*
- java/ql/test/kotlin/**/*
Python:

View File

@@ -0,0 +1,102 @@
name: "ATM - Check query suite"
env:
QUERY_PACK: javascript/ql/experimental/adaptivethreatmodeling/src
QUERY_SUITE: codeql-suites/javascript-atm-code-scanning.qls
on:
pull_request:
paths:
- ".github/workflows/atm-check-query-suite.yml"
- "javascript/ql/experimental/adaptivethreatmodeling/**"
workflow_dispatch:
jobs:
atm-check-query-suite:
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
- name: Setup CodeQL
uses: ./.github/actions/fetch-codeql
with:
channel: release
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: atm-suite
- name: Install ATM model
run: |
set -exu
# Install dependencies of ATM query pack, i.e. the ATM model
codeql pack install "${QUERY_PACK}"
# Retrieve model checksum
model_checksum=$(codeql resolve extensions "${QUERY_PACK}/${QUERY_SUITE}" | jq -r '.models[0].checksum')
# Trust the model so that we can use it in the ATM boosted queries
mkdir -p "$HOME/.config/codeql"
echo "--insecurely-execute-ml-model-checksums ${model_checksum}" >> "$HOME/.config/codeql/config"
- name: Create test DB
run: |
DB_PATH="${RUNNER_TEMP}/db"
echo "DB_PATH=${DB_PATH}" >> "${GITHUB_ENV}"
codeql database create "${DB_PATH}" --source-root config/atm --language javascript
- name: Run ATM query suite
run: |
SARIF_PATH="${RUNNER_TEMP}/sarif.json"
echo "SARIF_PATH=${SARIF_PATH}" >> "${GITHUB_ENV}"
codeql database analyze \
--threads=0 \
--ram 50000 \
--format sarif-latest \
--output "${SARIF_PATH}" \
--sarif-group-rules-by-pack \
-vv \
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
-- \
"${DB_PATH}" \
"${QUERY_PACK}/${QUERY_SUITE}"
- name: Upload SARIF
uses: actions/upload-artifact@v3
with:
name: javascript-ml-powered-queries.sarif
path: "${{ env.SARIF_PATH }}"
retention-days: 5
- name: Check results
run: |
# We should run at least the ML-powered queries in `expected_rules`.
expected_rules="js/ml-powered/nosql-injection js/ml-powered/path-injection js/ml-powered/sql-injection js/ml-powered/xss"
for rule in ${expected_rules}; do
found_rule=$(jq --arg rule "${rule}" '[.runs[0].tool.extensions[].rules | select(. != null) |
flatten | .[].id] | any(. == $rule)' "${SARIF_PATH}")
if [[ "${found_rule}" != "true" ]]; then
echo "Expected SARIF output to contain rule '${rule}', but found no such rule."
exit 1
else
echo "Found rule '${rule}'."
fi
done
# We should have at least one alert from an ML-powered query.
num_alerts=$(jq '[.runs[0].results[] |
select(.properties.score != null and (.rule.id | startswith("js/ml-powered/")))] | length' \
"${SARIF_PATH}")
if [[ "${num_alerts}" -eq 0 ]]; then
echo "Expected to find at least one alert from an ML-powered query but found ${num_alerts}."
exit 1
else
echo "Found ${num_alerts} alerts from ML-powered queries.";
fi

View File

@@ -0,0 +1,12 @@
name: ATM Model Integration Tests
on:
workflow_dispatch:
jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- name: foo
run: echo "Hello world"

View File

@@ -11,6 +11,7 @@ on:
- "*/ql/lib/**/*.yml"
- "!**/experimental/**"
- "!ql/**"
- "!swift/**"
- ".github/workflows/check-change-note.yml"
jobs:
@@ -26,9 +27,9 @@ jobs:
run: |
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq 'any(.[].filename ; test("/change-notes/.*[.]md$"))' |
grep true -c
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md', 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text, or released/x.y.z.md for released change-notes
- name: Fail if the change note filename doesn't match the expected format. The file name must be of the form 'YYYY-MM-DD.md' or 'YYYY-MM-DD-{title}.md', where '{title}' is arbitrary text.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))] | all(test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$") or test("/change-notes/released/[0-9]*[.][0-9]*[.][0-9]*[.]md$"))' |
gh api 'repos/${{github.repository}}/pulls/${{github.event.number}}/files' --paginate --jq '[.[].filename | select(test("/change-notes/.*[.]md$"))] | all(test("/change-notes/[0-9]{4}-[0-9]{2}-[0-9]{2}.*[.]md$"))' |
grep true -c

View File

@@ -1,29 +0,0 @@
name: "Check implicit this warnings"
on:
workflow_dispatch:
pull_request:
paths:
- "**qlpack.yml"
branches:
- main
- "rc/*"
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check that implicit this warnings is enabled for all packs
shell: bash
run: |
EXIT_CODE=0
packs="$(find . -iname 'qlpack.yml')"
for pack_file in ${packs}; do
option="$(yq '.warnOnImplicitThis' ${pack_file})"
if [ "${option}" != "true" ]; then
echo "::error file=${pack_file}::warnOnImplicitThis property must be set to 'true' for pack ${pack_file}"
EXIT_CODE=1
fi
done
exit "${EXIT_CODE}"

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
- uses: actions/stale@v7
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open 14 days with no activity. Comment or remove the `Stale` label in order to avoid having this issue closed in 7 days.'

View File

@@ -10,7 +10,6 @@ on:
- "*/ql/src/**/*.qll"
- "*/ql/lib/**/*.ql"
- "*/ql/lib/**/*.qll"
- "*/ql/lib/ext/**/*.yml"
- "misc/scripts/library-coverage/*.py"
# input data files
- "*/documentation/library-coverage/cwe-sink.csv"

View File

@@ -1,50 +0,0 @@
# Fast-forwards the branch specified in BRANCH_NAME
# to the github.ref/sha that this workflow is run on.
# Used as part of the release process, to ensure
# external query writers can always access a branch of github/codeql
# that is compatible with the latest stable release.
name: Fast-forward tracking branch for selected CodeQL version
on:
workflow_dispatch:
jobs:
fast-forward:
name: Fast-forward tracking branch for selected CodeQL version
runs-on: ubuntu-latest
if: github.repository == 'github/codeql'
permissions:
contents: write
env:
BRANCH_NAME: 'lgtm.com'
steps:
- name: Validate chosen branch
if: ${{ !startsWith(github.ref_name, 'codeql-cli-') }}
shell: bash
run: |
echo "::error ::The $BRANCH_NAME tracking branch should only be fast-forwarded to the tip of a codeql-cli-* branch, got $GITHUB_REF_NAME instead."
exit 1
- name: Checkout
uses: actions/checkout@v3
- name: Git config
shell: bash
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Fetch
shell: bash
run: |
set -x
echo "Fetching $BRANCH_NAME"
# Explicitly unshallow and fetch to ensure the remote ref is available.
git fetch --unshallow origin "$BRANCH_NAME"
git checkout -b "$BRANCH_NAME" "origin/$BRANCH_NAME"
- name: Fast-forward
shell: bash
run: |
echo "Fast-forwarding $BRANCH_NAME to ${GITHUB_REF}@${GITHUB_SHA}"
git merge --ff-only "$GITHUB_SHA"
git push origin "$BRANCH_NAME"

View File

@@ -15,7 +15,7 @@ jobs:
- name: Set up Go 1.20
uses: actions/setup-go@v4
with:
go-version: '1.20'
go-version: 1.20.0
id: go
- name: Check out code
@@ -50,7 +50,7 @@ jobs:
- name: Set up Go 1.20
uses: actions/setup-go@v4
with:
go-version: '1.20'
go-version: 1.20.0
id: go
- name: Check out code

View File

@@ -23,7 +23,7 @@ jobs:
- name: Set up Go 1.20
uses: actions/setup-go@v4
with:
go-version: '1.20'
go-version: 1.20.0
id: go
- name: Check out code

View File

@@ -32,7 +32,7 @@ jobs:
path: |
ql/extractor-pack/
ql/target/release/buramu
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ql/**/*.rs') }}
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
- name: Cache cargo
if: steps.cache-extractor.outputs.cache-hit != 'true'
uses: actions/cache@v3

View File

@@ -50,7 +50,7 @@ jobs:
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
- name: Install cargo-cross
if: runner.os == 'Linux'
run: cargo install cross --version 0.2.5
run: cargo install cross --version 0.2.1
- uses: ./.github/actions/os-version
id: os_version
- name: Cache entire extractor
@@ -58,10 +58,12 @@ jobs:
id: cache-extractor
with:
path: |
ruby/extractor/target/release/codeql-extractor-ruby
ruby/extractor/target/release/codeql-extractor-ruby.exe
ruby/extractor/target/release/autobuilder
ruby/extractor/target/release/autobuilder.exe
ruby/extractor/target/release/extractor
ruby/extractor/target/release/extractor.exe
ruby/extractor/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}-${{ hashFiles('shared/tree-sitter-extractor') }}-${{ hashFiles('ruby/extractor/**/*.rs') }}
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-ruby-extractor-${{ hashFiles('ruby/extractor/rust-toolchain.toml', 'ruby/extractor/Cargo.lock') }}--${{ hashFiles('ruby/extractor/**/*.rs') }}
- uses: actions/cache@v3
if: steps.cache-extractor.outputs.cache-hit != 'true'
with:
@@ -83,16 +85,13 @@ jobs:
# This ensures we don't depend on glibc > 2.17.
- name: Release build (linux)
if: steps.cache-extractor.outputs.cache-hit != 'true' && runner.os == 'Linux'
run: |
cd extractor
cross build --release
mv target/x86_64-unknown-linux-gnu/release/codeql-extractor-ruby target/release/
run: cd extractor && cross build --release
- name: Release build (windows and macos)
if: steps.cache-extractor.outputs.cache-hit != 'true' && runner.os != 'Linux'
run: cd extractor && cargo build --release
- name: Generate dbscheme
if: ${{ matrix.os == 'ubuntu-latest' && steps.cache-extractor.outputs.cache-hit != 'true'}}
run: extractor/target/release/codeql-extractor-ruby generate --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
run: extractor/target/release/generator --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
- uses: actions/upload-artifact@v3
if: ${{ matrix.os == 'ubuntu-latest' }}
with:
@@ -107,8 +106,10 @@ jobs:
with:
name: extractor-${{ matrix.os }}
path: |
ruby/extractor/target/release/codeql-extractor-ruby
ruby/extractor/target/release/codeql-extractor-ruby.exe
ruby/extractor/target/release/autobuilder
ruby/extractor/target/release/autobuilder.exe
ruby/extractor/target/release/extractor
ruby/extractor/target/release/extractor.exe
retention-days: 1
compile-queries:
runs-on: ubuntu-latest-xl
@@ -166,10 +167,13 @@ jobs:
mkdir -p ruby
cp -r codeql-extractor.yml tools ql/lib/ruby.dbscheme.stats ruby/
mkdir -p ruby/tools/{linux64,osx64,win64}
cp linux64/codeql-extractor-ruby ruby/tools/linux64/extractor
cp osx64/codeql-extractor-ruby ruby/tools/osx64/extractor
cp win64/codeql-extractor-ruby.exe ruby/tools/win64/extractor.exe
chmod +x ruby/tools/{linux64,osx64}/extractor
cp linux64/autobuilder ruby/tools/linux64/autobuilder
cp osx64/autobuilder ruby/tools/osx64/autobuilder
cp win64/autobuilder.exe ruby/tools/win64/autobuilder.exe
cp linux64/extractor ruby/tools/linux64/extractor
cp osx64/extractor ruby/tools/osx64/extractor
cp win64/extractor.exe ruby/tools/win64/extractor.exe
chmod +x ruby/tools/{linux64,osx64}/{autobuilder,extractor}
zip -rq codeql-ruby.zip ruby
- uses: actions/upload-artifact@v3
with:

View File

@@ -4,7 +4,6 @@ on:
push:
paths:
- "ruby/**"
- "shared/**"
- .github/workflows/ruby-build.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml

View File

@@ -16,7 +16,6 @@ on:
branches:
- main
- rc/*
- codeql-cli-*
push:
paths:
- "swift/**"
@@ -31,7 +30,6 @@ on:
branches:
- main
- rc/*
- codeql-cli-*
jobs:
# not using a matrix as you cannot depend on a specific job in a matrix, and we want to start linux checks

View File

@@ -17,6 +17,4 @@ jobs:
- uses: actions/checkout@v3
- name: Check synchronized files
run: python config/sync-files.py
- name: Check dbscheme fragments
run: python config/sync-dbscheme-fragments.py

View File

@@ -1,46 +0,0 @@
name: Test tree-sitter-extractor
on:
push:
paths:
- "shared/tree-sitter-extractor/**"
- .github/workflows/tree-sitter-extractor-test.yml
branches:
- main
- "rc/*"
pull_request:
paths:
- "shared/tree-sitter-extractor/**"
- .github/workflows/tree-sitter-extractor-test.yml
branches:
- main
- "rc/*"
env:
CARGO_TERM_COLOR: always
defaults:
run:
working-directory: shared/tree-sitter-extractor
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check formatting
run: cargo fmt --all -- --check
- name: Run tests
run: cargo test --verbose
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check formatting
run: cargo fmt --check
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run clippy
run: cargo clippy -- --no-deps -D warnings -A clippy::new_without_default -A clippy::too_many_arguments

View File

@@ -5,9 +5,9 @@ repos:
rev: v3.2.0
hooks:
- id: trailing-whitespace
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)|.*\.patch
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
- id: end-of-file-fixer
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)|.*\.patch
exclude: /test/.*$(?<!\.ql)(?<!\.qll)(?<!\.qlref)
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v13.0.1
@@ -19,12 +19,7 @@ repos:
rev: v1.6.0
hooks:
- id: autopep8
files: ^misc/codegen/.*\.py
- repo: https://github.com/warchant/pre-commit-buildifier
rev: 0.0.2
hooks:
- id: buildifier
files: ^swift/.*\.py
- repo: local
hooks:

18
.vscode/tasks.json vendored
View File

@@ -22,22 +22,6 @@
"command": "${config:python.pythonPath}",
},
"problemMatcher": []
},
{
"label": "Accept .expected changes from CI",
"type": "process",
// Non-Windows OS will usually have Python 3 already installed at /usr/bin/python3.
"command": "python3",
"args": [
"misc/scripts/accept-expected-changes-from-ci.py"
],
"group": "build",
"windows": {
// On Windows, use whatever Python interpreter is configured for this workspace. The default is
// just `python`, so if Python is already on the path, this will find it.
"command": "${config:python.pythonPath}",
},
"problemMatcher": []
}
]
}
}

View File

@@ -8,6 +8,7 @@
/swift/ @github/codeql-swift
/misc/codegen/ @github/codeql-swift
/java/kotlin-extractor/ @github/codeql-kotlin
/java/kotlin-explorer/ @github/codeql-kotlin
# ML-powered queries
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
@@ -39,6 +40,3 @@ WORKSPACE.bazel @github/codeql-ci-reviewers
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
/.github/workflows/ruby-* @github/codeql-ruby
/.github/workflows/swift.yml @github/codeql-swift
# Misc
/misc/scripts/accept-expected-changes-from-ci.py @RasmusWL

View File

@@ -1,33 +0,0 @@
{
"files": [
"javascript/ql/lib/semmlecode.javascript.dbscheme",
"python/ql/lib/semmlecode.python.dbscheme",
"ruby/ql/lib/ruby.dbscheme",
"ql/ql/src/ql.dbscheme"
],
"fragments": [
"/*- External data -*/",
"/*- Files and folders -*/",
"/*- Diagnostic messages -*/",
"/*- Diagnostic messages: severity -*/",
"/*- Source location prefix -*/",
"/*- Lines of code -*/",
"/*- Configuration files with key value pairs -*/",
"/*- YAML -*/",
"/*- XML Files -*/",
"/*- XML: sourceline -*/",
"/*- DEPRECATED: External defects and metrics -*/",
"/*- DEPRECATED: Snapshot date -*/",
"/*- DEPRECATED: Duplicate code -*/",
"/*- DEPRECATED: Version control data -*/",
"/*- JavaScript-specific part -*/",
"/*- Ruby dbscheme -*/",
"/*- Erb dbscheme -*/",
"/*- QL dbscheme -*/",
"/*- Dbscheme dbscheme -*/",
"/*- Yaml dbscheme -*/",
"/*- Blame dbscheme -*/",
"/*- JSON dbscheme -*/",
"/*- Python dbscheme -*/"
]
}

View File

@@ -40,6 +40,7 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll",
@@ -47,6 +48,7 @@
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplForRegExp.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll",
@@ -121,10 +123,6 @@
"java/ql/src/utils/modelgenerator/internal/CaptureModels.qll",
"csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll"
],
"Model as Data Generation Java/C# - CaptureModelsPrinting": [
"java/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll",
"csharp/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.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"
@@ -511,8 +509,7 @@
"SensitiveDataHeuristics Python/JS": [
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll",
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll",
"swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll"
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll"
],
"CFG": [
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
@@ -523,10 +520,6 @@
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
],
"SummaryTypeTracker": [
"python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/internal/SummaryTypeTracker.qll"
],
"AccessPathSyntax": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",

View File

@@ -1,86 +0,0 @@
#!/usr/bin/env python3
import argparse
import json
import os
import pathlib
import re
def make_groups(blocks):
groups = {}
for block in blocks:
groups.setdefault("".join(block["lines"]), []).append(block)
return list(groups.values())
def validate_fragments(fragments):
ok = True
for header, blocks in fragments.items():
groups = make_groups(blocks)
if len(groups) > 1:
ok = False
print("Warning: dbscheme fragments with header '{}' are different for {}".format(header, ["{}:{}:{}".format(
group[0]["file"], group[0]["start"], group[0]["end"]) for group in groups]))
return ok
def main():
script_path = os.path.realpath(__file__)
script_dir = os.path.dirname(script_path)
parser = argparse.ArgumentParser(
prog=os.path.basename(script_path),
description='Sync dbscheme fragments across files.'
)
parser.add_argument('files', metavar='dbscheme_file', type=pathlib.Path, nargs='*', default=[],
help='dbscheme files to check')
args = parser.parse_args()
with open(os.path.join(script_dir, "dbscheme-fragments.json"), "r") as f:
config = json.load(f)
fragment_headers = set(config["fragments"])
fragments = {}
ok = True
for file in args.files + config["files"]:
with open(os.path.join(os.path.dirname(script_dir), file), "r") as dbscheme:
header = None
line_number = 1
block = {"file": file, "start": line_number,
"end": None, "lines": []}
def end_block():
block["end"] = line_number - 1
if len(block["lines"]) > 0:
if header is None:
if re.match(r'(?m)\A(\s|//.*$|/\*(\**[^\*])*\*+/)*\Z', "".join(block["lines"])):
# Ignore comments at the beginning of the file
pass
else:
ok = False
print("Warning: dbscheme fragment without header: {}:{}:{}".format(
block["file"], block["start"], block["end"]))
else:
fragments.setdefault(header, []).append(block)
for line in dbscheme:
m = re.match(r"^\/\*-.*-\*\/$", line)
if m:
end_block()
header = line.strip()
if header not in fragment_headers:
ok = False
print("Warning: unknown header for dbscheme fragment: '{}': {}:{}".format(
header, file, line_number))
block = {"file": file, "start": line_number,
"end": None, "lines": []}
block["lines"].append(line)
line_number += 1
block["lines"].append('\n')
line_number += 1
end_block()
if not ok or not validate_fragments(fragments):
exit(1)
if __name__ == "__main__":
main()

View File

@@ -1,4 +0,0 @@
description: Revert support for repeated initializers, which are allowed in C with designated initializers.
compatibility: full
aggregate_field_init.rel: reorder aggregate_field_init.rel (int aggregate, int initializer, int field, int position) aggregate initializer field
aggregate_array_init.rel: reorder aggregate_array_init.rel (int aggregate, int initializer, int element_index, int position) aggregate initializer element_index

View File

@@ -2,4 +2,3 @@ name: codeql/cpp-downgrades
groups: cpp
downgrades: .
library: true
warnOnImplicitThis: true

View File

@@ -4,4 +4,3 @@ groups:
- examples
dependencies:
codeql/cpp-all: ${workspace}
warnOnImplicitThis: true

View File

@@ -0,0 +1 @@
<queries language="cpp"/>

View File

@@ -1,75 +1,3 @@
## 0.8.0
### New Features
* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.
## 0.7.4
No user-facing changes.
## 0.7.3
### Minor Analysis Improvements
* Deleted the deprecated `hasCopyConstructor` predicate from the `Class` class in `Class.qll`.
* Deleted many deprecated predicates and classes with uppercase `AST`, `SSA`, `CFG`, `API`, etc. in their names. Use the PascalCased versions instead.
* Deleted the deprecated `CodeDuplication.qll` file.
## 0.7.2
### New Features
* Added an AST-based interface (`semmle.code.cpp.rangeanalysis.new.RangeAnalysis`) for the relative range analysis library.
* A new predicate `BarrierGuard::getAnIndirectBarrierNode` has been added to the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) to mark indirect expressions as barrier nodes using the `BarrierGuard` API.
### Major Analysis Improvements
* In the intermediate representation, handling of control flow after non-returning calls has been improved. This should remove false positives in queries that use the intermedite representation or libraries based on it, including the new data flow library.
### Minor Analysis Improvements
* The `StdNamespace` class now also includes all inline namespaces that are children of `std` namespace.
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.
## 0.7.1
No user-facing changes.
## 0.7.0
### Breaking Changes
* The internal `SsaConsistency` module has been moved from `SSAConstruction` to `SSAConsitency`, and the deprecated `SSAConsistency` module has been removed.
### Deprecated APIs
* The single-parameter predicates `ArrayOrVectorAggregateLiteral.getElementExpr` and `ClassAggregateLiteral.getFieldExpr` have been deprecated in favor of `ArrayOrVectorAggregateLiteral.getAnElementExpr` and `ClassAggregateLiteral.getAFieldExpr`.
* The recently introduced new data flow and taint tracking APIs have had a
number of module and predicate renamings. The old APIs remain in place for
now.
* The `SslContextCallAbstractConfig`, `SslContextCallConfig`, `SslContextCallBannedProtocolConfig`, `SslContextCallTls12ProtocolConfig`, `SslContextCallTls13ProtocolConfig`, `SslContextCallTlsProtocolConfig`, `SslContextFlowsToSetOptionConfig`, `SslOptionConfig` dataflow configurations from `BoostorgAsio` have been deprecated. Please use `SslContextCallConfigSig`, `SslContextCallGlobal`, `SslContextCallFlow`, `SslContextCallBannedProtocolFlow`, `SslContextCallTls12ProtocolFlow`, `SslContextCallTls13ProtocolFlow`, `SslContextCallTlsProtocolFlow`, `SslContextFlowsToSetOptionFlow`.
### New Features
* Added overridable predicates `getSizeExpr` and `getSizeMult` to the `BufferAccess` class (`semmle.code.cpp.security.BufferAccess.qll`). This makes it possible to model a larger class of buffer reads and writes using the library.
### Minor Analysis Improvements
* The `BufferAccess` library (`semmle.code.cpp.security.BufferAccess`) no longer matches buffer accesses inside unevaluated contexts (such as inside `sizeof` or `decltype` expressions). As a result, queries using this library may see fewer false positives.
### Bug Fixes
* Fixed some accidental predicate visibility in the backwards-compatible wrapper for data flow configurations. In particular `DataFlow::hasFlowPath`, `DataFlow::hasFlow`, `DataFlow::hasFlowTo`, and `DataFlow::hasFlowToExpr` were accidentally exposed in a single version.
## 0.6.1
No user-facing changes.
## 0.6.0
### Breaking Changes

View File

@@ -0,0 +1,4 @@
---
category: breaking
---
* The internal `SsaConsistency` module has been moved from `SSAConstruction` to `SSAConsitency`, and the deprecated `SSAConsistency` module has been removed.

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* The `SslContextCallAbstractConfig`, `SslContextCallConfig`, `SslContextCallBannedProtocolConfig`, `SslContextCallTls12ProtocolConfig`, `SslContextCallTls13ProtocolConfig`, `SslContextCallTlsProtocolConfig`, `SslContextFlowsToSetOptionConfig`, `SslOptionConfig` dataflow configurations from `BoostorgAsio` have been deprecated. Please use `SslContextCallConfigSig`, `SslContextCallMake`, `SslContextCallFlow`, `SslContextCallBannedProtocolFlow`, `SslContextCallTls12ProtocolFlow`, `SslContextCallTls13ProtocolFlow`, `SslContextCallTlsProtocolFlow`, `SslContextFlowsToSetOptionFlow`.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The `BufferAccess` library (`semmle.code.cpp.security.BufferAccess`) no longer matches buffer accesses inside unevaluated contexts (such as inside `sizeof` or `decltype` expressions). As a result, queries using this library may see fewer false positives.

View File

@@ -1,3 +0,0 @@
## 0.6.1
No user-facing changes.

View File

@@ -1,25 +0,0 @@
## 0.7.0
### Breaking Changes
* The internal `SsaConsistency` module has been moved from `SSAConstruction` to `SSAConsitency`, and the deprecated `SSAConsistency` module has been removed.
### Deprecated APIs
* The single-parameter predicates `ArrayOrVectorAggregateLiteral.getElementExpr` and `ClassAggregateLiteral.getFieldExpr` have been deprecated in favor of `ArrayOrVectorAggregateLiteral.getAnElementExpr` and `ClassAggregateLiteral.getAFieldExpr`.
* The recently introduced new data flow and taint tracking APIs have had a
number of module and predicate renamings. The old APIs remain in place for
now.
* The `SslContextCallAbstractConfig`, `SslContextCallConfig`, `SslContextCallBannedProtocolConfig`, `SslContextCallTls12ProtocolConfig`, `SslContextCallTls13ProtocolConfig`, `SslContextCallTlsProtocolConfig`, `SslContextFlowsToSetOptionConfig`, `SslOptionConfig` dataflow configurations from `BoostorgAsio` have been deprecated. Please use `SslContextCallConfigSig`, `SslContextCallGlobal`, `SslContextCallFlow`, `SslContextCallBannedProtocolFlow`, `SslContextCallTls12ProtocolFlow`, `SslContextCallTls13ProtocolFlow`, `SslContextCallTlsProtocolFlow`, `SslContextFlowsToSetOptionFlow`.
### New Features
* Added overridable predicates `getSizeExpr` and `getSizeMult` to the `BufferAccess` class (`semmle.code.cpp.security.BufferAccess.qll`). This makes it possible to model a larger class of buffer reads and writes using the library.
### Minor Analysis Improvements
* The `BufferAccess` library (`semmle.code.cpp.security.BufferAccess`) no longer matches buffer accesses inside unevaluated contexts (such as inside `sizeof` or `decltype` expressions). As a result, queries using this library may see fewer false positives.
### Bug Fixes
* Fixed some accidental predicate visibility in the backwards-compatible wrapper for data flow configurations. In particular `DataFlow::hasFlowPath`, `DataFlow::hasFlow`, `DataFlow::hasFlowTo`, and `DataFlow::hasFlowToExpr` were accidentally exposed in a single version.

View File

@@ -1,3 +0,0 @@
## 0.7.1
No user-facing changes.

View File

@@ -1,15 +0,0 @@
## 0.7.2
### New Features
* Added an AST-based interface (`semmle.code.cpp.rangeanalysis.new.RangeAnalysis`) for the relative range analysis library.
* A new predicate `BarrierGuard::getAnIndirectBarrierNode` has been added to the new dataflow library (`semmle.code.cpp.dataflow.new.DataFlow`) to mark indirect expressions as barrier nodes using the `BarrierGuard` API.
### Major Analysis Improvements
* In the intermediate representation, handling of control flow after non-returning calls has been improved. This should remove false positives in queries that use the intermedite representation or libraries based on it, including the new data flow library.
### Minor Analysis Improvements
* The `StdNamespace` class now also includes all inline namespaces that are children of `std` namespace.
* The new dataflow (`semmle.code.cpp.dataflow.new.DataFlow`) and taint-tracking libraries (`semmle.code.cpp.dataflow.new.TaintTracking`) now support tracking flow through static local variables.

View File

@@ -1,7 +0,0 @@
## 0.7.3
### Minor Analysis Improvements
* Deleted the deprecated `hasCopyConstructor` predicate from the `Class` class in `Class.qll`.
* Deleted many deprecated predicates and classes with uppercase `AST`, `SSA`, `CFG`, `API`, etc. in their names. Use the PascalCased versions instead.
* Deleted the deprecated `CodeDuplication.qll` file.

View File

@@ -1,3 +0,0 @@
## 0.7.4
No user-facing changes.

View File

@@ -1,9 +0,0 @@
## 0.8.0
### New Features
* The `ProductFlow::StateConfigSig` signature now includes default predicates for `isBarrier1`, `isBarrier2`, `isAdditionalFlowStep1`, and `isAdditionalFlowStep1`. Hence, it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
### Minor Analysis Improvements
* Deleted the deprecated `getURL` predicate from the `Container`, `Folder`, and `File` classes. Use the `getLocation` predicate instead.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.8.0
lastReleaseVersion: 0.6.0

View File

@@ -0,0 +1,301 @@
import semmle.code.cpp.ir.dataflow.DataFlow
import semmle.code.cpp.ir.dataflow.DataFlow2
module ProductFlow {
abstract class Configuration extends string {
bindingset[this]
Configuration() { any() }
/**
* Holds if `(source1, source2)` is a relevant data flow source.
*
* `source1` and `source2` must belong to the same callable.
*/
predicate isSourcePair(DataFlow::Node source1, DataFlow::Node source2) { none() }
/**
* Holds if `(source1, source2)` is a relevant data flow source with initial states `state1`
* and `state2`, respectively.
*
* `source1` and `source2` must belong to the same callable.
*/
predicate isSourcePair(
DataFlow::Node source1, DataFlow::FlowState state1, DataFlow::Node source2,
DataFlow::FlowState state2
) {
state1 = "" and
state2 = "" and
this.isSourcePair(source1, source2)
}
/**
* Holds if `(sink1, sink2)` is a relevant data flow sink.
*
* `sink1` and `sink2` must belong to the same callable.
*/
predicate isSinkPair(DataFlow::Node sink1, DataFlow::Node sink2) { none() }
/**
* Holds if `(sink1, sink2)` is a relevant data flow sink with final states `state1`
* and `state2`, respectively.
*
* `sink1` and `sink2` must belong to the same callable.
*/
predicate isSinkPair(
DataFlow::Node sink1, DataFlow::FlowState state1, DataFlow::Node sink2,
DataFlow::FlowState state2
) {
state1 = "" and
state2 = "" and
this.isSinkPair(sink1, sink2)
}
/**
* Holds if data flow through `node` is prohibited through the first projection of the product
* dataflow graph when the flow state is `state`.
*/
predicate isBarrier1(DataFlow::Node node, DataFlow::FlowState state) {
this.isBarrier1(node) and state = ""
}
/**
* Holds if data flow through `node` is prohibited through the second projection of the product
* dataflow graph when the flow state is `state`.
*/
predicate isBarrier2(DataFlow::Node node, DataFlow::FlowState state) {
this.isBarrier2(node) and state = ""
}
/**
* Holds if data flow through `node` is prohibited through the first projection of the product
* dataflow graph.
*/
predicate isBarrier1(DataFlow::Node node) { none() }
/**
* Holds if data flow through `node` is prohibited through the second projection of the product
* dataflow graph.
*/
predicate isBarrier2(DataFlow::Node node) { none() }
/**
* Holds if data flow out of `node` is prohibited in the first projection of the product
* dataflow graph.
*/
predicate isBarrierOut1(DataFlow::Node node) { none() }
/**
* Holds if data flow out of `node` is prohibited in the second projection of the product
* dataflow graph.
*/
predicate isBarrierOut2(DataFlow::Node node) { none() }
/*
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the first projection of the product dataflow graph.
*/
predicate isAdditionalFlowStep1(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the first projection of the product dataflow graph.
*
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep1(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
state1 instanceof DataFlow::FlowStateEmpty and
state2 instanceof DataFlow::FlowStateEmpty and
this.isAdditionalFlowStep1(node1, node2)
}
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the second projection of the product dataflow graph.
*/
predicate isAdditionalFlowStep2(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the second projection of the product dataflow graph.
*
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep2(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
state1 instanceof DataFlow::FlowStateEmpty and
state2 instanceof DataFlow::FlowStateEmpty and
this.isAdditionalFlowStep2(node1, node2)
}
/**
* Holds if data flow into `node` is prohibited in the first projection of the product
* dataflow graph.
*/
predicate isBarrierIn1(DataFlow::Node node) { none() }
/**
* Holds if data flow into `node` is prohibited in the second projection of the product
* dataflow graph.
*/
predicate isBarrierIn2(DataFlow::Node node) { none() }
predicate hasFlowPath(
DataFlow::PathNode source1, DataFlow2::PathNode source2, DataFlow::PathNode sink1,
DataFlow2::PathNode sink2
) {
reachable(this, source1, source2, sink1, sink2)
}
}
private import Internal
module Internal {
class Conf1 extends DataFlow::Configuration {
Conf1() { this = "Conf1" }
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
exists(Configuration conf | conf.isSourcePair(source, state, _, _))
}
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
exists(Configuration conf | conf.isSinkPair(sink, state, _, _))
}
override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
exists(Configuration conf | conf.isBarrier1(node, state))
}
override predicate isBarrierOut(DataFlow::Node node) {
exists(Configuration conf | conf.isBarrierOut1(node))
}
override predicate isAdditionalFlowStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
exists(Configuration conf | conf.isAdditionalFlowStep1(node1, state1, node2, state2))
}
override predicate isBarrierIn(DataFlow::Node node) {
exists(Configuration conf | conf.isBarrierIn1(node))
}
}
class Conf2 extends DataFlow2::Configuration {
Conf2() { this = "Conf2" }
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
exists(Configuration conf, DataFlow::PathNode source1 |
conf.isSourcePair(source1.getNode(), source1.getState(), source, state) and
any(Conf1 c).hasFlowPath(source1, _)
)
}
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
exists(Configuration conf, DataFlow::PathNode sink1 |
conf.isSinkPair(sink1.getNode(), sink1.getState(), sink, state) and
any(Conf1 c).hasFlowPath(_, sink1)
)
}
override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
exists(Configuration conf | conf.isBarrier2(node, state))
}
override predicate isBarrierOut(DataFlow::Node node) {
exists(Configuration conf | conf.isBarrierOut2(node))
}
override predicate isAdditionalFlowStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
exists(Configuration conf | conf.isAdditionalFlowStep2(node1, state1, node2, state2))
}
override predicate isBarrierIn(DataFlow::Node node) {
exists(Configuration conf | conf.isBarrierIn2(node))
}
}
}
pragma[nomagic]
private predicate reachableInterprocEntry(
Configuration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2,
DataFlow::PathNode node1, DataFlow2::PathNode node2
) {
conf.isSourcePair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState()) and
node1 = source1 and
node2 = source2
or
exists(
DataFlow::PathNode midEntry1, DataFlow2::PathNode midEntry2, DataFlow::PathNode midExit1,
DataFlow2::PathNode midExit2
|
reachableInterprocEntry(conf, source1, source2, midEntry1, midEntry2) and
interprocEdgePair(midExit1, midExit2, node1, node2) and
localPathStep1*(midEntry1, midExit1) and
localPathStep2*(midEntry2, midExit2)
)
}
private predicate localPathStep1(DataFlow::PathNode pred, DataFlow::PathNode succ) {
DataFlow::PathGraph::edges(pred, succ) and
pragma[only_bind_out](pred.getNode().getEnclosingCallable()) =
pragma[only_bind_out](succ.getNode().getEnclosingCallable())
}
private predicate localPathStep2(DataFlow2::PathNode pred, DataFlow2::PathNode succ) {
DataFlow2::PathGraph::edges(pred, succ) and
pragma[only_bind_out](pred.getNode().getEnclosingCallable()) =
pragma[only_bind_out](succ.getNode().getEnclosingCallable())
}
pragma[nomagic]
private predicate interprocEdge1(
Declaration predDecl, Declaration succDecl, DataFlow::PathNode pred1, DataFlow::PathNode succ1
) {
DataFlow::PathGraph::edges(pred1, succ1) and
predDecl != succDecl and
pred1.getNode().getEnclosingCallable() = predDecl and
succ1.getNode().getEnclosingCallable() = succDecl
}
pragma[nomagic]
private predicate interprocEdge2(
Declaration predDecl, Declaration succDecl, DataFlow2::PathNode pred2, DataFlow2::PathNode succ2
) {
DataFlow2::PathGraph::edges(pred2, succ2) and
predDecl != succDecl and
pred2.getNode().getEnclosingCallable() = predDecl and
succ2.getNode().getEnclosingCallable() = succDecl
}
private predicate interprocEdgePair(
DataFlow::PathNode pred1, DataFlow2::PathNode pred2, DataFlow::PathNode succ1,
DataFlow2::PathNode succ2
) {
exists(Declaration predDecl, Declaration succDecl |
interprocEdge1(predDecl, succDecl, pred1, succ1) and
interprocEdge2(predDecl, succDecl, pred2, succ2)
)
}
private predicate reachable(
Configuration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2,
DataFlow::PathNode sink1, DataFlow2::PathNode sink2
) {
exists(DataFlow::PathNode mid1, DataFlow2::PathNode mid2 |
reachableInterprocEntry(conf, source1, source2, mid1, mid2) and
conf.isSinkPair(sink1.getNode(), sink1.getState(), sink2.getNode(), sink2.getState()) and
localPathStep1*(mid1, sink1) and
localPathStep2*(mid2, sink2)
)
}
}

View File

@@ -1 +1,86 @@
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.Bound
import cpp
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.ir.ValueNumbering
private newtype TBound =
TBoundZero() or
TBoundValueNumber(ValueNumber vn) {
exists(Instruction i |
vn.getAnInstruction() = i and
(
i.getResultIRType() instanceof IRIntegerType or
i.getResultIRType() instanceof IRAddressType
) and
not vn.getAnInstruction() instanceof ConstantInstruction
|
i instanceof PhiInstruction
or
i instanceof InitializeParameterInstruction
or
i instanceof CallInstruction
or
i instanceof VariableAddressInstruction
or
i instanceof FieldAddressInstruction
or
i.(LoadInstruction).getSourceAddress() instanceof VariableAddressInstruction
or
i.(LoadInstruction).getSourceAddress() instanceof FieldAddressInstruction
or
i.getAUse() instanceof ArgumentOperand
or
i instanceof PointerArithmeticInstruction
or
i.getAUse() instanceof AddressOperand
)
}
/**
* A bound that may be inferred for an expression plus/minus an integer delta.
*/
abstract class Bound extends TBound {
abstract string toString();
/** Gets an expression that equals this bound plus `delta`. */
abstract Instruction getInstruction(int delta);
/** Gets an expression that equals this bound. */
Instruction getInstruction() { result = getInstruction(0) }
abstract Location getLocation();
}
/**
* The bound that corresponds to the integer 0. This is used to represent all
* integer bounds as bounds are always accompanied by an added integer delta.
*/
class ZeroBound extends Bound, TBoundZero {
override string toString() { result = "0" }
override Instruction getInstruction(int delta) {
result.(ConstantValueInstruction).getValue().toInt() = delta
}
override Location getLocation() { result instanceof UnknownDefaultLocation }
}
/**
* A bound corresponding to the value of an `Instruction`.
*/
class ValueNumberBound extends Bound, TBoundValueNumber {
ValueNumber vn;
ValueNumberBound() { this = TBoundValueNumber(vn) }
/** Gets an `Instruction` that equals this bound. */
override Instruction getInstruction(int delta) {
this = TBoundValueNumber(valueNumber(result)) and delta = 0
}
override string toString() { result = "ValueNumberBound" }
override Location getLocation() { result = vn.getLocation() }
/** Gets the value number that equals this bound. */
ValueNumber getValueNumber() { result = vn }
}

View File

@@ -3,4 +3,3 @@ import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
// Import each extension we want to enable
import extensions.SubtractSelf
import extensions.ConstantBitwiseAndExprRange
import extensions.StrlenLiteralRangeExpr

View File

@@ -238,7 +238,7 @@ class NoReason extends Reason, TNoReason {
class CondReason extends Reason, TCondReason {
IRGuardCondition getCond() { this = TCondReason(result) }
override string toString() { result = this.getCond().toString() }
override string toString() { result = getCond().toString() }
}
/**
@@ -260,14 +260,14 @@ private predicate typeBound(IRIntegerType typ, int lowerbound, int upperbound) {
private class NarrowingCastInstruction extends ConvertInstruction {
NarrowingCastInstruction() {
not this instanceof SafeCastInstruction and
typeBound(this.getResultIRType(), _, _)
typeBound(getResultIRType(), _, _)
}
/** Gets the lower bound of the resulting type. */
int getLowerBound() { typeBound(this.getResultIRType(), result, _) }
int getLowerBound() { typeBound(getResultIRType(), result, _) }
/** Gets the upper bound of the resulting type. */
int getUpperBound() { typeBound(this.getResultIRType(), _, result) }
int getUpperBound() { typeBound(getResultIRType(), _, result) }
}
/**

View File

@@ -109,8 +109,8 @@ private predicate safeCast(IRIntegerType fromtyp, IRIntegerType totyp) {
*/
class PtrToPtrCastInstruction extends ConvertInstruction {
PtrToPtrCastInstruction() {
this.getResultIRType() instanceof IRAddressType and
this.getUnary().getResultIRType() instanceof IRAddressType
getResultIRType() instanceof IRAddressType and
getUnary().getResultIRType() instanceof IRAddressType
}
}
@@ -119,7 +119,7 @@ class PtrToPtrCastInstruction extends ConvertInstruction {
* that cannot overflow or underflow.
*/
class SafeIntCastInstruction extends ConvertInstruction {
SafeIntCastInstruction() { safeCast(this.getUnary().getResultIRType(), this.getResultIRType()) }
SafeIntCastInstruction() { safeCast(getUnary().getResultIRType(), getResultIRType()) }
}
/**

View File

@@ -50,8 +50,8 @@ private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
// If an operand can have negative values, the lower bound is unconstrained.
// Otherwise, the lower bound is zero.
exists(float lLower, float rLower |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
(
(lLower < 0 or rLower < 0) and
result = exprMinVal(this)
@@ -68,10 +68,10 @@ private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
// If an operand can have negative values, the upper bound is unconstrained.
// Otherwise, the upper bound is the minimum of the upper bounds of the operands
exists(float lLower, float lUpper, float rLower, float rUpper |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
(
(lLower < 0 or rLower < 0) and
result = exprMaxVal(this)
@@ -85,6 +85,6 @@ private class ConstantBitwiseAndExprRange extends SimpleRangeAnalysisExpr {
}
override predicate dependsOnChild(Expr child) {
child = this.getLeftOperand() or child = this.getRightOperand()
child = getLeftOperand() or child = getRightOperand()
}
}

View File

@@ -50,7 +50,7 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
* We don't handle the case where `a` and `b` are both non-constant values.
*/
ConstantRShiftExprRange() {
this.getUnspecifiedType() instanceof IntegralType and
getUnspecifiedType() instanceof IntegralType and
exists(Expr l, Expr r |
l = this.(RShiftExpr).getLeftOperand() and
r = this.(RShiftExpr).getRightOperand()
@@ -84,10 +84,10 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
override float getLowerBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
@@ -95,8 +95,8 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
lLower < 0
or
not (
isValidShiftExprShift(rLower, this.getLeftOperand()) and
isValidShiftExprShift(rUpper, this.getLeftOperand())
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
@@ -111,10 +111,10 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
override float getUpperBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
@@ -122,8 +122,8 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
lLower < 0
or
not (
isValidShiftExprShift(rLower, this.getLeftOperand()) and
isValidShiftExprShift(rUpper, this.getLeftOperand())
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
@@ -137,7 +137,7 @@ class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
}
override predicate dependsOnChild(Expr child) {
child = this.getLeftOperand() or child = this.getRightOperand()
child = getLeftOperand() or child = getRightOperand()
}
}
@@ -163,7 +163,7 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
* We don't handle the case where `a` and `b` are both non-constant values.
*/
ConstantLShiftExprRange() {
this.getUnspecifiedType() instanceof IntegralType and
getUnspecifiedType() instanceof IntegralType and
exists(Expr l, Expr r |
l = this.(LShiftExpr).getLeftOperand() and
r = this.(LShiftExpr).getRightOperand()
@@ -197,10 +197,10 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
override float getLowerBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
@@ -208,8 +208,8 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
lLower < 0
or
not (
isValidShiftExprShift(rLower, this.getLeftOperand()) and
isValidShiftExprShift(rUpper, this.getLeftOperand())
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
@@ -228,10 +228,10 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
override float getUpperBounds() {
exists(int lLower, int lUpper, int rLower, int rUpper |
lLower = getFullyConvertedLowerBounds(this.getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(this.getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(this.getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(this.getRightOperand()) and
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
lLower <= lUpper and
rLower <= rUpper
|
@@ -239,8 +239,8 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
lLower < 0
or
not (
isValidShiftExprShift(rLower, this.getLeftOperand()) and
isValidShiftExprShift(rUpper, this.getLeftOperand())
isValidShiftExprShift(rLower, getLeftOperand()) and
isValidShiftExprShift(rUpper, getLeftOperand())
)
then
// We don't want to deal with shifting negative numbers at the moment,
@@ -258,6 +258,6 @@ class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
}
override predicate dependsOnChild(Expr child) {
child = this.getLeftOperand() or child = this.getRightOperand()
child = getLeftOperand() or child = getRightOperand()
}
}

View File

@@ -1,118 +0,0 @@
/**
* This module implements subclasses for various DataFlow nodes that extends
* their `toString()` predicates with range information, if applicable. By
* including this module in a `path-problem` query, this range information
* will be displayed at each step in the query results.
*
* This is currently implemented for `DataFlow::ExprNode` and `DataFlow::DefinitionByReferenceNode`,
* but it is not yet implemented for `DataFlow::ParameterNode`.
*/
private import cpp
private import semmle.code.cpp.dataflow.DataFlow
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
string getExprBoundAsString(Expr e) {
if exists(lowerBound(e)) and exists(upperBound(e))
then result = "[" + lowerBound(e) + ", " + upperBound(e) + "]"
else result = "[unknown range]"
}
/**
* Holds for any integer type after resolving typedefs and stripping `const`
* specifiers, such as for `const size_t`
*/
predicate isIntegralType(Type t) {
// We use `getUnspecifiedType` here because without it things like
// `const size_t` aren't considered to be integral
t.getUnspecifiedType() instanceof IntegralType
}
/**
* Holds for any reference to an integer type after resolving typedefs and
* stripping `const` specifiers, such as for `const size_t&`
*/
predicate isIntegralReferenceType(Type t) { isIntegralType(t.(ReferenceType).stripType()) }
/**
* Holds for any pointer to an integer type after resolving typedefs and
* stripping `const` specifiers, such as for `const size_t*`. This predicate
* holds for any pointer depth, such as for `const size_t**`.
*/
predicate isIntegralPointerType(Type t) { isIntegralType(t.(PointerType).stripType()) }
predicate hasIntegralOrReferenceIntegralType(Locatable e) {
exists(Type t |
(
t = e.(Expr).getUnspecifiedType()
or
// This will cover variables, parameters, type declarations, etc.
t = e.(DeclarationEntry).getUnspecifiedType()
) and
(isIntegralType(t) or isIntegralReferenceType(t))
)
}
Expr getLOp(Operation o) {
result = o.(BinaryOperation).getLeftOperand() or
result = o.(Assignment).getLValue()
}
Expr getROp(Operation o) {
result = o.(BinaryOperation).getRightOperand() or
result = o.(Assignment).getRValue()
}
/**
* Display the ranges of expressions in the path view
*/
private class ExprRangeNode extends DataFlow::ExprNode {
pragma[inline]
private string getIntegralBounds(Expr arg) {
if hasIntegralOrReferenceIntegralType(arg)
then result = getExprBoundAsString(arg)
else result = ""
}
private string getOperationBounds(Operation e) {
result =
getExprBoundAsString(e) + " = " + getExprBoundAsString(getLOp(e)) + e.getOperator() +
getExprBoundAsString(getROp(e))
}
private string getCallBounds(Call e) {
result =
getExprBoundAsString(e) + "(" +
concat(Expr arg, int i |
arg = e.getArgument(i)
|
this.getIntegralBounds(arg), "," order by i
) + ")"
}
override string toString() {
exists(Expr e | e = this.getExpr() |
if hasIntegralOrReferenceIntegralType(e)
then
result = super.toString() + ": " + this.getOperationBounds(e)
or
result = super.toString() + ": " + this.getCallBounds(e)
or
not exists(this.getOperationBounds(e)) and
not exists(this.getCallBounds(e)) and
result = super.toString() + ": " + getExprBoundAsString(e)
else result = super.toString()
)
}
}
/**
* Display the ranges of expressions in the path view
*/
private class ReferenceArgumentRangeNode extends DataFlow::DefinitionByReferenceNode {
override string toString() {
if hasIntegralOrReferenceIntegralType(this.asDefiningArgument())
then result = super.toString() + ": " + getExprBoundAsString(this.getArgument())
else result = super.toString()
}
}

View File

@@ -1,18 +0,0 @@
private import cpp
private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
/**
* Provides range analysis information for calls to `strlen` on literal strings.
* For example, the range of `strlen("literal")` will be 7.
*/
class StrlenLiteralRangeExpr extends SimpleRangeAnalysisExpr, FunctionCall {
StrlenLiteralRangeExpr() {
this.getTarget().hasGlobalOrStdName("strlen") and this.getArgument(0).isConstant()
}
override int getLowerBounds() { result = this.getArgument(0).getValue().length() }
override int getUpperBounds() { result = this.getArgument(0).getValue().length() }
override predicate dependsOnChild(Expr e) { none() }
}

View File

@@ -3,8 +3,8 @@ import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
private class SelfSub extends SimpleRangeAnalysisExpr, SubExpr {
SelfSub() {
// Match `x - x` but not `myInt - (unsigned char)myInt`.
this.getLeftOperand().getExplicitlyConverted().(VariableAccess).getTarget() =
this.getRightOperand().getExplicitlyConverted().(VariableAccess).getTarget()
getLeftOperand().getExplicitlyConverted().(VariableAccess).getTarget() =
getRightOperand().getExplicitlyConverted().(VariableAccess).getTarget()
}
override float getLowerBounds() { result = 0 }

View File

@@ -54,7 +54,7 @@ module PrivateCleartextWrite {
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
}
module WriteFlow = TaintTracking::Global<WriteConfig>;
module WriteFlow = TaintTracking::Make<WriteConfig>;
class PrivateDataSource extends Source {
PrivateDataSource() { this.getExpr() instanceof PrivateDataExpr }

View File

@@ -87,7 +87,7 @@ class SemIntegerLiteralExpr extends SemNumericLiteralExpr {
final int getIntValue() { Specific::integerLiteral(this, _, result) }
final override float getApproximateFloatValue() {
result = this.getIntValue()
result = getIntValue()
or
Specific::largeIntegerLiteral(this, _, result)
}
@@ -124,13 +124,13 @@ class SemBinaryExpr extends SemKnownExpr {
/** Holds if `a` and `b` are the two operands, in either order. */
final predicate hasOperands(SemExpr a, SemExpr b) {
a = this.getLeftOperand() and b = this.getRightOperand()
a = getLeftOperand() and b = getRightOperand()
or
a = this.getRightOperand() and b = this.getLeftOperand()
a = getRightOperand() and b = getLeftOperand()
}
/** Gets the two operands. */
final SemExpr getAnOperand() { result = this.getLeftOperand() or result = this.getRightOperand() }
final SemExpr getAnOperand() { result = getLeftOperand() or result = getRightOperand() }
}
/** An expression that performs and ordered comparison of two operands. */
@@ -154,8 +154,8 @@ class SemRelationalExpr extends SemBinaryExpr {
*/
final SemExpr getLesserOperand() {
if opcode instanceof Opcode::CompareLT or opcode instanceof Opcode::CompareLE
then result = this.getLeftOperand()
else result = this.getRightOperand()
then result = getLeftOperand()
else result = getRightOperand()
}
/**
@@ -167,8 +167,8 @@ class SemRelationalExpr extends SemBinaryExpr {
*/
final SemExpr getGreaterOperand() {
if opcode instanceof Opcode::CompareGT or opcode instanceof Opcode::CompareGE
then result = this.getLeftOperand()
else result = this.getRightOperand()
then result = getLeftOperand()
else result = getRightOperand()
}
/** Holds if this comparison returns `false` if the two operands are equal. */
@@ -280,11 +280,11 @@ class SemLoadExpr extends SemNullaryExpr {
}
class SemSsaLoadExpr extends SemLoadExpr {
SemSsaLoadExpr() { exists(this.getDef()) }
SemSsaLoadExpr() { exists(getDef()) }
}
class SemNonSsaLoadExpr extends SemLoadExpr {
SemNonSsaLoadExpr() { not exists(this.getDef()) }
SemNonSsaLoadExpr() { not exists(getDef()) }
}
class SemStoreExpr extends SemUnaryExpr {

View File

@@ -5,99 +5,19 @@
private import cpp as Cpp
private import semmle.code.cpp.ir.IR as IR
private import Semantic
private import analysis.Bound as IRBound
private import experimental.semmle.code.cpp.rangeanalysis.Bound as IRBound
private import semmle.code.cpp.controlflow.IRGuards as IRGuards
private import semmle.code.cpp.ir.ValueNumbering
module SemanticExprConfig {
class Location = Cpp::Location;
/** A `ConvertInstruction` or a `CopyValueInstruction`. */
private class Conversion extends IR::UnaryInstruction {
Conversion() {
this instanceof IR::CopyValueInstruction
or
this instanceof IR::ConvertInstruction
}
/** Holds if this instruction converts a value of type `tFrom` to a value of type `tTo`. */
predicate converts(SemType tFrom, SemType tTo) {
tFrom = getSemanticType(this.getUnary().getResultIRType()) and
tTo = getSemanticType(this.getResultIRType())
}
}
/**
* Gets a conversion-like instruction that consumes `op`, and
* which is guaranteed to not overflow.
*/
private IR::Instruction safeConversion(IR::Operand op) {
exists(Conversion conv, SemType tFrom, SemType tTo |
conv.converts(tFrom, tTo) and
conversionCannotOverflow(tFrom, tTo) and
conv.getUnaryOperand() = op and
result = conv
)
}
/** Holds if `i1 = i2` or if `i2` is a safe conversion that consumes `i1`. */
private predicate idOrSafeConversion(IR::Instruction i1, IR::Instruction i2) {
not i1.getResultIRType() instanceof IR::IRVoidType and
(
i1 = i2
or
i2 = safeConversion(i1.getAUse()) and
i1.getBlock() = i2.getBlock()
)
}
module Equiv = QlBuiltins::EquivalenceRelation<IR::Instruction, idOrSafeConversion/2>;
/**
* The expressions on which we perform range analysis.
*/
class Expr extends Equiv::EquivalenceClass {
/** Gets the n'th instruction in this equivalence class. */
private IR::Instruction getInstruction(int n) {
result =
rank[n + 1](IR::Instruction instr, int i, IR::IRBlock block |
this = Equiv::getEquivalenceClass(instr) and block.getInstruction(i) = instr
|
instr order by i
)
}
/** Gets a textual representation of this element. */
string toString() { result = this.getUnconverted().toString() }
/** Gets the basic block of this expression. */
IR::IRBlock getBlock() { result = this.getUnconverted().getBlock() }
/** Gets the unconverted instruction associated with this expression. */
IR::Instruction getUnconverted() { result = this.getInstruction(0) }
/**
* Gets the final instruction associated with this expression. This
* represents the result after applying all the safe conversions.
*/
IR::Instruction getConverted() {
exists(int n |
result = this.getInstruction(n) and
not exists(this.getInstruction(n + 1))
)
}
/** Gets the type of the result produced by this instruction. */
IR::IRType getResultIRType() { result = this.getConverted().getResultIRType() }
/** Gets the location of the source code for this expression. */
Location getLocation() { result = this.getUnconverted().getLocation() }
}
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 | getSemanticExpr(instr) = expr |
exists(IR::ConstantInstruction instr | instr = expr |
type = getSemanticType(instr.getResultIRType()) and
value = instr.getValue()
)
@@ -138,46 +58,41 @@ module SemanticExprConfig {
predicate nullLiteral(Expr expr, SemAddressType type) { anyConstantExpr(expr, type, _) }
predicate stringLiteral(Expr expr, SemType type, string value) {
anyConstantExpr(expr, type, value) and
expr.getUnconverted() instanceof IR::StringConstantInstruction
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.getUnconverted() and
exists(IR::BinaryInstruction instr | instr = expr |
type = getSemanticType(instr.getResultIRType()) and
leftOperand = getSemanticExpr(instr.getLeft()) and
rightOperand = getSemanticExpr(instr.getRight()) 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) {
exists(IR::UnaryInstruction instr | instr = expr.getUnconverted() |
type = getSemanticType(instr.getResultIRType()) and
operand = getSemanticExpr(instr.getUnary()) and
// REVIEW: Merge the two operand types.
opcode.toString() = instr.getOpcode().toString()
)
or
exists(IR::StoreInstruction instr | instr = expr.getUnconverted() |
type = getSemanticType(instr.getResultIRType()) and
operand = getSemanticExpr(instr.getSourceValue()) and
opcode instanceof Opcode::Store
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) {
exists(IR::LoadInstruction load |
load = expr.getUnconverted() and
type = getSemanticType(load.getResultIRType()) and
opcode instanceof Opcode::Load
)
or
exists(IR::InitializeParameterInstruction init |
init = expr.getUnconverted() and
type = getSemanticType(init.getResultIRType()) and
type = getSemanticType(expr.getResultIRType()) and
(
expr instanceof IR::LoadInstruction and opcode instanceof Opcode::Load
or
expr instanceof IR::InitializeParameterInstruction and
opcode instanceof Opcode::InitializeParameter
)
}
@@ -207,10 +122,8 @@ module SemanticExprConfig {
newtype TSsaVariable =
TSsaInstruction(IR::Instruction instr) { instr.hasMemoryResult() } or
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() } or
TSsaPointerArithmeticGuard(ValueNumber instr) {
exists(Guard g, IR::Operand use |
use = instr.getAUse() and use.getIRType() instanceof IR::IRAddressType
|
TSsaPointerArithmeticGuard(IR::PointerArithmeticInstruction instr) {
exists(Guard g, IR::Operand use | use = instr.getAUse() |
g.comparesLt(use, _, _, _, _) or
g.comparesLt(_, use, _, _, _) or
g.comparesEq(use, _, _, _, _) or
@@ -225,7 +138,7 @@ module SemanticExprConfig {
IR::Instruction asInstruction() { none() }
ValueNumber asPointerArithGuard() { none() }
IR::PointerArithmeticInstruction asPointerArithGuard() { none() }
IR::Operand asOperand() { none() }
}
@@ -243,15 +156,15 @@ module SemanticExprConfig {
}
class SsaPointerArithmeticGuard extends SsaVariable, TSsaPointerArithmeticGuard {
ValueNumber vn;
IR::PointerArithmeticInstruction instr;
SsaPointerArithmeticGuard() { this = TSsaPointerArithmeticGuard(vn) }
SsaPointerArithmeticGuard() { this = TSsaPointerArithmeticGuard(instr) }
final override string toString() { result = vn.toString() }
final override string toString() { result = instr.toString() }
final override Location getLocation() { result = vn.getLocation() }
final override Location getLocation() { result = instr.getLocation() }
final override ValueNumber asPointerArithGuard() { result = vn }
final override IR::PointerArithmeticInstruction asPointerArithGuard() { result = instr }
}
class SsaOperand extends SsaVariable, TSsaOperand {
@@ -266,9 +179,7 @@ module SemanticExprConfig {
final override IR::Operand asOperand() { result = op }
}
predicate explicitUpdate(SsaVariable v, Expr sourceExpr) {
getSemanticExpr(v.asInstruction()) = sourceExpr
}
predicate explicitUpdate(SsaVariable v, Expr sourceExpr) { v.asInstruction() = sourceExpr }
predicate phi(SsaVariable v) { v.asInstruction() instanceof IR::PhiInstruction }
@@ -281,9 +192,9 @@ module SemanticExprConfig {
}
Expr getAUse(SsaVariable v) {
result.getUnconverted().(IR::LoadInstruction).getSourceValue() = v.asInstruction()
result.(IR::LoadInstruction).getSourceValue() = v.asInstruction()
or
result.getUnconverted() = v.asPointerArithGuard().getAnInstruction()
result = valueNumber(v.asPointerArithGuard()).getAnInstruction()
}
SemType getSsaVariableType(SsaVariable v) {
@@ -325,7 +236,7 @@ module SemanticExprConfig {
final override predicate hasRead(SsaVariable v) {
exists(IR::Operand operand |
operand.getDef() = v.asInstruction() or
operand.getDef() = v.asPointerArithGuard().getAnInstruction()
operand.getDef() = valueNumber(v.asPointerArithGuard()).getAnInstruction()
|
not operand instanceof IR::PhiInputOperand and
operand.getUse().getBlock() = block
@@ -346,7 +257,7 @@ module SemanticExprConfig {
final override predicate hasRead(SsaVariable v) {
exists(IR::PhiInputOperand operand |
operand.getDef() = v.asInstruction() or
operand.getDef() = v.asPointerArithGuard().getAnInstruction()
operand.getDef() = valueNumber(v.asPointerArithGuard()).getAnInstruction()
|
operand.getPredecessorBlock() = pred and
operand.getUse().getBlock() = succ
@@ -392,21 +303,17 @@ module SemanticExprConfig {
}
Expr getBoundExpr(Bound bound, int delta) {
result = getSemanticExpr(bound.(IRBound::Bound).getInstruction(delta))
result = bound.(IRBound::Bound).getInstruction(delta)
}
class Guard = IRGuards::IRGuardCondition;
predicate guard(Guard guard, BasicBlock block) { block = guard.getBlock() }
Expr getGuardAsExpr(Guard guard) { result = getSemanticExpr(guard) }
Expr getGuardAsExpr(Guard guard) { result = guard }
predicate equalityGuard(Guard guard, Expr e1, Expr e2, boolean polarity) {
exists(IR::Instruction left, IR::Instruction right |
getSemanticExpr(left) = e1 and
getSemanticExpr(right) = e2 and
guard.comparesEq(left.getAUse(), right.getAUse(), 0, true, polarity)
)
guard.comparesEq(e1.getAUse(), e2.getAUse(), 0, true, polarity)
}
predicate guardDirectlyControlsBlock(Guard guard, BasicBlock controlled, boolean branch) {
@@ -417,17 +324,16 @@ module SemanticExprConfig {
guard.controlsEdge(bb1, bb2, branch)
}
Guard comparisonGuard(Expr e) { getSemanticExpr(result) = e }
Guard comparisonGuard(Expr e) { result = e }
predicate implies_v2(Guard g1, boolean b1, Guard g2, boolean b2) {
none() // TODO
}
/** Gets the expression associated with `instr`. */
SemExpr getSemanticExpr(IR::Instruction instr) { result = Equiv::getEquivalenceClass(instr) }
}
predicate getSemanticExpr = SemanticExprConfig::getSemanticExpr/1;
SemExpr getSemanticExpr(IR::Instruction instr) { result = instr }
IR::Instruction getCppInstruction(SemExpr e) { e = result }
SemBasicBlock getSemanticBasicBlock(IR::IRBlock block) { result = block }

View File

@@ -59,7 +59,7 @@ class SemSsaReadPositionBlock extends SemSsaReadPosition {
SemBasicBlock getBlock() { result = block }
SemExpr getAnExpr() { result = this.getBlock().getAnExpr() }
SemExpr getAnExpr() { result = getBlock().getAnExpr() }
}
/**
@@ -70,27 +70,6 @@ predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionP
// Conservatively assume that every edge is a back edge if we don't have dominance information.
(
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
irreducibleSccEdge(edge.getOrigBlock(), phi.getBasicBlock()) or
not edge.getOrigBlock().hasDominanceInformation()
)
}
/**
* Holds if the edge from b1 to b2 is part of a multiple-entry cycle in an irreducible control flow
* graph.
*
* An ireducible control flow graph is one where the usual dominance-based back edge detection does
* not work, because there is a cycle with multiple entry points, meaning there are
* mutually-reachable basic blocks where neither dominates the other. For such a graph, we first
* remove all detectable back-edges using the normal condition that the predecessor block is
* dominated by the successor block, then mark all edges in a cycle in the resulting graph as back
* edges.
*/
private predicate irreducibleSccEdge(SemBasicBlock b1, SemBasicBlock b2) {
trimmedEdge(b1, b2) and trimmedEdge+(b2, b1)
}
private predicate trimmedEdge(SemBasicBlock pred, SemBasicBlock succ) {
pred.getASuccessor() = succ and
not succ.bbDominates(pred)
}

View File

@@ -38,7 +38,7 @@ class SemType extends TSemType {
* 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 = this.toString() }
string getIdentityString() { result = toString() }
/**
* Gets the size of the type, in bytes, if known.
@@ -132,7 +132,7 @@ class SemIntegerType extends SemNumericType {
final predicate isSigned() { signed = true }
/** Holds if this integer type is unsigned. */
final predicate isUnsigned() { not this.isSigned() }
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.
}

View File

@@ -2,7 +2,7 @@
* Simple constant analysis using the Semantic interface.
*/
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import experimental.semmle.code.cpp.semantic.Semantic
private import ConstantAnalysisSpecific as Specific
/** An expression that always has the same integer value. */

View File

@@ -2,7 +2,7 @@
* C++-specific implementation of constant analysis.
*/
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import experimental.semmle.code.cpp.semantic.Semantic
/**
* Gets the constant integer value of the specified expression, if any.

View File

@@ -0,0 +1,29 @@
private import RangeAnalysisStage
module FloatDelta implements DeltaSig {
class Delta = float;
bindingset[d]
bindingset[result]
float toFloat(Delta d) { result = d }
bindingset[d]
bindingset[result]
int toInt(Delta d) { result = d }
bindingset[n]
bindingset[result]
Delta fromInt(int n) { result = n }
bindingset[f]
Delta fromFloat(float f) {
result =
min(float diff, float res |
diff = (res - f) and res = f.ceil()
or
diff = (f - res) and res = f.floor()
|
res order by diff
)
}
}

View File

@@ -11,7 +11,7 @@
*/
private import ModulusAnalysisSpecific::Private
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import experimental.semmle.code.cpp.semantic.Semantic
private import ConstantAnalysis
private import RangeUtils
private import RangeAnalysisStage
@@ -120,6 +120,13 @@ module ModulusAnalysis<DeltaSig D, BoundSig<D> Bounds, UtilSig<D> U> {
)
}
/**
* 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`.
*
@@ -315,4 +322,20 @@ module ModulusAnalysis<DeltaSig D, BoundSig<D> Bounds, UtilSig<D> U> {
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

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

View File

@@ -0,0 +1,2 @@
import RangeAnalysisImpl
import experimental.semmle.code.cpp.semantic.SemanticBound

View File

@@ -0,0 +1,107 @@
private import RangeAnalysisStage
private import RangeAnalysisSpecific
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
private import RangeUtils
private import experimental.semmle.code.cpp.semantic.SemanticBound as SemanticBound
private import experimental.semmle.code.cpp.semantic.SemanticLocation
private import experimental.semmle.code.cpp.semantic.SemanticSSA
module ConstantBounds implements BoundSig<FloatDelta> {
class SemBound instanceof SemanticBound::SemBound {
SemBound() {
this instanceof SemanticBound::SemZeroBound
or
this.(SemanticBound::SemSsaBound).getAVariable() instanceof SemSsaPhiNode
}
string toString() { result = super.toString() }
SemLocation getLocation() { result = super.getLocation() }
SemExpr getExpr(float delta) { result = super.getExpr(delta) }
}
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
}
}
private module RelativeBounds implements BoundSig<FloatDelta> {
class SemBound instanceof SemanticBound::SemBound {
SemBound() { not this instanceof SemanticBound::SemZeroBound }
string toString() { result = super.toString() }
SemLocation getLocation() { result = super.getLocation() }
SemExpr getExpr(float delta) { result = super.getExpr(delta) }
}
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
}
}
private module ConstantStage =
RangeStage<FloatDelta, ConstantBounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
private module RelativeStage =
RangeStage<FloatDelta, RelativeBounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
private newtype TSemReason =
TSemNoReason() or
TSemCondReason(SemGuard guard) {
guard = any(ConstantStage::SemCondReason reason).getCond()
or
guard = any(RelativeStage::SemCondReason reason).getCond()
}
/**
* 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() }
}
private ConstantStage::SemReason constantReason(SemReason reason) {
result instanceof ConstantStage::SemNoReason and reason instanceof SemNoReason
or
result.(ConstantStage::SemCondReason).getCond() = reason.(SemCondReason).getCond()
}
private RelativeStage::SemReason relativeReason(SemReason reason) {
result instanceof RelativeStage::SemNoReason and reason instanceof SemNoReason
or
result.(RelativeStage::SemCondReason).getCond() = reason.(SemCondReason).getCond()
}
predicate semBounded(
SemExpr e, SemanticBound::SemBound b, float delta, boolean upper, SemReason reason
) {
ConstantStage::semBounded(e, b, delta, upper, constantReason(reason))
or
RelativeStage::semBounded(e, b, delta, upper, relativeReason(reason))
}

View File

@@ -2,11 +2,11 @@
* C++-specific implementation of range analysis.
*/
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import experimental.semmle.code.cpp.semantic.Semantic
private import RangeAnalysisStage
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
module CppLangImplConstant implements LangSig<FloatDelta> {
module CppLangImpl implements LangSig<FloatDelta> {
/**
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
*

View File

@@ -65,29 +65,31 @@
private import RangeUtils as Utils
private import SignAnalysisCommon
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.ModulusAnalysis
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExpr
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticSSA
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticGuard
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticCFG
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticType
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticOpcode
private import experimental.semmle.code.cpp.semantic.analysis.ModulusAnalysis
import experimental.semmle.code.cpp.semantic.SemanticExpr
import experimental.semmle.code.cpp.semantic.SemanticSSA
import experimental.semmle.code.cpp.semantic.SemanticGuard
import experimental.semmle.code.cpp.semantic.SemanticCFG
import experimental.semmle.code.cpp.semantic.SemanticType
import experimental.semmle.code.cpp.semantic.SemanticOpcode
private import ConstantAnalysis
private import Sign
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticLocation
import experimental.semmle.code.cpp.semantic.SemanticLocation
/**
* Holds if `typ` is a small integral type with the given lower and upper bounds.
*/
private predicate typeBound(SemIntegerType typ, float lowerbound, float upperbound) {
private predicate typeBound(SemIntegerType typ, int lowerbound, int upperbound) {
exists(int bitSize | bitSize = typ.getByteSize() * 8 |
if typ.isSigned()
then (
upperbound = 2.pow(bitSize - 1) - 1 and
lowerbound = -upperbound - 1
) else (
lowerbound = 0 and
upperbound = 2.pow(bitSize) - 1
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
)
)
)
}
@@ -241,19 +243,11 @@ signature module BoundSig<DeltaSig D> {
}
}
signature module OverflowSig<DeltaSig D> {
predicate semExprDoesNotOverflow(boolean positively, SemExpr expr);
}
module RangeStage<
DeltaSig D, BoundSig<D> Bounds, OverflowSig<D> OverflowParam, LangSig<D> LangParam,
UtilSig<D> UtilParam>
{
module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<D> UtilParam> {
private import Bounds
private import LangParam
private import UtilParam
private import D
private import OverflowParam
/**
* An expression that does conversion, boxing, or unboxing
@@ -277,7 +271,7 @@ module RangeStage<
*/
private class SafeCastExpr extends ConvertOrBoxExpr {
SafeCastExpr() {
conversionCannotOverflow(getTrackedType(pragma[only_bind_into](this.getOperand())),
conversionCannotOverflow(getTrackedType(pragma[only_bind_into](getOperand())),
pragma[only_bind_out](getTrackedType(this)))
}
}
@@ -292,10 +286,10 @@ module RangeStage<
}
/** Gets the lower bound of the resulting type. */
float getLowerBound() { typeBound(getTrackedType(this), result, _) }
int getLowerBound() { typeBound(getTrackedType(this), result, _) }
/** Gets the upper bound of the resulting type. */
float getUpperBound() { typeBound(getTrackedType(this), _, result) }
int getUpperBound() { typeBound(getTrackedType(this), _, result) }
}
private module SignAnalysisInstantiated = SignAnalysis<D, UtilParam>; // TODO: will this cause reevaluation if it's instantiated with the same DeltaSig and UtilParam multiple times?
@@ -501,7 +495,7 @@ module RangeStage<
SemSsaVariable v2, SemGuard guardEq, boolean eqIsTrue, D::Delta d1, D::Delta d2,
D::Delta oldDelta
|
guardEq = semEqFlowCond(v, semSsaRead(pragma[only_bind_into](v2), d1), d2, true, eqIsTrue) and
guardEq = semEqFlowCond(v, semSsaRead(v2, d1), d2, true, eqIsTrue) and
result = boundFlowCond(v2, e, oldDelta, upper, testIsTrue) and
// guardEq needs to control guard
guardEq.directlyControls(result.getBasicBlock(), eqIsTrue) and
@@ -536,7 +530,7 @@ module RangeStage<
/** Gets the condition that is the reason for the bound. */
SemGuard getCond() { this = TSemCondReason(result) }
override string toString() { result = this.getCond().toString() }
override string toString() { result = getCond().toString() }
}
/**
@@ -597,6 +591,24 @@ module RangeStage<
delta = D::fromInt(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 = D::fromInt(1)
else
if semPositive(x)
then upper = false and delta = D::fromInt(0)
else
if strictlyNegativeIntegralExpr(x)
then upper = true and delta = D::fromInt(-1)
else
if semNegative(x)
then upper = true and delta = D::fromInt(0)
else none()
)
or
exists(SemExpr x, SemSubExpr sub |
e2 = sub and
sub.getLeftOperand() = e1 and
@@ -729,7 +741,7 @@ module RangeStage<
) {
exists(SemExpr e, D::Delta d1, D::Delta d2 |
unequalFlowStepIntegralSsa(v, pos, e, d1, reason) and
boundedUpper(e, b, d2) and
boundedUpper(e, b, d1) and
boundedLower(e, b, d2) and
delta = D::fromFloat(D::toFloat(d1) + D::toFloat(d2))
)
@@ -877,21 +889,6 @@ module RangeStage<
)
}
pragma[nomagic]
private predicate boundedPhiRankStep(
SemSsaPhiNode phi, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge,
D::Delta origdelta, SemReason reason, int rix
) {
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
Utils::rankedPhiInput(phi, inp, edge, rix) and
boundedPhiCandValidForEdge(phi, b, delta, upper, fromBackEdge, origdelta, reason, inp, edge)
|
if rix = 1
then any()
else boundedPhiRankStep(phi, b, delta, upper, fromBackEdge, origdelta, reason, rix - 1)
)
}
/**
* Holds if `b + delta` is a valid bound for `phi`.
* - `upper = true` : `phi <= b + delta`
@@ -901,9 +898,8 @@ module RangeStage<
SemSsaPhiNode phi, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge,
D::Delta origdelta, SemReason reason
) {
exists(int r |
Utils::maxPhiInputRank(phi, r) and
boundedPhiRankStep(phi, b, delta, upper, fromBackEdge, origdelta, reason, r)
forex(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge | edge.phiInput(phi, inp) |
boundedPhiCandValidForEdge(phi, b, delta, upper, fromBackEdge, origdelta, reason, inp, edge)
)
}
@@ -945,81 +941,6 @@ module RangeStage<
bounded(cast.getOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
}
predicate bounded(
SemExpr e, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge, D::Delta origdelta,
SemReason reason
) {
initialBounded(e, b, delta, upper, fromBackEdge, origdelta, reason) and
(
semExprDoesNotOverflow(upper.booleanNot(), e)
or
not potentiallyOverflowingExpr(upper.booleanNot(), e)
or
exists(D::Delta otherDelta |
initialBounded(e, _, otherDelta, upper.booleanNot(), _, _, _) and
(
upper = true and D::toFloat(otherDelta) >= 0
or
upper = false and D::toFloat(otherDelta) <= 0
)
)
)
}
predicate potentiallyOverflowingExpr(boolean positively, SemExpr expr) {
(
expr.getOpcode() instanceof Opcode::Add or
expr.getOpcode() instanceof Opcode::PointerAdd
) and
(
positively = true and
(
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getLeftOperand())) = TPos() and
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getRightOperand())) = TPos()
)
or
positively = false and
(
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getLeftOperand())) = TNeg() and
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getRightOperand())) = TNeg()
)
)
or
(
expr.getOpcode() instanceof Opcode::Sub or
expr.getOpcode() instanceof Opcode::PointerSub
) and
(
positively = true and
(
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getLeftOperand())) = TPos() and
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getRightOperand())) = TNeg()
)
or
positively = false and
(
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getLeftOperand())) = TNeg() and
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getRightOperand())) = TPos()
)
)
or
positively in [true, false] and
(
expr.getOpcode() instanceof Opcode::Mul or
expr.getOpcode() instanceof Opcode::ShiftLeft
)
or
positively = false and
(
expr.getOpcode() instanceof Opcode::Negate or
expr.getOpcode() instanceof Opcode::SubOne or
expr.(SemDivExpr).getSemType() instanceof SemFloatingPointType
)
or
positively = true and
expr.getOpcode() instanceof Opcode::AddOne
}
/**
* Computes a normal form of `x` where -0.0 has changed to +0.0. This can be
* needed on the lesser side of a floating-point comparison or on both sides of
@@ -1034,7 +955,7 @@ module RangeStage<
* - `upper = true` : `e <= b + delta`
* - `upper = false` : `e >= b + delta`
*/
predicate initialBounded(
private predicate bounded(
SemExpr e, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge, D::Delta origdelta,
SemReason reason
) {
@@ -1122,196 +1043,13 @@ module RangeStage<
delta = D::fromFloat(f) and
if semPositive(e) then f >= 0 else any()
)
or
exists(
SemBound bLeft, SemBound bRight, D::Delta dLeft, D::Delta dRight, boolean fbeLeft,
boolean fbeRight, D::Delta odLeft, D::Delta odRight, SemReason rLeft, SemReason rRight
|
boundedAddOperand(e, upper, bLeft, false, dLeft, fbeLeft, odLeft, rLeft) and
boundedAddOperand(e, upper, bRight, true, dRight, fbeRight, odRight, rRight) and
delta = D::fromFloat(D::toFloat(dLeft) + D::toFloat(dRight)) and
fromBackEdge = fbeLeft.booleanOr(fbeRight)
|
b = bLeft and origdelta = odLeft and reason = rLeft and bRight instanceof SemZeroBound
or
b = bRight and origdelta = odRight and reason = rRight and bLeft instanceof SemZeroBound
)
or
exists(
SemRemExpr rem, D::Delta d_max, D::Delta d1, D::Delta d2, boolean fbe1, boolean fbe2,
D::Delta od1, D::Delta od2, SemReason r1, SemReason r2
|
rem = e and
b instanceof SemZeroBound and
not (upper = true and semPositive(rem.getRightOperand())) and
not (upper = true and semPositive(rem.getLeftOperand())) and
boundedRemExpr(rem, true, d1, fbe1, od1, r1) and
boundedRemExpr(rem, false, d2, fbe2, od2, r2) and
(
if D::toFloat(d1).abs() > D::toFloat(d2).abs()
then (
d_max = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1
) else (
d_max = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2
)
)
|
upper = true and delta = D::fromFloat(D::toFloat(d_max).abs() - 1)
or
upper = false and delta = D::fromFloat(-D::toFloat(d_max).abs() + 1)
)
or
exists(
D::Delta dLeft, D::Delta dRight, boolean fbeLeft, boolean fbeRight, D::Delta odLeft,
D::Delta odRight, SemReason rLeft, SemReason rRight
|
boundedMulOperand(e, upper, true, dLeft, fbeLeft, odLeft, rLeft) and
boundedMulOperand(e, upper, false, dRight, fbeRight, odRight, rRight) and
delta = D::fromFloat(D::toFloat(dLeft) * D::toFloat(dRight)) and
fromBackEdge = fbeLeft.booleanOr(fbeRight)
|
b instanceof SemZeroBound and origdelta = odLeft and reason = rLeft
or
b instanceof SemZeroBound and origdelta = odRight and reason = rRight
)
)
}
pragma[nomagic]
private predicate boundedConditionalExpr(
SemConditionalExpr cond, SemBound b, boolean upper, boolean branch, D::Delta delta,
boolean fromBackEdge, D::Delta origdelta, SemReason reason
) {
bounded(cond.getBranchExpr(branch), b, delta, upper, fromBackEdge, origdelta, reason)
}
pragma[nomagic]
private predicate boundedAddOperand(
SemAddExpr add, boolean upper, SemBound b, boolean isLeft, D::Delta delta, boolean fromBackEdge,
D::Delta origdelta, SemReason reason
) {
// `semValueFlowStep` already handles the case where one of the operands is a constant.
not semValueFlowStep(add, _, _) and
(
isLeft = true and
bounded(add.getLeftOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
or
isLeft = false and
bounded(add.getRightOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
)
}
pragma[nomagic]
private predicate boundedRemExpr(
SemRemExpr rem, boolean upper, D::Delta delta, boolean fromBackEdge, D::Delta origdelta,
SemReason reason
) {
bounded(rem.getRightOperand(), any(SemZeroBound zb), delta, upper, fromBackEdge, origdelta,
reason)
}
/**
* Define `cmp(true) = <=` and `cmp(false) = >=`.
*
* Holds if `mul = left * right`, and in order to know if `mul cmp(upper) 0 + k` (for
* some `k`) we need to know that `left cmp(upperLeft) 0 + k1` and
* `right cmp(upperRight) 0 + k2` (for some `k1` and `k2`).
*/
pragma[nomagic]
private predicate boundedMulOperandCand(
SemMulExpr mul, SemExpr left, SemExpr right, boolean upper, boolean upperLeft,
boolean upperRight
) {
not boundFlowStepMul(mul, _, _) and
mul.getLeftOperand() = left and
mul.getRightOperand() = right and
(
semPositive(left) and
(
// left, right >= 0
semPositive(right) and
(
// max(left * right) = max(left) * max(right)
upper = true and
upperLeft = true and
upperRight = true
or
// min(left * right) = min(left) * min(right)
upper = false and
upperLeft = false and
upperRight = false
)
or
// left >= 0, right <= 0
semNegative(right) and
(
// max(left * right) = min(left) * max(right)
upper = true and
upperLeft = false and
upperRight = true
or
// min(left * right) = max(left) * min(right)
upper = false and
upperLeft = true and
upperRight = false
)
)
or
semNegative(left) and
(
// left <= 0, right >= 0
semPositive(right) and
(
// max(left * right) = max(left) * min(right)
upper = true and
upperLeft = true and
upperRight = false
or
// min(left * right) = min(left) * max(right)
upper = false and
upperLeft = false and
upperRight = true
)
or
// left, right <= 0
semNegative(right) and
(
// max(left * right) = min(left) * min(right)
upper = true and
upperLeft = false and
upperRight = false
or
// min(left * right) = max(left) * max(right)
upper = false and
upperLeft = true and
upperRight = true
)
)
)
}
/**
* Holds if `isLeft = true` and `mul`'s left operand is bounded by `delta`,
* or if `isLeft = false` and `mul`'s right operand is bounded by `delta`.
*
* If `upper = true` the computed bound contributes to an upper bound of `mul`,
* and if `upper = false` it contributes to a lower bound.
* The `fromBackEdge`, `origdelta`, `reason` triple are defined by the recursive
* call to `bounded`.
*/
pragma[nomagic]
private predicate boundedMulOperand(
SemMulExpr mul, boolean upper, boolean isLeft, D::Delta delta, boolean fromBackEdge,
D::Delta origdelta, SemReason reason
) {
exists(boolean upperLeft, boolean upperRight, SemExpr left, SemExpr right |
boundedMulOperandCand(mul, left, right, upper, upperLeft, upperRight)
|
isLeft = true and
bounded(left, any(SemZeroBound zb), delta, upperLeft, fromBackEdge, origdelta, reason)
or
isLeft = false and
bounded(right, any(SemZeroBound zb), delta, upperRight, fromBackEdge, origdelta, reason)
)
}
}

View File

@@ -2,8 +2,8 @@
* Provides utility predicates for range analysis.
*/
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import RangeAnalysisRelativeSpecific
private import experimental.semmle.code.cpp.semantic.Semantic
private import RangeAnalysisSpecific
private import RangeAnalysisStage as Range
private import ConstantAnalysis
@@ -138,26 +138,3 @@ module RangeUtil<Range::DeltaSig D, Range::LangSig<D> Lang> implements Range::Ut
not exists(Lang::getAlternateTypeForSsaVariable(var)) and result = var.getType()
}
}
/**
* Holds if `rix` is the number of input edges to `phi`.
*/
predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
rix = max(int r | rankedPhiInput(phi, _, _, r))
}
/**
* 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`.
*/
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

@@ -1,4 +1,4 @@
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import experimental.semmle.code.cpp.semantic.Semantic
newtype TSign =
TNeg() or
@@ -73,7 +73,7 @@ class Sign extends TSign {
* Gets a possible sign after subtracting an expression with sign `s` from an expression
* that has this sign.
*/
Sign sub(Sign s) { result = this.add(s.neg()) }
Sign sub(Sign s) { result = add(s.neg()) }
/**
* Gets a possible sign after multiplying an expression with sign `s` to an expression
@@ -231,37 +231,37 @@ class Sign extends TSign {
or
op instanceof Opcode::Store and result = this
or
op instanceof Opcode::AddOne and result = this.inc()
op instanceof Opcode::AddOne and result = inc()
or
op instanceof Opcode::SubOne and result = this.dec()
op instanceof Opcode::SubOne and result = dec()
or
op instanceof Opcode::Negate and result = this.neg()
op instanceof Opcode::Negate and result = neg()
or
op instanceof Opcode::BitComplement and result = this.bitnot()
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 = this.add(s)
op instanceof Opcode::Add and result = add(s)
or
op instanceof Opcode::Sub and result = this.sub(s)
op instanceof Opcode::Sub and result = sub(s)
or
op instanceof Opcode::Mul and result = this.mul(s)
op instanceof Opcode::Mul and result = mul(s)
or
op instanceof Opcode::Div and result = this.div(s)
op instanceof Opcode::Div and result = div(s)
or
op instanceof Opcode::Rem and result = this.rem(s)
op instanceof Opcode::Rem and result = rem(s)
or
op instanceof Opcode::BitAnd and result = this.bitand(s)
op instanceof Opcode::BitAnd and result = bitand(s)
or
op instanceof Opcode::BitOr and result = this.bitor(s)
op instanceof Opcode::BitOr and result = bitor(s)
or
op instanceof Opcode::BitXor and result = this.bitxor(s)
op instanceof Opcode::BitXor and result = bitxor(s)
or
op instanceof Opcode::ShiftLeft and result = this.lshift(s)
op instanceof Opcode::ShiftLeft and result = lshift(s)
or
op instanceof Opcode::ShiftRight and result = this.rshift(s)
op instanceof Opcode::ShiftRight and result = rshift(s)
or
op instanceof Opcode::ShiftRightUnsigned and result = this.urshift(s)
op instanceof Opcode::ShiftRightUnsigned and result = urshift(s)
}
}

View File

@@ -8,7 +8,7 @@
private import RangeAnalysisStage
private import SignAnalysisSpecific as Specific
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import experimental.semmle.code.cpp.semantic.Semantic
private import ConstantAnalysis
private import RangeUtils
private import Sign
@@ -198,16 +198,6 @@ module SignAnalysis<DeltaSig D, UtilSig<D> Utils> {
}
}
/** An expression of an unsigned type. */
private class UnsignedExpr extends FlowSignExpr {
UnsignedExpr() { Utils::getTrackedType(this) instanceof SemUnsignedIntegerType }
override Sign getSignRestriction() {
result = TPos() or
result = TZero()
}
}
pragma[nomagic]
private predicate binaryExprOperands(SemBinaryExpr binary, SemExpr left, SemExpr right) {
binary.getLeftOperand() = left and binary.getRightOperand() = right
@@ -338,11 +328,10 @@ module SignAnalysis<DeltaSig D, UtilSig<D> Utils> {
* - `isEq = false` : `v != eqbound`
*/
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
exists(SemGuard guard, boolean testIsTrue, boolean polarity, SemExpr e |
pos.hasReadOfVar(pragma[only_bind_into](v)) and
semGuardControlsSsaRead(guard, pragma[only_bind_into](pos), testIsTrue) and
e = Utils::semSsaRead(pragma[only_bind_into](v), D::fromInt(0)) and
guard.isEquality(eqbound, e, polarity) and
exists(SemGuard guard, boolean testIsTrue, boolean polarity |
pos.hasReadOfVar(v) and
semGuardControlsSsaRead(guard, pos, testIsTrue) and
guard.isEquality(eqbound, Utils::semSsaRead(v, D::fromInt(0)), polarity) and
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
not unknownSign(eqbound)
)

View File

@@ -2,7 +2,7 @@
* Provides C++-specific definitions for use in sign analysis.
*/
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
private import experimental.semmle.code.cpp.semantic.Semantic
/**
* Workaround to allow certain expressions to have a negative sign, even if the type of the

View File

@@ -5,11 +5,9 @@
private import cpp
private import semmle.code.cpp.ir.IR
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticBound
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysis
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysisImpl
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
private import experimental.semmle.code.cpp.semantic.SemanticBound
private import experimental.semmle.code.cpp.semantic.SemanticExprSpecific
private import RangeAnalysis
/**
* Gets the lower bound of the expression.
@@ -24,10 +22,8 @@ private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
* `lowerBound(expr.getFullyConverted())`
*/
float lowerBound(Expr expr) {
exists(Instruction i, ConstantBounds::SemBound b |
i.getAst() = expr and b instanceof ConstantBounds::SemZeroBound
|
ConstantStage::semBounded(getSemanticExpr(i), b, result, false, _)
exists(Instruction i, SemBound b | i.getAst() = expr and b instanceof SemZeroBound |
semBounded(getSemanticExpr(i), b, result, false, _)
)
}
@@ -44,10 +40,8 @@ float lowerBound(Expr expr) {
* `upperBound(expr.getFullyConverted())`
*/
float upperBound(Expr expr) {
exists(Instruction i, ConstantBounds::SemBound b |
i.getAst() = expr and b instanceof ConstantBounds::SemZeroBound
|
ConstantStage::semBounded(getSemanticExpr(i), b, result, true, _)
exists(Instruction i, SemBound b | i.getAst() = expr and b instanceof SemZeroBound |
semBounded(getSemanticExpr(i), b, result, true, _)
)
}
@@ -96,15 +90,7 @@ predicate defMightOverflow(RangeSsaDefinition def, StackVariable v) {
* does not consider the possibility that the expression might overflow
* due to a conversion.
*/
predicate exprMightOverflowNegatively(Expr expr) {
lowerBound(expr) < exprMinVal(expr)
or
exists(SemanticExprConfig::Expr semExpr |
semExpr.getUnconverted().getAst() = expr and
ConstantStage::potentiallyOverflowingExpr(false, semExpr) and
not ConstantStage::initialBounded(semExpr, _, _, false, _, _, _)
)
}
predicate exprMightOverflowNegatively(Expr expr) { none() }
/**
* Holds if the expression might overflow negatively. Conversions
@@ -122,15 +108,7 @@ predicate convertedExprMightOverflowNegatively(Expr expr) {
* does not consider the possibility that the expression might overflow
* due to a conversion.
*/
predicate exprMightOverflowPositively(Expr expr) {
upperBound(expr) > exprMaxVal(expr)
or
exists(SemanticExprConfig::Expr semExpr |
semExpr.getUnconverted().getAst() = expr and
ConstantStage::potentiallyOverflowingExpr(true, semExpr) and
not ConstantStage::initialBounded(semExpr, _, _, true, _, _, _)
)
}
predicate exprMightOverflowPositively(Expr expr) { none() }
/**
* Holds if the expression might overflow positively. Conversions

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all
version: 0.8.0
version: 0.6.1-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
@@ -8,5 +8,3 @@ upgrades: upgrades
dependencies:
codeql/ssa: ${workspace}
codeql/tutorial: ${workspace}
codeql/util: ${workspace}
warnOnImplicitThis: true

View File

@@ -176,6 +176,20 @@ class Class extends UserType {
/** Holds if this class, struct or union has a constructor. */
predicate hasConstructor() { exists(this.getAConstructor()) }
/**
* Holds if this class has a copy constructor 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 constructor, and we do not want to promote one particular
* definition by naming it with this predicate. Having a copy constructor
* 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 hasCopyConstructor() { this.getAMemberFunction() instanceof CopyConstructor }
/**
* Like accessOfBaseMember but returns multiple results if there are multiple
* paths to `base` through the inheritance graph.

View File

@@ -42,7 +42,7 @@ class Compilation extends @compilation {
}
/** Gets a file compiled during this invocation. */
File getAFileCompiled() { result = this.getFileCompiled(_) }
File getAFileCompiled() { result = getFileCompiled(_) }
/** Gets the `i`th file compiled during this invocation */
File getFileCompiled(int i) { compilation_compiling_files(this, i, unresolveElement(result)) }
@@ -74,7 +74,7 @@ class Compilation extends @compilation {
/**
* Gets an argument passed to the extractor on this invocation.
*/
string getAnArgument() { result = this.getArgument(_) }
string getAnArgument() { result = getArgument(_) }
/**
* Gets the `i`th argument passed to the extractor on this invocation.

View File

@@ -39,8 +39,7 @@ class Field extends MemberVariable {
* complete most-derived object.
*/
int getAByteOffsetIn(Class mostDerivedClass) {
result =
mostDerivedClass.getABaseClassByteOffset(this.getDeclaringType()) + this.getByteOffset()
result = mostDerivedClass.getABaseClassByteOffset(getDeclaringType()) + getByteOffset()
}
/**
@@ -117,10 +116,10 @@ class BitField extends Field {
int getBitOffset() { fieldoffsets(underlyingElement(this), _, result) }
/** Holds if this bitfield is anonymous. */
predicate isAnonymous() { this.hasName("(unnamed bitfield)") }
predicate isAnonymous() { hasName("(unnamed bitfield)") }
override predicate isInitializable() {
// Anonymous bitfields are not initializable.
not this.isAnonymous()
not isAnonymous()
}
}

View File

@@ -34,6 +34,14 @@ class Container extends Locatable, @container {
*/
string getAbsolutePath() { none() } // overridden by subclasses
/**
* DEPRECATED: Use `getLocation` instead.
* Gets a URL representing the location of this container.
*
* For more information see [Providing URLs](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/#providing-urls).
*/
deprecated string getURL() { none() } // overridden by subclasses
/**
* Gets the relative path of this file or folder from the root folder of the
* analyzed source location. The relative path of the root folder itself is
@@ -175,6 +183,12 @@ class Folder extends Container, @folder {
}
override string getAPrimaryQlClass() { result = "Folder" }
/**
* DEPRECATED: Use `getLocation` instead.
* Gets the URL of this folder.
*/
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
}
/**
@@ -199,6 +213,12 @@ class File extends Container, @file {
result.hasLocationInfo(_, 0, 0, 0, 0)
}
/**
* DEPRECATED: Use `getLocation` instead.
* Gets the URL of this file.
*/
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
/** Holds if this file was compiled as C (at any point). */
predicate compiledAsC() { fileannotations(underlyingElement(this), 1, "compiled as c", "1") }

View File

@@ -24,10 +24,10 @@ class LinkTarget extends @link_target {
* captured as part of the snapshot, then everything is grouped together
* into a single dummy link target.
*/
predicate isDummy() { this.getBinary().getAbsolutePath() = "" }
predicate isDummy() { getBinary().getAbsolutePath() = "" }
/** Gets a textual representation of this element. */
string toString() { result = this.getBinary().getAbsolutePath() }
string toString() { result = getBinary().getAbsolutePath() }
/**
* Gets a function which was compiled into this link target, or had its

View File

@@ -34,7 +34,7 @@ class Macro extends PreprocessorDirective, @ppd_define {
* Gets the name of the macro. For example, `MAX` in
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
*/
string getName() { result = this.getHead().regexpCapture("([^(]*+).*", 1) }
string getName() { result = this.getHead().splitAt("(", 0) }
/** Holds if the macro has name `name`. */
predicate hasName(string name) { this.getName() = name }

View File

@@ -24,7 +24,7 @@ class NameQualifier extends NameQualifiableElement, @namequalifier {
* Gets the expression ultimately qualified by the chain of name
* qualifiers. For example, `f()` in `N1::N2::f()`.
*/
Expr getExpr() { result = this.getQualifiedElement+() }
Expr getExpr() { result = getQualifiedElement+() }
/** Gets a location for this name qualifier. */
override Location getLocation() { namequalifiers(underlyingElement(this), _, _, result) }
@@ -56,12 +56,12 @@ class NameQualifier extends NameQualifiableElement, @namequalifier {
if nqe instanceof SpecialNameQualifyingElement
then
exists(Access a |
a = this.getQualifiedElement() and
a = getQualifiedElement() and
result = a.getTarget().getDeclaringType()
)
or
exists(FunctionCall c |
c = this.getQualifiedElement() and
c = getQualifiedElement() and
result = c.getTarget().getDeclaringType()
)
else result = nqe
@@ -109,7 +109,7 @@ class NameQualifiableElement extends Element, @namequalifiableelement {
* namespace.
*/
predicate hasGlobalQualifiedName() {
this.getNameQualifier*().getQualifyingElement() instanceof GlobalNamespace
getNameQualifier*().getQualifyingElement() instanceof GlobalNamespace
}
/**
@@ -119,7 +119,7 @@ class NameQualifiableElement extends Element, @namequalifiableelement {
*/
predicate hasSuperQualifiedName() {
exists(NameQualifier nq, SpecialNameQualifyingElement snqe |
nq = this.getNameQualifier*() and
nq = getNameQualifier*() and
namequalifiers(unresolveElement(nq), _, unresolveElement(snqe), _) and
snqe.getName() = "__super"
)
@@ -164,5 +164,5 @@ library class SpecialNameQualifyingElement extends NameQualifyingElement,
/** Gets the name of this special qualifying element. */
override string getName() { specialnamequalifyingelements(underlyingElement(this), result) }
override string toString() { result = this.getName() }
override string toString() { result = getName() }
}

View File

@@ -230,12 +230,8 @@ class GlobalNamespace extends Namespace {
}
/**
* The C++ `std::` namespace and its inline namespaces.
* The C++ `std::` namespace.
*/
class StdNamespace extends Namespace {
StdNamespace() {
this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace
or
this.isInline() and this.getParentNamespace() instanceof StdNamespace
}
StdNamespace() { this.hasName("std") and this.getParentNamespace() instanceof GlobalNamespace }
}

View File

@@ -37,7 +37,7 @@ class NestedFieldAccess extends FieldAccess {
NestedFieldAccess() {
ultimateQualifier = getUltimateQualifier(this) and
this.getTarget() = getANestedField(ultimateQualifier.getType().stripType())
getTarget() = getANestedField(ultimateQualifier.getType().stripType())
}
/**

View File

@@ -27,6 +27,9 @@ 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))
}
@@ -127,7 +130,7 @@ class PrintAstNode extends TPrintAstNode {
// 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 |
this.childAndAccessorPredicate(child, _, nonConvertedIndex, isConverted)
childAndAccessorPredicate(child, _, nonConvertedIndex, isConverted)
|
// Unconverted children come first, then sort by original child index within each group.
child order by isConverted, nonConvertedIndex
@@ -140,7 +143,7 @@ class PrintAstNode extends TPrintAstNode {
*/
private PrintAstNode getConvertedChild(int childIndex) {
exists(Expr expr |
expr = this.getChildInternal(childIndex).(AstNode).getAst() and
expr = getChildInternal(childIndex).(AstNode).getAst() and
expr.getFullyConverted() instanceof Conversion and
result.(AstNode).getAst() = expr.getFullyConverted() and
not expr instanceof Conversion
@@ -152,8 +155,8 @@ class PrintAstNode extends TPrintAstNode {
* at index `childIndex`, if that node has any conversions.
*/
private string getConvertedChildAccessorPredicate(int childIndex) {
exists(this.getConvertedChild(childIndex)) and
result = this.getChildAccessorPredicateInternal(childIndex) + ".getFullyConverted()"
exists(getConvertedChild(childIndex)) and
result = getChildAccessorPredicateInternal(childIndex) + ".getFullyConverted()"
}
/**
@@ -161,12 +164,12 @@ class PrintAstNode extends TPrintAstNode {
* within a function are printed, but the query can override
* `PrintASTConfiguration.shouldPrintFunction` to filter the output.
*/
final predicate shouldPrint() { shouldPrintFunction(this.getEnclosingFunction()) }
final predicate shouldPrint() { shouldPrintFunction(getEnclosingFunction()) }
/**
* Gets the children of this node.
*/
final PrintAstNode getAChild() { result = this.getChild(_) }
final PrintAstNode getAChild() { result = getChild(_) }
/**
* Gets the parent of this node, if any.
@@ -184,7 +187,7 @@ class PrintAstNode extends TPrintAstNode {
*/
string getProperty(string key) {
key = "semmle.label" and
result = this.toString()
result = toString()
}
/**
@@ -198,12 +201,12 @@ class PrintAstNode extends TPrintAstNode {
private predicate childAndAccessorPredicate(
PrintAstNode child, string childPredicate, int nonConvertedIndex, boolean isConverted
) {
child = this.getChildInternal(nonConvertedIndex) and
childPredicate = this.getChildAccessorPredicateInternal(nonConvertedIndex) and
child = getChildInternal(nonConvertedIndex) and
childPredicate = getChildAccessorPredicateInternal(nonConvertedIndex) and
isConverted = false
or
child = this.getConvertedChild(nonConvertedIndex) and
childPredicate = this.getConvertedChildAccessorPredicate(nonConvertedIndex) and
child = getConvertedChild(nonConvertedIndex) and
childPredicate = getConvertedChildAccessorPredicate(nonConvertedIndex) and
isConverted = true
}
@@ -215,7 +218,7 @@ class PrintAstNode extends TPrintAstNode {
// The exact value of `childIndex` doesn't matter, as long as we preserve the correct order.
result =
rank[childIndex](string childPredicate, int nonConvertedIndex, boolean isConverted |
this.childAndAccessorPredicate(_, childPredicate, nonConvertedIndex, isConverted)
childAndAccessorPredicate(_, childPredicate, nonConvertedIndex, isConverted)
|
// Unconverted children come first, then sort by original child index within each group.
childPredicate order by isConverted, nonConvertedIndex
@@ -231,11 +234,12 @@ class PrintAstNode extends TPrintAstNode {
/**
* Gets the `Function` that contains this node.
*/
private Function getEnclosingFunction() {
result = this.getParent*().(FunctionNode).getFunction()
}
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.
*/
@@ -249,7 +253,7 @@ private class PrintableElement extends Element {
}
pragma[noinline]
string getAPrimaryQlClass0() { result = this.getAPrimaryQlClass() }
string getAPrimaryQlClass0() { result = getAPrimaryQlClass() }
}
/**
@@ -277,9 +281,12 @@ abstract class BaseAstNode extends PrintAstNode {
final Locatable getAst() { result = ast }
/** DEPRECATED: Alias for getAst */
deprecated Locatable getAST() { result = this.getAst() }
deprecated Locatable getAST() { result = getAst() }
}
/** DEPRECATED: Alias for BaseAstNode */
deprecated class BaseASTNode = BaseAstNode;
/**
* A node representing an AST node other than a `DeclarationEntry`.
*/
@@ -287,6 +294,9 @@ abstract class AstNode extends BaseAstNode, TAstNode {
AstNode() { this = TAstNode(ast) }
}
/** DEPRECATED: Alias for AstNode */
deprecated class ASTNode = AstNode;
/**
* A node representing an `Expr`.
*/
@@ -301,7 +311,7 @@ class ExprNode extends AstNode {
result = super.getProperty(key)
or
key = "Value" and
result = qlClass(expr) + this.getValue()
result = qlClass(expr) + getValue()
or
key = "Type" and
result = qlClass(expr.getType()) + expr.getType().toString()
@@ -311,7 +321,7 @@ class ExprNode extends AstNode {
}
override string getChildAccessorPredicateInternal(int childIndex) {
result = getChildAccessorWithoutConversions(ast, this.getChildInternal(childIndex).getAst())
result = getChildAccessorWithoutConversions(ast, getChildInternal(childIndex).getAst())
}
/**
@@ -431,7 +441,7 @@ class StmtNode extends AstNode {
}
override string getChildAccessorPredicateInternal(int childIndex) {
result = getChildAccessorWithoutConversions(ast, this.getChildInternal(childIndex).getAst())
result = getChildAccessorWithoutConversions(ast, getChildInternal(childIndex).getAst())
}
}
@@ -507,7 +517,7 @@ class ParametersNode extends PrintAstNode, TParametersNode {
}
override string getChildAccessorPredicateInternal(int childIndex) {
exists(this.getChildInternal(childIndex)) and
exists(getChildInternal(childIndex)) and
result = "getParameter(" + childIndex.toString() + ")"
}
@@ -534,7 +544,7 @@ class ConstructorInitializersNode extends PrintAstNode, TConstructorInitializers
}
final override string getChildAccessorPredicateInternal(int childIndex) {
exists(this.getChildInternal(childIndex)) and
exists(getChildInternal(childIndex)) and
result = "getInitializer(" + childIndex.toString() + ")"
}
@@ -561,7 +571,7 @@ class DestructorDestructionsNode extends PrintAstNode, TDestructorDestructionsNo
}
final override string getChildAccessorPredicateInternal(int childIndex) {
exists(this.getChildInternal(childIndex)) and
exists(getChildInternal(childIndex)) and
result = "getDestruction(" + childIndex.toString() + ")"
}
@@ -618,7 +628,7 @@ class FunctionNode extends AstNode {
override string getProperty(string key) {
result = super.getProperty(key)
or
key = "semmle.order" and result = this.getOrder().toString()
key = "semmle.order" and result = getOrder().toString()
}
/**
@@ -742,13 +752,13 @@ private predicate namedExprChildPredicates(Expr expr, Element ele, string pred)
expr.(VariableAccess).getQualifier() = ele and pred = "getQualifier()"
or
exists(Field f |
expr.(ClassAggregateLiteral).getAFieldExpr(f) = ele and
pred = "getAFieldExpr(" + f.toString() + ")"
expr.(ClassAggregateLiteral).getFieldExpr(f) = ele and
pred = "getFieldExpr(" + f.toString() + ")"
)
or
exists(int n |
expr.(ArrayOrVectorAggregateLiteral).getAnElementExpr(n) = ele and
pred = "getAnElementExpr(" + n.toString() + ")"
expr.(ArrayOrVectorAggregateLiteral).getElementExpr(n) = ele and
pred = "getElementExpr(" + n.toString() + ")"
)
or
expr.(AlignofExprOperator).getExprOperand() = ele and pred = "getExprOperand()"

View File

@@ -1699,28 +1699,7 @@ class AutoType extends TemplateParameter {
private predicate suppressUnusedThis(Type t) { any() }
/**
* A source code location referring to a user-defined type.
*
* Note that only _user-defined_ types have `TypeMention`s. In particular,
* built-in types, and derived types with built-in types as their base don't
* have any `TypeMention`s. For example, given
* ```cpp
* struct S { ... };
* void f(S s1, int i1) {
* S s2;
* S* s3;
* S& s4 = s2;
* decltype(s2) s5;
*
* int i2;
* int* i3;
* int i4[10];
* }
* ```
* there will be a `TypeMention` for the mention of `S` at `S s1`, `S s2`, and `S& s4 = s2`,
* but not at `decltype(s2) s5`. Additionally, there will be no `TypeMention`s for `int`.
*/
/** A source code location referring to a type */
class TypeMention extends Locatable, @type_mention {
override string toString() { result = "type mention" }

View File

@@ -133,7 +133,7 @@ class Variable extends Declaration, @variable {
or
exists(AssignExpr ae | ae.getLValue().(Access).getTarget() = this and result = ae.getRValue())
or
exists(ClassAggregateLiteral l | result = l.getAFieldExpr(this))
exists(ClassAggregateLiteral l | result = l.getFieldExpr(this))
}
/**

View File

@@ -8,7 +8,7 @@ import cpp
*/
deprecated class StrcatFunction extends Function {
StrcatFunction() {
this.getName() =
getName() =
[
"strcat", // strcat(dst, src)
"strncat", // strncat(dst, src, max_amount)

View File

@@ -98,7 +98,7 @@ library class DefOrUse extends ControlFlowNodeBase {
pragma[noinline]
private predicate reaches_helper(boolean isDef, SemanticStackVariable v, BasicBlock bb, int i) {
this.getVariable(isDef) = v and
getVariable(isDef) = v and
bb.getNode(i) = this
}
@@ -118,21 +118,21 @@ library class DefOrUse extends ControlFlowNodeBase {
* predicates are duplicated for now.
*/
exists(BasicBlock bb, int i | this.reaches_helper(isDef, v, bb, i) |
exists(BasicBlock bb, int i | reaches_helper(isDef, v, bb, i) |
exists(int j |
j > i and
(bbDefAt(bb, j, v, defOrUse) or bbUseAt(bb, j, v, defOrUse)) and
not exists(int k | this.firstBarrierAfterThis(isDef, k, v) and k < j)
not exists(int k | firstBarrierAfterThis(isDef, k, v) and k < j)
)
or
not this.firstBarrierAfterThis(isDef, _, v) and
not firstBarrierAfterThis(isDef, _, v) and
bbSuccessorEntryReachesDefOrUse(bb, v, defOrUse, _)
)
}
private predicate firstBarrierAfterThis(boolean isDef, int j, SemanticStackVariable v) {
exists(BasicBlock bb, int i |
this.getVariable(isDef) = v and
getVariable(isDef) = v and
bb.getNode(i) = this and
j = min(int k | bbBarrierAt(bb, k, v, _) and k > i)
)

View File

@@ -14,6 +14,9 @@ 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

View File

@@ -130,7 +130,7 @@ library class SsaHelper extends int {
* Remove any custom phi nodes that are invalid.
*/
private predicate sanitized_custom_phi_node(StackVariable v, BasicBlock b) {
this.custom_phi_node(v, b) and
custom_phi_node(v, b) and
not addressTakenVariable(v) and
not isReferenceVar(v) and
b.isReachable()
@@ -142,7 +142,7 @@ library class SsaHelper extends int {
*/
cached
predicate phi_node(StackVariable v, BasicBlock b) {
this.frontier_phi_node(v, b) or this.sanitized_custom_phi_node(v, b)
frontier_phi_node(v, b) or sanitized_custom_phi_node(v, b)
}
/**
@@ -154,15 +154,14 @@ library class SsaHelper extends int {
*/
private predicate frontier_phi_node(StackVariable v, BasicBlock b) {
exists(BasicBlock x |
dominanceFrontier(x, b) and
this.ssa_defn_rec(pragma[only_bind_into](v), pragma[only_bind_into](x))
dominanceFrontier(x, b) and ssa_defn_rec(pragma[only_bind_into](v), pragma[only_bind_into](x))
) and
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
live_at_start_of_bb(pragma[only_bind_into](v), b)
}
private predicate ssa_defn_rec(StackVariable v, BasicBlock b) {
this.phi_node(v, b)
phi_node(v, b)
or
variableUpdate(v, _, b, _)
}
@@ -173,7 +172,7 @@ library class SsaHelper extends int {
*/
cached
predicate ssa_defn(StackVariable v, ControlFlowNode node, BasicBlock b, int index) {
this.phi_node(v, b) and b.getStart() = node and index = -1
phi_node(v, b) and b.getStart() = node and index = -1
or
variableUpdate(v, node, b, index)
}
@@ -197,7 +196,7 @@ library class SsaHelper extends int {
* basic blocks.
*/
private predicate defUseRank(StackVariable v, BasicBlock b, int rankix, int i) {
i = rank[rankix](int j | this.ssa_defn(v, _, b, j) or ssa_use(v, _, b, j))
i = rank[rankix](int j | ssa_defn(v, _, b, j) or ssa_use(v, _, b, j))
}
/**
@@ -207,7 +206,7 @@ library class SsaHelper extends int {
* the block.
*/
private int lastRank(StackVariable v, BasicBlock b) {
result = max(int rankix | this.defUseRank(v, b, rankix, _)) + 1
result = max(int rankix | defUseRank(v, b, rankix, _)) + 1
}
/**
@@ -216,8 +215,8 @@ library class SsaHelper extends int {
*/
private predicate ssaDefRank(StackVariable v, ControlFlowNode def, BasicBlock b, int rankix) {
exists(int i |
this.ssa_defn(v, def, b, i) and
this.defUseRank(v, b, rankix, i)
ssa_defn(v, def, b, i) and
defUseRank(v, b, rankix, i)
)
}
@@ -233,21 +232,21 @@ library class SsaHelper extends int {
// use is understood to happen _before_ the definition. Phi nodes are
// at rankidx -1 and will therefore always reach the first node in the
// basic block.
this.ssaDefRank(v, def, b, rankix - 1)
ssaDefRank(v, def, b, rankix - 1)
or
this.ssaDefReachesRank(v, def, b, rankix - 1) and
rankix <= this.lastRank(v, b) and // Without this, the predicate would be infinite.
not this.ssaDefRank(v, _, b, rankix - 1) // Range is inclusive of but not past next def.
ssaDefReachesRank(v, def, b, rankix - 1) and
rankix <= lastRank(v, b) and // Without this, the predicate would be infinite.
not ssaDefRank(v, _, b, rankix - 1) // Range is inclusive of but not past next def.
}
/** Holds if SSA variable `(v, def)` reaches the end of block `b`. */
cached
predicate ssaDefinitionReachesEndOfBB(StackVariable v, ControlFlowNode def, BasicBlock b) {
live_at_exit_of_bb(v, b) and this.ssaDefReachesRank(v, def, b, this.lastRank(v, b))
live_at_exit_of_bb(v, b) and ssaDefReachesRank(v, def, b, lastRank(v, b))
or
exists(BasicBlock idom |
this.ssaDefinitionReachesEndOfBB(v, def, idom) and
this.noDefinitionsSinceIDominator(v, idom, b)
ssaDefinitionReachesEndOfBB(v, def, idom) and
noDefinitionsSinceIDominator(v, idom, b)
)
}
@@ -261,7 +260,7 @@ library class SsaHelper extends int {
private predicate noDefinitionsSinceIDominator(StackVariable v, BasicBlock idom, BasicBlock b) {
bbIDominates(idom, b) and // It is sufficient to traverse the dominator graph, cf. discussion above.
live_at_exit_of_bb(v, b) and
not this.ssa_defn(v, _, b, _)
not ssa_defn(v, _, b, _)
}
/**
@@ -270,8 +269,8 @@ library class SsaHelper extends int {
*/
private predicate ssaDefinitionReachesUseWithinBB(StackVariable v, ControlFlowNode def, Expr use) {
exists(BasicBlock b, int rankix, int i |
this.ssaDefReachesRank(v, def, b, rankix) and
this.defUseRank(v, b, rankix, i) and
ssaDefReachesRank(v, def, b, rankix) and
defUseRank(v, b, rankix, i) and
ssa_use(v, use, b, i)
)
}
@@ -280,12 +279,12 @@ library class SsaHelper extends int {
* Holds if SSA variable `(v, def)` reaches the control-flow node `use`.
*/
private predicate ssaDefinitionReaches(StackVariable v, ControlFlowNode def, Expr use) {
this.ssaDefinitionReachesUseWithinBB(v, def, use)
ssaDefinitionReachesUseWithinBB(v, def, use)
or
exists(BasicBlock b |
ssa_use(v, use, b, _) and
this.ssaDefinitionReachesEndOfBB(v, def, b.getAPredecessor()) and
not this.ssaDefinitionReachesUseWithinBB(v, _, use)
ssaDefinitionReachesEndOfBB(v, def, b.getAPredecessor()) and
not ssaDefinitionReachesUseWithinBB(v, _, use)
)
}
@@ -295,10 +294,10 @@ library class SsaHelper extends int {
*/
cached
string toString(ControlFlowNode node, StackVariable v) {
if this.phi_node(v, node)
if phi_node(v, node)
then result = "SSA phi(" + v.getName() + ")"
else (
this.ssa_defn(v, node, _, _) and result = "SSA def(" + v.getName() + ")"
ssa_defn(v, node, _, _) and result = "SSA def(" + v.getName() + ")"
)
}
@@ -308,7 +307,10 @@ library class SsaHelper extends int {
*/
cached
VariableAccess getAUse(ControlFlowNode def, StackVariable v) {
this.ssaDefinitionReaches(v, def, result) and
ssaDefinitionReaches(v, def, result) and
ssa_use(v, result, _, _)
}
}
/** DEPRECATED: Alias for SsaHelper */
deprecated class SSAHelper = SsaHelper;

View File

@@ -25,7 +25,7 @@ import cpp
*/
abstract class StackVariableReachability extends string {
bindingset[this]
StackVariableReachability() { this.length() >= 0 }
StackVariableReachability() { length() >= 0 }
/** Holds if `node` is a source for the reachability analysis using variable `v`. */
abstract predicate isSource(ControlFlowNode node, StackVariable v);
@@ -227,7 +227,7 @@ predicate bbSuccessorEntryReachesLoopInvariant(
*/
abstract class StackVariableReachabilityWithReassignment extends StackVariableReachability {
bindingset[this]
StackVariableReachabilityWithReassignment() { this.length() >= 0 }
StackVariableReachabilityWithReassignment() { length() >= 0 }
/** Override this predicate rather than `isSource` (`isSource` is used internally). */
abstract predicate isSourceActual(ControlFlowNode node, StackVariable v);
@@ -330,7 +330,7 @@ abstract class StackVariableReachabilityWithReassignment extends StackVariableRe
*/
abstract class StackVariableReachabilityExt extends string {
bindingset[this]
StackVariableReachabilityExt() { this.length() >= 0 }
StackVariableReachabilityExt() { length() >= 0 }
/** `node` is a source for the reachability analysis using variable `v`. */
abstract predicate isSource(ControlFlowNode node, StackVariable v);

View File

@@ -1385,6 +1385,9 @@ 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.
@@ -1395,6 +1398,9 @@ private module Cached {
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.
@@ -1404,4 +1410,7 @@ private module Cached {
conditionalSuccessor(n1, false, n2) and
not conditionalSuccessor(n1, true, n2)
}
/** DEPRECATED: Alias for qlCfgFalseSuccessor */
deprecated predicate qlCFGFalseSuccessor = qlCfgFalseSuccessor/2;
}

View File

@@ -2,7 +2,7 @@
* Provides an implementation of global (interprocedural) data flow. This file
* re-exports the local (intraprocedural) data flow analysis from
* `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed
* through the `Global` and `GlobalWithState` modules.
* through the `Make` and `MakeWithState` modules.
*/
private import DataFlowImplCommon
@@ -73,10 +73,10 @@ signature module ConfigSig {
*/
default FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `flowPath`. */
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `flowPath`. */
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
@@ -166,10 +166,10 @@ signature module StateConfigSig {
*/
default FlowFeature getAFeature() { none() }
/** Holds if sources should be grouped in the result of `flowPath`. */
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
default predicate sourceGrouping(Node source, string sourceGroup) { none() }
/** Holds if sinks should be grouped in the result of `flowPath`. */
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
default predicate sinkGrouping(Node sink, string sinkGroup) { none() }
/**
@@ -182,15 +182,15 @@ signature module StateConfigSig {
}
/**
* Gets the exploration limit for `partialFlow` and `partialFlowRev`
* Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev`
* measured in approximate number of interprocedural steps.
*/
signature int explorationLimitSig();
/**
* The output of a global data flow computation.
* The output of a data flow computation.
*/
signature module GlobalFlowSig {
signature module DataFlowSig {
/**
* A `Node` augmented with a call context (except for sinks) and an access path.
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
@@ -203,28 +203,28 @@ signature module GlobalFlowSig {
* The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`.
*/
predicate flowPath(PathNode source, PathNode sink);
predicate hasFlowPath(PathNode source, PathNode sink);
/**
* Holds if data can flow from `source` to `sink`.
*/
predicate flow(Node source, Node sink);
predicate hasFlow(Node source, Node sink);
/**
* Holds if data can flow from some source to `sink`.
*/
predicate flowTo(Node sink);
predicate hasFlowTo(Node sink);
/**
* Holds if data can flow from some source to `sink`.
*/
predicate flowToExpr(DataFlowExpr sink);
predicate hasFlowToExpr(DataFlowExpr sink);
}
/**
* Constructs a global data flow computation.
* Constructs a standard data flow computation.
*/
module Global<ConfigSig Config> implements GlobalFlowSig {
module Make<ConfigSig Config> implements DataFlowSig {
private module C implements FullStateConfigSig {
import DefaultState<Config>
import Config
@@ -233,15 +233,10 @@ module Global<ConfigSig Config> implements GlobalFlowSig {
import Impl<C>
}
/** DEPRECATED: Use `Global` instead. */
deprecated module Make<ConfigSig Config> implements GlobalFlowSig {
import Global<Config>
}
/**
* Constructs a global data flow computation using flow state.
* Constructs a data flow computation using flow state.
*/
module GlobalWithState<StateConfigSig Config> implements GlobalFlowSig {
module MakeWithState<StateConfigSig Config> implements DataFlowSig {
private module C implements FullStateConfigSig {
import Config
}
@@ -249,11 +244,6 @@ module GlobalWithState<StateConfigSig Config> implements GlobalFlowSig {
import Impl<C>
}
/** DEPRECATED: Use `GlobalWithState` instead. */
deprecated module MakeWithState<StateConfigSig Config> implements GlobalFlowSig {
import GlobalWithState<Config>
}
signature class PathNodeSig {
/** Gets a textual representation of this element. */
string toString();
@@ -361,52 +351,3 @@ module MergePathGraph<
}
}
}
/**
* Constructs a `PathGraph` from three `PathGraph`s by disjoint union.
*/
module MergePathGraph3<
PathNodeSig PathNode1, PathNodeSig PathNode2, PathNodeSig PathNode3,
PathGraphSig<PathNode1> Graph1, PathGraphSig<PathNode2> Graph2, PathGraphSig<PathNode3> Graph3>
{
private module MergedInner = MergePathGraph<PathNode1, PathNode2, Graph1, Graph2>;
private module Merged =
MergePathGraph<MergedInner::PathNode, PathNode3, MergedInner::PathGraph, Graph3>;
/** A node in a graph of path explanations that is formed by disjoint union of the three given graphs. */
class PathNode instanceof Merged::PathNode {
/** Gets this as a projection on the first given `PathGraph`. */
PathNode1 asPathNode1() { result = super.asPathNode1().asPathNode1() }
/** Gets this as a projection on the second given `PathGraph`. */
PathNode2 asPathNode2() { result = super.asPathNode1().asPathNode2() }
/** Gets this as a projection on the third given `PathGraph`. */
PathNode3 asPathNode3() { result = super.asPathNode2() }
/** Gets a textual representation of this element. */
string toString() { result = super.toString() }
/**
* Holds if this element is at the specified location.
* The location spans column `startcolumn` of line `startline` to
* column `endcolumn` of line `endline` in file `filepath`.
* For more information, see
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
*/
predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
}
/** Gets the underlying `Node`. */
Node getNode() { result = super.getNode() }
}
/**
* Provides the query predicates needed to include a graph in a path-problem query.
*/
module PathGraph = Merged::PathGraph;
}

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