mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Merge branch 'main' into python-clean-up-import-resolution
This commit is contained in:
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: LGTM.com - false positive
|
||||
about: Tell us about an alert that shouldn't be reported
|
||||
title: LGTM.com - false positive
|
||||
labels: false-positive
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description of the false positive**
|
||||
|
||||
<!-- Please explain briefly why you think it shouldn't be included. -->
|
||||
|
||||
**URL to the alert on the project page on LGTM.com**
|
||||
|
||||
<!--
|
||||
1. Open the project on LGTM.com.
|
||||
For example, https://lgtm.com/projects/g/pallets/click/.
|
||||
2. Switch to the `Alerts` tab. For example, https://lgtm.com/projects/g/pallets/click/alerts/.
|
||||
3. Scroll to the alert that you would like to report.
|
||||
4. Click on the right most icon `View this alert within the complete file`.
|
||||
5. A new browser tab opens. Copy and paste the page URL here.
|
||||
For example, https://lgtm.com/projects/g/pallets/click/snapshot/719fb7d8322b0767cdd1e5903ba3eb3233ba8dd5/files/click/_winconsole.py#xa08d213ab3289f87:1.
|
||||
-->
|
||||
36
.github/ISSUE_TEMPLATE/ql--false-positive.md
vendored
Normal file
36
.github/ISSUE_TEMPLATE/ql--false-positive.md
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: CodeQL False positive
|
||||
about: Report CodeQL alerts that you think should not have been detected (not applicable, not exploitable, etc.)
|
||||
title: False positive
|
||||
labels: false-positive
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Description of the false positive**
|
||||
|
||||
<!-- Please explain briefly why you think it shouldn't be included. -->
|
||||
|
||||
**Code samples or links to source code**
|
||||
|
||||
<!--
|
||||
For open source code: file links with line numbers on GitHub, for example:
|
||||
https://github.com/github/codeql/blob/dc440aaee6695deb0d9676b87e06ea984e1b4ae5/javascript/ql/test/query-tests/Security/CWE-078/CommandInjection/exec-sh2.js#L10
|
||||
|
||||
For closed source code: (redacted) code samples that illustrate the problem, for example:
|
||||
|
||||
```
|
||||
function execSh(command, options) {
|
||||
return cp.spawn(getShell(), ["-c", command], options) // <- command line injection
|
||||
};
|
||||
```
|
||||
-->
|
||||
|
||||
**URL to the alert on GitHub code scanning (optional)**
|
||||
|
||||
<!--
|
||||
1. Open the project on GitHub.com.
|
||||
2. Switch to the `Security` tab.
|
||||
3. Browse to the alert that you would like to report.
|
||||
4. Copy and paste the page URL here.
|
||||
-->
|
||||
18
.github/actions/fetch-codeql/action.yml
vendored
18
.github/actions/fetch-codeql/action.yml
vendored
@@ -1,14 +1,22 @@
|
||||
name: Fetch CodeQL
|
||||
description: Fetches the latest version of CodeQL
|
||||
|
||||
inputs:
|
||||
channel:
|
||||
description: 'The CodeQL channel to use'
|
||||
required: false
|
||||
default: 'nightly'
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Fetch CodeQL
|
||||
shell: bash
|
||||
run: |
|
||||
gh extension install github/gh-codeql
|
||||
gh codeql set-channel nightly
|
||||
gh codeql version
|
||||
gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
CHANNEL: ${{ inputs.channel }}
|
||||
run: |
|
||||
gh extension install github/gh-codeql
|
||||
gh codeql set-channel "$CHANNEL"
|
||||
gh codeql version
|
||||
gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}"
|
||||
|
||||
11
.github/labeler.yml
vendored
11
.github/labeler.yml
vendored
@@ -43,3 +43,14 @@ documentation:
|
||||
"QL-for-QL":
|
||||
- ql/**/*
|
||||
- .github/workflows/ql-for-ql*
|
||||
|
||||
# Since these are all shared files that need to be synced, just pick _one_ copy of each.
|
||||
"DataFlow Library":
|
||||
- "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll"
|
||||
- "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll"
|
||||
- "java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
|
||||
- "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll"
|
||||
- "java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll"
|
||||
|
||||
"ATM":
|
||||
- javascript/ql/experimental/adaptivethreatmodeling/**/*
|
||||
|
||||
93
.github/workflows/atm-check-query-suite.yml
vendored
Normal file
93
.github/workflows/atm-check-query-suite.yml
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
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
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
with:
|
||||
channel: release
|
||||
|
||||
- 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 \
|
||||
--format sarif-latest \
|
||||
--output "${SARIF_PATH}" \
|
||||
--sarif-group-rules-by-pack \
|
||||
-vv \
|
||||
-- \
|
||||
"${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
|
||||
12
.github/workflows/atm-model-integration-tests.yml
vendored
Normal file
12
.github/workflows/atm-model-integration-tests.yml
vendored
Normal 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"
|
||||
71
.github/workflows/compile-queries.yml
vendored
Normal file
71
.github/workflows/compile-queries.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
name: "Compile all queries using the latest stable CodeQL CLI"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: # makes sure the cache gets populated - running on the branches people tend to merge into.
|
||||
- main
|
||||
- "rc/*"
|
||||
- "codeql-cli-*"
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
compile-queries:
|
||||
runs-on: ubuntu-latest-xl
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
# calculate the merge-base with main, in a way that works both on PRs and pushes to main.
|
||||
- name: Calculate merge-base
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
env:
|
||||
BASE_BRANCH: ${{ github.base_ref }}
|
||||
run: |
|
||||
MERGE_BASE=$(git cat-file commit $GITHUB_SHA | grep '^parent ' | head -1 | cut -f 2 -d " ")
|
||||
echo "merge-base=$MERGE_BASE" >> $GITHUB_ENV
|
||||
- name: Read CodeQL query compilation - PR
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '*/ql/src/.cache'
|
||||
key: codeql-compile-pr-${{ github.sha }} # deliberately not using the `compile-compile-main` keys here.
|
||||
restore-keys: |
|
||||
codeql-compile-${{ github.base_ref }}-${{ env.merge-base }}
|
||||
codeql-compile-${{ github.base_ref }}-
|
||||
codeql-compile-main-
|
||||
- name: Fill CodeQL query compilation cache - main
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: '*/ql/src/.cache'
|
||||
key: codeql-compile-${{ github.ref_name }}-${{ github.sha }} # just fill on main
|
||||
restore-keys: | # restore from another random commit, to speed up compilation.
|
||||
codeql-compile-${{ github.ref_name }}-
|
||||
codeql-compile-main-
|
||||
- name: Setup CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
with:
|
||||
channel: 'release'
|
||||
- name: check formatting
|
||||
run: find */ql -type f \( -name "*.qll" -o -name "*.ql" \) -print0 | xargs -0 codeql query format --check-only
|
||||
- name: compile queries - check-only
|
||||
# run with --check-only if running in a PR (github.sha != main)
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
shell: bash
|
||||
run: codeql query compile -j0 */ql/src --keep-going --warnings=error --check-only
|
||||
- name: compile queries - full
|
||||
# do full compile if running on main - this populates the cache
|
||||
if : ${{ github.event_name != 'pull_request' }}
|
||||
shell: bash
|
||||
run: |
|
||||
# Move all the existing cache into another folder, so we only preserve the cache for the current queries.
|
||||
mkdir -p ${COMBINED_CACHE_DIR}
|
||||
rm */ql/src/.cache/{lock,size}
|
||||
# copy the contents of the .cache folders into the combined cache folder.
|
||||
cp -r */ql/src/.cache/* ${COMBINED_CACHE_DIR}/
|
||||
# clean up the .cache folders
|
||||
rm -rf */ql/src/.cache/*
|
||||
|
||||
# compile the queries
|
||||
codeql query compile -j0 */ql/src --keep-going --warnings=error --compilation-cache ${COMBINED_CACHE_DIR}
|
||||
env:
|
||||
COMBINED_CACHE_DIR: ${{ github.workspace }}/compilation-dir
|
||||
13
.github/workflows/js-ml-tests.yml
vendored
13
.github/workflows/js-ml-tests.yml
vendored
@@ -23,19 +23,6 @@ defaults:
|
||||
working-directory: javascript/ql/experimental/adaptivethreatmodeling
|
||||
|
||||
jobs:
|
||||
qlformat:
|
||||
name: Check QL formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Check QL formatting
|
||||
run: |
|
||||
find . "(" -name "*.ql" -or -name "*.qll" ")" -print0 | \
|
||||
xargs -0 codeql query format --check-only
|
||||
|
||||
qlcompile:
|
||||
name: Check QL compilation
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
8
.github/workflows/ql-for-ql-build.yml
vendored
8
.github/workflows/ql-for-ql-build.yml
vendored
@@ -24,13 +24,13 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- name: Get CodeQL version
|
||||
id: get-codeql-version
|
||||
run: |
|
||||
echo "::set-output name=version::$("${CODEQL}" --version | head -n 1 | rev | cut -d " " -f 1 | rev)"
|
||||
echo "version=$("${CODEQL}" --version | head -n 1 | rev | cut -d " " -f 1 | rev)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
@@ -133,7 +133,7 @@ jobs:
|
||||
env:
|
||||
CONF: ./ql-for-ql-config.yml
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: ql
|
||||
db-location: ${{ runner.temp }}/db
|
||||
@@ -145,7 +145,7 @@ jobs:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
|
||||
uses: github/codeql-action/analyze@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
category: "ql-for-ql"
|
||||
- name: Copy sarif file to CWD
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: actions/cache@v3
|
||||
|
||||
2
.github/workflows/ql-for-ql-tests.yml
vendored
2
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: actions/cache@v3
|
||||
|
||||
4
.github/workflows/ruby-build.yml
vendored
4
.github/workflows/ruby-build.yml
vendored
@@ -96,8 +96,8 @@ jobs:
|
||||
- name: Build Query Pack
|
||||
run: |
|
||||
codeql pack create ../shared/ssa --output target/packs
|
||||
codeql pack create ../misc/suite-helpers --output target/packs
|
||||
codeql pack create ql/lib --output target/packs
|
||||
codeql pack install ql/src
|
||||
codeql pack create ql/src --output target/packs
|
||||
PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*)
|
||||
codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
|
||||
@@ -202,7 +202,7 @@ jobs:
|
||||
echo 'name: sample-tests
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
codeql/ruby-all: 0.0.1
|
||||
codeql/ruby-all: "*"
|
||||
extractor: ruby
|
||||
tests: .
|
||||
' > qlpack.yml
|
||||
|
||||
7
.github/workflows/ruby-qltest.yml
vendored
7
.github/workflows/ruby-qltest.yml
vendored
@@ -28,13 +28,6 @@ defaults:
|
||||
working-directory: ruby
|
||||
|
||||
jobs:
|
||||
qlformat:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- name: Check QL formatting
|
||||
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
|
||||
qlcompile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
39
.github/workflows/swift-codegen.yml
vendored
39
.github/workflows/swift-codegen.yml
vendored
@@ -1,39 +0,0 @@
|
||||
name: "Swift: Check code generation"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "swift/**"
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift-codegen.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
codegen:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: bazelbuild/setup-bazelisk@v2
|
||||
- uses: actions/setup-python@v3
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
name: Check that python code is properly formatted
|
||||
with:
|
||||
extra_args: autopep8 --all-files
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
bazel test //swift/codegen/test --test_output=errors
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
name: Check that QL generated code was checked in
|
||||
with:
|
||||
extra_args: swift-codegen --all-files
|
||||
- name: Generate C++ files
|
||||
run: |
|
||||
bazel run //swift/codegen:codegen -- --generate=trap,cpp --cpp-output=$PWD/swift-generated-cpp-files
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: swift-generated-cpp-files
|
||||
path: swift-generated-cpp-files/**
|
||||
45
.github/workflows/swift-integration-tests.yml
vendored
45
.github/workflows/swift-integration-tests.yml
vendored
@@ -1,45 +0,0 @@
|
||||
name: "Swift: Run Integration Tests"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "swift/**"
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift-integration-tests.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
branches:
|
||||
- main
|
||||
defaults:
|
||||
run:
|
||||
working-directory: swift
|
||||
|
||||
jobs:
|
||||
integration-tests:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-20.04
|
||||
# - macos-latest TODO
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: bazelbuild/setup-bazelisk@v2
|
||||
- uses: actions/setup-python@v3
|
||||
- name: Build Swift extractor
|
||||
run: |
|
||||
bazel run //swift:create-extractor-pack
|
||||
- name: Get Swift version
|
||||
id: get_swift_version
|
||||
run: |
|
||||
VERSION=$(bazel run //swift/extractor -- --version | sed -ne 's/.*version \(\S*\).*/\1/p')
|
||||
echo "::set-output name=version::$VERSION"
|
||||
- uses: swift-actions/setup-swift@v1
|
||||
with:
|
||||
swift-version: "${{steps.get_swift_version.outputs.version}}"
|
||||
- name: Run integration tests
|
||||
run: |
|
||||
python integration-tests/runner.py
|
||||
43
.github/workflows/swift-qltest.yml
vendored
43
.github/workflows/swift-qltest.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: "Swift: Run QL Tests"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "swift/**"
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift-qltest.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
branches:
|
||||
- main
|
||||
defaults:
|
||||
run:
|
||||
working-directory: swift
|
||||
|
||||
jobs:
|
||||
qlformat:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- name: Check QL formatting
|
||||
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
|
||||
qltest:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os : [ubuntu-20.04, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: bazelbuild/setup-bazelisk@v2
|
||||
- name: Build Swift extractor
|
||||
run: |
|
||||
bazel run //swift:create-extractor-pack
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
codeql test run --threads=0 --ram 5000 --search-path "${{ github.workspace }}/swift/extractor-pack" --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition ql/test
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
120
.github/workflows/swift.yml
vendored
Normal file
120
.github/workflows/swift.yml
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
name: "Swift"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- "swift/**"
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- .github/workflows/swift.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
- .pre-commit-config.yaml
|
||||
- "!**/*.md"
|
||||
- "!**/*.qhelp"
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
codegen: ${{ steps.filter.outputs.codegen }}
|
||||
ql: ${{ steps.filter.outputs.ql }}
|
||||
steps:
|
||||
- uses: dorny/paths-filter@4512585405083f25c027a35db413c2b3b9006d50
|
||||
id: filter
|
||||
with:
|
||||
filters: |
|
||||
codegen:
|
||||
- '.github/workflows/swift.yml'
|
||||
- "misc/bazel/**"
|
||||
- "*.bazel*"
|
||||
- 'swift/actions/setup-env/**'
|
||||
- '.pre-commit-config.yaml'
|
||||
- 'swift/codegen/**'
|
||||
- 'swift/schema.py'
|
||||
- 'swift/**/*.dbscheme'
|
||||
- 'swift/ql/lib/codeql/swift/elements.qll'
|
||||
- 'swift/ql/lib/codeql/swift/elements/**'
|
||||
- 'swift/ql/lib/codeql/swift/generated/**'
|
||||
- 'swift/ql/test/extractor-tests/generated/**'
|
||||
ql:
|
||||
- 'github/workflows/swift.yml'
|
||||
- 'swift/**/*.ql'
|
||||
- 'swift/**/*.qll'
|
||||
# not using a matrix as you cannot depend on a specific job in a matrix, and we want to start linux checks
|
||||
# without waiting for the macOS build
|
||||
build-and-test-macos:
|
||||
runs-on: macos-12-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/create-extractor-pack
|
||||
- uses: ./swift/actions/run-quick-tests
|
||||
- uses: ./swift/actions/print-unextracted
|
||||
build-and-test-linux:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/create-extractor-pack
|
||||
- uses: ./swift/actions/run-quick-tests
|
||||
- uses: ./swift/actions/print-unextracted
|
||||
qltests-linux:
|
||||
needs: build-and-test-linux
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-ql-tests
|
||||
qltests-macos:
|
||||
needs: build-and-test-macos
|
||||
runs-on: macos-12-xl
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
slice: ["1/2", "2/2"]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-ql-tests
|
||||
with:
|
||||
flags: --slice ${{ matrix.slice }}
|
||||
integration-tests-linux:
|
||||
needs: build-and-test-linux
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-integration-tests
|
||||
integration-tests-macos:
|
||||
needs: build-and-test-macos
|
||||
runs-on: macos-12-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-integration-tests
|
||||
codegen:
|
||||
runs-on: ubuntu-latest
|
||||
needs: changes
|
||||
if: ${{ needs.changes.outputs.codegen == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/setup-env
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
name: Check that python code is properly formatted
|
||||
with:
|
||||
extra_args: autopep8 --all-files
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
name: Check that QL generated code was checked in
|
||||
with:
|
||||
extra_args: swift-codegen --all-files
|
||||
- name: Generate C++ files
|
||||
run: |
|
||||
bazel run //swift/codegen:codegen -- --generate=trap,cpp --cpp-output=$PWD/generated-cpp-files
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: swift-generated-cpp-files
|
||||
path: generated-cpp-files/**
|
||||
database-upgrade-scripts:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: ./swift/actions/database-upgrade-scripts
|
||||
@@ -19,7 +19,7 @@ repos:
|
||||
rev: v1.6.0
|
||||
hooks:
|
||||
- id: autopep8
|
||||
files: ^swift/codegen/.*\.py
|
||||
files: ^swift/.*\.py
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
@@ -31,7 +31,7 @@ repos:
|
||||
|
||||
- id: sync-files
|
||||
name: Fix files required to be identical
|
||||
files: \.(qll?|qhelp|swift)$
|
||||
files: \.(qll?|qhelp|swift)$|^config/identical-files\.json$
|
||||
language: system
|
||||
entry: python3 config/sync-files.py --latest
|
||||
pass_filenames: false
|
||||
@@ -44,7 +44,7 @@ repos:
|
||||
|
||||
- id: swift-codegen
|
||||
name: Run Swift checked in code generation
|
||||
files: ^swift/(codegen/|.*/generated/|ql/lib/(swift\.dbscheme$|codeql/swift/elements))
|
||||
files: ^swift/(schema.py$|codegen/|.*/generated/|ql/lib/(swift\.dbscheme$|codeql/swift/elements))
|
||||
language: system
|
||||
entry: bazel run //swift/codegen -- --quiet
|
||||
pass_filenames: false
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
|
||||
|
||||
# CodeQL tools and associated docs
|
||||
/docs/codeql-cli/ @github/codeql-cli-reviewers
|
||||
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||
/docs/ql-language-reference/ @github/codeql-frontend-reviewers
|
||||
/docs/codeql/codeql-cli/ @github/codeql-cli-reviewers
|
||||
/docs/codeql/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||
/docs/codeql/ql-language-reference/ @github/codeql-frontend-reviewers
|
||||
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
|
||||
|
||||
# QL for QL reviewers
|
||||
@@ -40,8 +40,9 @@ WORKSPACE.bazel @github/codeql-ci-reviewers
|
||||
|
||||
# Workflows
|
||||
/.github/workflows/ @github/codeql-ci-reviewers
|
||||
/.github/workflows/atm-* @github/codeql-ml-powered-queries-reviewers
|
||||
/.github/workflows/go-* @github/codeql-go
|
||||
/.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers
|
||||
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
|
||||
/.github/workflows/ruby-* @github/codeql-ruby
|
||||
/.github/workflows/swift-* @github/codeql-c
|
||||
/.github/workflows/swift.yml @github/codeql-c
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
| Unneeded defensive code | More true positive and fewer false positive results | This query now recognizes additional defensive code patterns. |
|
||||
| Unsafe dynamic method access | Fewer false positive results | This query no longer flags concatenated strings as unsafe method names. |
|
||||
| Unused parameter | Fewer false positive results | This query no longer flags parameters with leading underscore. |
|
||||
| Unused variable, import, function or class | Fewer false positive results | This query now flags fewer variables that are implictly used by JSX elements. It no longer flags variables with a leading underscore and variables in dead code. |
|
||||
| Unused variable, import, function or class | Fewer false positive results | This query now flags fewer variables that are implicitly used by JSX elements. It no longer flags variables with a leading underscore and variables in dead code. |
|
||||
| Unvalidated dynamic method call | More true positive results | This query now flags concatenated strings as unvalidated method names in more cases. |
|
||||
| Useless assignment to property. | Fewer false positive results | This query now treats assignments with complex right-hand sides correctly. |
|
||||
| Useless conditional | Fewer results | Additional defensive coding patterns are now ignored. |
|
||||
|
||||
@@ -19,7 +19,7 @@ The following changes in version 1.23 affect C/C++ analysis in all applications.
|
||||
| Hard-coded Japanese era start date in call (`cpp/japanese-era/constructor-or-method-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
|
||||
| Hard-coded Japanese era start date in struct (`cpp/japanese-era/struct-with-exact-era-date`) | Deprecated | This query has been deprecated. Use the new combined query Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) instead. |
|
||||
| Hard-coded Japanese era start date (`cpp/japanese-era/exact-era-date`) | More correct results | This query now checks for the beginning date of the Reiwa era (1st May 2019). |
|
||||
| Non-constant format string (`cpp/non-constant-format`) | Fewer false positive results | Fixed false positive results triggrered by mismatching declarations of a formatting function. |
|
||||
| Non-constant format string (`cpp/non-constant-format`) | Fewer false positive results | Fixed false positive results triggered by mismatching declarations of a formatting function. |
|
||||
| Sign check of bitwise operation (`cpp/bitwise-sign-check`) | Fewer false positive results | Results involving `>=` or `<=` are no longer reported. |
|
||||
| Too few arguments to formatting function (`cpp/wrong-number-format-arguments`) | Fewer false positive results | Fixed false positive results triggered by mismatching declarations of a formatting function. |
|
||||
| Too many arguments to formatting function (`cpp/too-many-format-arguments`) | Fewer false positive results | Fixed false positive results triggered by mismatching declarations of a formatting function. |
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
|
||||
## Changes to libraries
|
||||
|
||||
* The predicates `RegExpTerm.getSuccessor` and `RegExpTerm.getPredecessor` have been changed to reflect textual, not operational, matching order. This only makes a difference in lookbehind assertions, which are operationally matched backwards. Previously, `getSuccessor` would mimick this, so in an assertion `(?<=ab)` the term `b` would be considered the predecessor, not the successor, of `a`. Textually, however, `a` is still matched before `b`, and this is the order we now follow.
|
||||
* The predicates `RegExpTerm.getSuccessor` and `RegExpTerm.getPredecessor` have been changed to reflect textual, not operational, matching order. This only makes a difference in lookbehind assertions, which are operationally matched backwards. Previously, `getSuccessor` would mimic this, so in an assertion `(?<=ab)` the term `b` would be considered the predecessor, not the successor, of `a`. Textually, however, `a` is still matched before `b`, and this is the order we now follow.
|
||||
* An extensible model of the `EventEmitter` pattern has been implemented.
|
||||
* Taint-tracking configurations now interact differently with the `data` flow label, which may affect queries
|
||||
that combine taint-tracking and flow labels.
|
||||
|
||||
@@ -17,6 +17,7 @@ provide:
|
||||
# - "javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml"
|
||||
- "csharp/ql/campaigns/Solorigate/lib/qlpack.yml"
|
||||
- "csharp/ql/campaigns/Solorigate/src/qlpack.yml"
|
||||
- "csharp/ql/campaigns/Solorigate/test/qlpack.yml"
|
||||
|
||||
21
config/atm/ml-powered-queries-repo/add-note.js
Normal file
21
config/atm/ml-powered-queries-repo/add-note.js
Normal file
@@ -0,0 +1,21 @@
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
Logger = require('./logger').Logger;
|
||||
Note = require('./models/note').Note;
|
||||
|
||||
(async () => {
|
||||
if (process.argv.length != 5) {
|
||||
Logger.log("Creates a private note. Usage: node add-note.js <token> <title> <body>")
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the default mongoose connection
|
||||
await mongoose.connect('mongodb://localhost:27017/notes', { useFindAndModify: false });
|
||||
|
||||
const [userToken, title, body] = process.argv.slice(2);
|
||||
await Note.create({ title, body, userToken });
|
||||
|
||||
Logger.log(`Created private note with title ${title} and body ${body} belonging to user with token ${userToken}.`);
|
||||
|
||||
await mongoose.connection.close();
|
||||
})();
|
||||
68
config/atm/ml-powered-queries-repo/app.js
Normal file
68
config/atm/ml-powered-queries-repo/app.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const bodyParser = require('body-parser');
|
||||
const express = require('express');
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
const notesApi = require('./notes-api');
|
||||
const usersApi = require('./users-api');
|
||||
|
||||
const addSampleData = module.exports.addSampleData = async () => {
|
||||
const [userA, userB] = await User.create([
|
||||
{
|
||||
name: "A",
|
||||
token: "tokenA"
|
||||
},
|
||||
{
|
||||
name: "B",
|
||||
token: "tokenB"
|
||||
}
|
||||
]);
|
||||
|
||||
await Note.create([
|
||||
{
|
||||
title: "Public note belonging to A",
|
||||
body: "This is a public note belonging to A",
|
||||
isPublic: true,
|
||||
ownerToken: userA.token
|
||||
},
|
||||
{
|
||||
title: "Public note belonging to B",
|
||||
body: "This is a public note belonging to B",
|
||||
isPublic: true,
|
||||
ownerToken: userB.token
|
||||
},
|
||||
{
|
||||
title: "Private note belonging to A",
|
||||
body: "This is a private note belonging to A",
|
||||
ownerToken: userA.token
|
||||
},
|
||||
{
|
||||
title: "Private note belonging to B",
|
||||
body: "This is a private note belonging to B",
|
||||
ownerToken: userB.token
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
module.exports.startApp = async () => {
|
||||
// Open the default mongoose connection
|
||||
await mongoose.connect('mongodb://mongo:27017/notes', { useFindAndModify: false });
|
||||
// Drop contents of DB
|
||||
mongoose.connection.dropDatabase();
|
||||
// Add some sample data
|
||||
await addSampleData();
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded());
|
||||
|
||||
app.get('/', async (_req, res) => {
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.use('/api/notes', notesApi.router);
|
||||
app.use('/api/users', usersApi.router);
|
||||
|
||||
app.listen(3000);
|
||||
Logger.log('Express started on port 3000');
|
||||
};
|
||||
7
config/atm/ml-powered-queries-repo/index.js
Normal file
7
config/atm/ml-powered-queries-repo/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const startApp = require('./app').startApp;
|
||||
|
||||
Logger = require('./logger').Logger;
|
||||
Note = require('./models/note').Note;
|
||||
User = require('./models/user').User;
|
||||
|
||||
startApp();
|
||||
5
config/atm/ml-powered-queries-repo/logger.js
Normal file
5
config/atm/ml-powered-queries-repo/logger.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports.Logger = class {
|
||||
log(message, ...objs) {
|
||||
console.log(message, objs);
|
||||
}
|
||||
};
|
||||
8
config/atm/ml-powered-queries-repo/models/note.js
Normal file
8
config/atm/ml-powered-queries-repo/models/note.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
module.exports.Note = mongoose.model('Note', new mongoose.Schema({
|
||||
title: String,
|
||||
body: String,
|
||||
ownerToken: String,
|
||||
isPublic: Boolean
|
||||
}));
|
||||
6
config/atm/ml-powered-queries-repo/models/user.js
Normal file
6
config/atm/ml-powered-queries-repo/models/user.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
module.exports.User = mongoose.model('User', new mongoose.Schema({
|
||||
name: String,
|
||||
token: String
|
||||
}));
|
||||
44
config/atm/ml-powered-queries-repo/notes-api.js
Normal file
44
config/atm/ml-powered-queries-repo/notes-api.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const express = require('express')
|
||||
|
||||
const router = module.exports.router = express.Router();
|
||||
|
||||
function serializeNote(note) {
|
||||
return {
|
||||
title: note.title,
|
||||
body: note.body
|
||||
};
|
||||
}
|
||||
|
||||
router.post('/find', async (req, res) => {
|
||||
const notes = await Note.find({
|
||||
ownerToken: req.body.token
|
||||
}).exec();
|
||||
res.json({
|
||||
notes: notes.map(serializeNote)
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/findPublic', async (_req, res) => {
|
||||
const notes = await Note.find({
|
||||
isPublic: true
|
||||
}).exec();
|
||||
res.json({
|
||||
notes: notes.map(serializeNote)
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/findVisible', async (req, res) => {
|
||||
const notes = await Note.find({
|
||||
$or: [
|
||||
{
|
||||
isPublic: true
|
||||
},
|
||||
{
|
||||
ownerToken: req.body.token
|
||||
}
|
||||
]
|
||||
}).exec();
|
||||
res.json({
|
||||
notes: notes.map(serializeNote)
|
||||
});
|
||||
});
|
||||
37
config/atm/ml-powered-queries-repo/read-notes.js
Normal file
37
config/atm/ml-powered-queries-repo/read-notes.js
Normal file
@@ -0,0 +1,37 @@
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
Logger = require('./logger').Logger;
|
||||
Note = require('./models/note').Note;
|
||||
User = require('./models/user').User;
|
||||
|
||||
(async () => {
|
||||
if (process.argv.length != 3) {
|
||||
Logger.log("Outputs all notes visible to a user. Usage: node read-notes.js <token>")
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the default mongoose connection
|
||||
await mongoose.connect('mongodb://localhost:27017/notes', { useFindAndModify: false });
|
||||
|
||||
const ownerToken = process.argv[2];
|
||||
|
||||
const user = await User.findOne({
|
||||
token: ownerToken
|
||||
}).exec();
|
||||
|
||||
const notes = await Note.find({
|
||||
$or: [
|
||||
{ isPublic: true },
|
||||
{ ownerToken }
|
||||
]
|
||||
}).exec();
|
||||
|
||||
notes.map(note => {
|
||||
Logger.log("Title:" + note.title);
|
||||
Logger.log("By:" + user.name);
|
||||
Logger.log("Body:" + note.body);
|
||||
Logger.log();
|
||||
});
|
||||
|
||||
await mongoose.connection.close();
|
||||
})();
|
||||
25
config/atm/ml-powered-queries-repo/users-api.js
Normal file
25
config/atm/ml-powered-queries-repo/users-api.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const express = require('express')
|
||||
|
||||
Logger = require('./logger').Logger;
|
||||
const router = module.exports.router = express.Router();
|
||||
|
||||
router.post('/updateName', async (req, res) => {
|
||||
Logger.log("/updateName called with new name", req.body.name);
|
||||
await User.findOneAndUpdate({
|
||||
token: req.body.token
|
||||
}, {
|
||||
name: req.body.name
|
||||
}).exec();
|
||||
res.json({
|
||||
name: req.body.name
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/getName', async (req, res) => {
|
||||
const user = await User.findOne({
|
||||
token: req.body.token
|
||||
}).exec();
|
||||
res.json({
|
||||
name: user.name
|
||||
});
|
||||
});
|
||||
@@ -94,8 +94,8 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
|
||||
],
|
||||
"Model as Data Generation Java/C# - CaptureModels": [
|
||||
"java/ql/src/utils/model-generator/internal/CaptureModels.qll",
|
||||
"csharp/ql/src/utils/model-generator/internal/CaptureModels.qll"
|
||||
"java/ql/src/utils/modelgenerator/internal/CaptureModels.qll",
|
||||
"csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll"
|
||||
],
|
||||
"Sign Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
|
||||
@@ -486,40 +486,6 @@
|
||||
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll"
|
||||
],
|
||||
"ReDoS Util Python/JS/Ruby/Java": [
|
||||
"javascript/ql/lib/semmle/javascript/security/regexp/NfaUtils.qll",
|
||||
"python/ql/lib/semmle/python/security/regexp/NfaUtils.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/regexp/NfaUtils.qll",
|
||||
"java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll"
|
||||
],
|
||||
"ReDoS Exponential Python/JS/Ruby/Java": [
|
||||
"javascript/ql/lib/semmle/javascript/security/regexp/ExponentialBackTracking.qll",
|
||||
"python/ql/lib/semmle/python/security/regexp/ExponentialBackTracking.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/regexp/ExponentialBackTracking.qll",
|
||||
"java/ql/lib/semmle/code/java/security/regexp/ExponentialBackTracking.qll"
|
||||
],
|
||||
"ReDoS Polynomial Python/JS/Ruby/Java": [
|
||||
"javascript/ql/lib/semmle/javascript/security/regexp/SuperlinearBackTracking.qll",
|
||||
"python/ql/lib/semmle/python/security/regexp/SuperlinearBackTracking.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/regexp/SuperlinearBackTracking.qll",
|
||||
"java/ql/lib/semmle/code/java/security/regexp/SuperlinearBackTracking.qll"
|
||||
],
|
||||
"RegexpMatching Python/JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/regexp/RegexpMatching.qll",
|
||||
"python/ql/lib/semmle/python/security/regexp/RegexpMatching.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/regexp/RegexpMatching.qll"
|
||||
],
|
||||
"BadTagFilterQuery Python/JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll",
|
||||
"python/ql/lib/semmle/python/security/BadTagFilterQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/BadTagFilterQuery.qll"
|
||||
],
|
||||
"OverlyLargeRange Python/JS/Ruby/Java": [
|
||||
"javascript/ql/lib/semmle/javascript/security/OverlyLargeRangeQuery.qll",
|
||||
"python/ql/lib/semmle/python/security/OverlyLargeRangeQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/OverlyLargeRangeQuery.qll",
|
||||
"java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll"
|
||||
],
|
||||
"CFG": [
|
||||
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
|
||||
"ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll",
|
||||
@@ -600,8 +566,12 @@
|
||||
"swift/ql/test/extractor-tests/patterns/patterns.swift",
|
||||
"swift/ql/test/library-tests/ast/patterns.swift"
|
||||
],
|
||||
"Swift control flow test file": [
|
||||
"swift/ql/test/library-tests/controlflow/graph/cfg.swift",
|
||||
"swift/ql/test/library-tests/ast/cfg.swift"
|
||||
],
|
||||
"IncompleteMultiCharacterSanitization JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,11 +11,12 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -3,4 +3,4 @@ groups:
|
||||
- cpp
|
||||
- examples
|
||||
dependencies:
|
||||
codeql/cpp-all: "*"
|
||||
codeql/cpp-all: ${workspace}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
## 0.4.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Fixed bugs in the `FormatLiteral` class that were causing `getMaxConvertedLength` and related predicates to return no results when the format literal was `%e`, `%f` or `%g` and an explicit precision was specified.
|
||||
|
||||
## 0.4.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
cpp/ql/lib/change-notes/released/0.4.2.md
Normal file
3
cpp/ql/lib/change-notes/released/0.4.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.4.2
|
||||
|
||||
No user-facing changes.
|
||||
5
cpp/ql/lib/change-notes/released/0.4.3.md
Normal file
5
cpp/ql/lib/change-notes/released/0.4.3.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.4.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Fixed bugs in the `FormatLiteral` class that were causing `getMaxConvertedLength` and related predicates to return no results when the format literal was `%e`, `%f` or `%g` and an explicit precision was specified.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.1
|
||||
lastReleaseVersion: 0.4.3
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -136,6 +136,18 @@ module Consistency {
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
query predicate readStepIsLocal(Node n1, Node n2, string msg) {
|
||||
readStep(n1, _, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Read step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
query predicate storeStepIsLocal(Node n1, Node n2, string msg) {
|
||||
storeStep(n1, _, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Store step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
private DataFlowType typeRepr() { result = getNodeType(_) }
|
||||
|
||||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
|
||||
|
||||
@@ -4,6 +4,12 @@
|
||||
* variable), and `v` is an integer in the range `[0 .. m-1]`.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The main recursion has base cases in both `ssaModulus` (for guarded reads) and `semExprModulus`
|
||||
* (for constant values). The most interesting recursive case is `phiModulusRankStep`, which
|
||||
* handles phi inputs.
|
||||
*/
|
||||
|
||||
private import ModulusAnalysisSpecific::Private
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
@@ -162,6 +168,11 @@ private predicate phiModulusInit(SemSsaPhiNode phi, SemBound b, int val, int mod
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate phiModulusRankStep(SemSsaPhiNode phi, SemBound b, int val, int mod, int rix) {
|
||||
/*
|
||||
* base case. If any phi input is equal to `b + val` modulo `mod`, that's a potential congruence
|
||||
* class for the phi node.
|
||||
*/
|
||||
|
||||
rix = 0 and
|
||||
phiModulusInit(phi, b, val, mod)
|
||||
or
|
||||
@@ -169,6 +180,12 @@ private predicate phiModulusRankStep(SemSsaPhiNode phi, SemBound b, int val, int
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
|
|
||||
/*
|
||||
* Recursive case. If `inp` = `b + v2` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be the congruence class of `v1` modulo
|
||||
* the greatest common denominator of `m1`, `m2`, and `v1 - v2`.
|
||||
*/
|
||||
|
||||
exists(int v2, int m2 |
|
||||
rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
@@ -176,6 +193,12 @@ private predicate phiModulusRankStep(SemSsaPhiNode phi, SemBound b, int val, int
|
||||
mod = m1.gcd(m2).gcd(v1 - v2)
|
||||
)
|
||||
or
|
||||
/*
|
||||
* Recursive case. If `inp` = `phi` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be a congruence class modulo the greatest
|
||||
* common denominator of `m1` and `m2`.
|
||||
*/
|
||||
|
||||
exists(int m2 |
|
||||
rankedPhiInput(phi, inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.4.2-dev
|
||||
version: 0.4.4-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/ssa: 0.0.1
|
||||
codeql/ssa: ${workspace}
|
||||
|
||||
@@ -1125,12 +1125,12 @@ class FormatLiteral extends Literal {
|
||||
exists(int dot, int afterdot |
|
||||
(if this.getPrecision(n) = 0 then dot = 0 else dot = 1) and
|
||||
(
|
||||
(
|
||||
if this.hasExplicitPrecision(n)
|
||||
then afterdot = this.getPrecision(n)
|
||||
else not this.hasImplicitPrecision(n)
|
||||
) and
|
||||
afterdot = 6
|
||||
if this.hasExplicitPrecision(n)
|
||||
then afterdot = this.getPrecision(n)
|
||||
else (
|
||||
not this.hasImplicitPrecision(n) and
|
||||
afterdot = 6
|
||||
)
|
||||
) and
|
||||
len = 1 + 309 + dot + afterdot
|
||||
) and
|
||||
@@ -1140,12 +1140,12 @@ class FormatLiteral extends Literal {
|
||||
exists(int dot, int afterdot |
|
||||
(if this.getPrecision(n) = 0 then dot = 0 else dot = 1) and
|
||||
(
|
||||
(
|
||||
if this.hasExplicitPrecision(n)
|
||||
then afterdot = this.getPrecision(n)
|
||||
else not this.hasImplicitPrecision(n)
|
||||
) and
|
||||
afterdot = 6
|
||||
if this.hasExplicitPrecision(n)
|
||||
then afterdot = this.getPrecision(n)
|
||||
else (
|
||||
not this.hasImplicitPrecision(n) and
|
||||
afterdot = 6
|
||||
)
|
||||
) and
|
||||
len = 1 + 1 + dot + afterdot + 1 + 1 + 3
|
||||
) and
|
||||
@@ -1155,12 +1155,12 @@ class FormatLiteral extends Literal {
|
||||
exists(int dot, int afterdot |
|
||||
(if this.getPrecision(n) = 0 then dot = 0 else dot = 1) and
|
||||
(
|
||||
(
|
||||
if this.hasExplicitPrecision(n)
|
||||
then afterdot = this.getPrecision(n)
|
||||
else not this.hasImplicitPrecision(n)
|
||||
) and
|
||||
afterdot = 6
|
||||
if this.hasExplicitPrecision(n)
|
||||
then afterdot = this.getPrecision(n)
|
||||
else (
|
||||
not this.hasImplicitPrecision(n) and
|
||||
afterdot = 6
|
||||
)
|
||||
) and
|
||||
// note: this could be displayed in the style %e or %f;
|
||||
// however %f is only used when 'P > X >= -4'
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -136,6 +136,18 @@ module Consistency {
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
query predicate readStepIsLocal(Node n1, Node n2, string msg) {
|
||||
readStep(n1, _, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Read step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
query predicate storeStepIsLocal(Node n1, Node n2, string msg) {
|
||||
storeStep(n1, _, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Store step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
private DataFlowType typeRepr() { result = getNodeType(_) }
|
||||
|
||||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -147,6 +147,12 @@ abstract class Configuration extends string {
|
||||
*/
|
||||
FlowFeature getAFeature() { none() }
|
||||
|
||||
/** Holds if sources should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sourceGrouping(Node source, string sourceGroup) { none() }
|
||||
|
||||
/** Holds if sinks should be grouped in the result of `hasFlowPath`. */
|
||||
predicate sinkGrouping(Node sink, string sinkGroup) { none() }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||
*/
|
||||
@@ -158,7 +164,7 @@ abstract class Configuration extends string {
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) }
|
||||
predicate hasFlowPath(PathNode source, PathNode sink) { hasFlowPath(source, sink, this) }
|
||||
|
||||
/**
|
||||
* Holds if data may flow from some source to `sink` for this configuration.
|
||||
@@ -2629,6 +2635,7 @@ private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration
|
||||
/**
|
||||
* Gets the number of `AccessPath`s that correspond to `apa`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
evalUnfold(apa, false, config) and
|
||||
result = 1 and
|
||||
@@ -2647,6 +2654,7 @@ private int countAps(AccessPathApprox apa, Configuration config) {
|
||||
* that it is expanded to a precise head-tail representation.
|
||||
*/
|
||||
language[monotonicAggregates]
|
||||
pragma[assume_small_delta]
|
||||
private int countPotentialAps(AccessPathApprox apa, Configuration config) {
|
||||
apa instanceof AccessPathApproxNil and result = 1
|
||||
or
|
||||
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
|
||||
}
|
||||
|
||||
private newtype TPathNode =
|
||||
pragma[assume_small_delta]
|
||||
TPathNodeMid(
|
||||
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
|
||||
) {
|
||||
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
|
||||
state = sink.getState() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSourceGroup(string sourceGroup, Configuration config) {
|
||||
exists(PathNodeImpl source |
|
||||
sourceGroup = source.getSourceGroup() and
|
||||
config = source.getConfiguration()
|
||||
)
|
||||
} or
|
||||
TPathNodeSinkGroup(string sinkGroup, Configuration config) {
|
||||
exists(PathNodeSink sink |
|
||||
sinkGroup = sink.getSinkGroup() and
|
||||
config = sink.getConfiguration()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2778,6 +2799,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
|
||||
override AccessPathFrontHead getFront() { result = TFrontHead(head) }
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override AccessPathApproxCons getApprox() {
|
||||
result = TConsNil(head, tail.(AccessPathNil).getType())
|
||||
or
|
||||
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
|
||||
result = TCons1(head, this.length())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override int length() { result = 1 + tail.length() }
|
||||
|
||||
private string toStringImpl(boolean needsSuffix) {
|
||||
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source are generated.
|
||||
*/
|
||||
class PathNode extends TPathNode {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { none() }
|
||||
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result }
|
||||
|
||||
abstract private class PathNodeImpl extends TPathNode {
|
||||
/** Gets the `FlowState` of this node. */
|
||||
FlowState getState() { none() }
|
||||
abstract FlowState getState();
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
Configuration getConfiguration() { none() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() {
|
||||
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
|
||||
reach(this) and
|
||||
reach(result)
|
||||
}
|
||||
abstract Configuration getConfiguration();
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
predicate isSource() { none() }
|
||||
}
|
||||
abstract predicate isSource();
|
||||
|
||||
abstract private class PathNodeImpl extends PathNode {
|
||||
abstract PathNodeImpl getASuccessorImpl();
|
||||
|
||||
private PathNodeImpl getASuccessorIfHidden() {
|
||||
@@ -2953,6 +2938,22 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
)
|
||||
}
|
||||
|
||||
string getSourceGroup() {
|
||||
this.isSource() and
|
||||
this.getConfiguration().sourceGrouping(this.getNodeEx().asNode(), result)
|
||||
}
|
||||
|
||||
predicate isFlowSource() {
|
||||
this.isSource() and not exists(this.getSourceGroup())
|
||||
or
|
||||
this instanceof PathNodeSourceGroup
|
||||
}
|
||||
|
||||
predicate isFlowSink() {
|
||||
this = any(PathNodeSink sink | not exists(sink.getSinkGroup())) or
|
||||
this instanceof PathNodeSinkGroup
|
||||
}
|
||||
|
||||
private string ppAp() {
|
||||
this instanceof PathNodeSink and result = ""
|
||||
or
|
||||
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
|
||||
}
|
||||
|
||||
override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
|
||||
|
||||
override string toStringWithContext() {
|
||||
result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
|
||||
}
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
string toStringWithContext() { result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
/**
|
||||
* 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
|
||||
) {
|
||||
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
@@ -2982,18 +2993,71 @@ abstract private class PathNodeImpl extends PathNode {
|
||||
|
||||
/** Holds if `n` can reach a sink. */
|
||||
private predicate directReach(PathNodeImpl n) {
|
||||
n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
|
||||
n instanceof PathNodeSink or
|
||||
n instanceof PathNodeSinkGroup or
|
||||
directReach(n.getANonHiddenSuccessor())
|
||||
}
|
||||
|
||||
/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */
|
||||
private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) }
|
||||
private predicate reach(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) }
|
||||
|
||||
/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
|
||||
private predicate pathSucc(PathNodeImpl n1, PathNodeImpl n2) {
|
||||
n1.getANonHiddenSuccessor() = n2 and directReach(n2)
|
||||
}
|
||||
|
||||
private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
|
||||
* Only those `PathNode`s that are reachable from a source, and which can reach a sink, are generated.
|
||||
*/
|
||||
class PathNode instanceof PathNodeImpl {
|
||||
PathNode() { reach(this) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this element, including a textual
|
||||
* representation of the call context.
|
||||
*/
|
||||
final string toStringWithContext() { result = super.toStringWithContext() }
|
||||
|
||||
/**
|
||||
* 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/).
|
||||
*/
|
||||
final predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
super.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets the underlying `Node`. */
|
||||
final Node getNode() { super.getNodeEx().projectToNode() = result }
|
||||
|
||||
/** Gets the `FlowState` of this node. */
|
||||
final FlowState getState() { result = super.getState() }
|
||||
|
||||
/** Gets the associated configuration. */
|
||||
final Configuration getConfiguration() { result = super.getConfiguration() }
|
||||
|
||||
/** Gets a successor of this node, if any. */
|
||||
final PathNode getASuccessor() { result = super.getANonHiddenSuccessor() }
|
||||
|
||||
/** Holds if this node is a source. */
|
||||
final predicate isSource() { super.isSource() }
|
||||
|
||||
/** Holds if this node is a grouping of source nodes. */
|
||||
final predicate isSourceGroup(string group) { this = TPathNodeSourceGroup(group, _) }
|
||||
|
||||
/** Holds if this node is a grouping of sink nodes. */
|
||||
final predicate isSinkGroup(string group) { this = TPathNodeSinkGroup(group, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the query predicates needed to include a graph in a path-problem query.
|
||||
@@ -3004,7 +3068,7 @@ module PathGraph {
|
||||
|
||||
/** Holds if `n` is a node in the graph of data flow path explanations. */
|
||||
query predicate nodes(PathNode n, string key, string val) {
|
||||
reach(n) and key = "semmle.label" and val = n.toString()
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3013,11 +3077,7 @@ module PathGraph {
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
|
||||
Subpaths::subpaths(arg, par, ret, out) and
|
||||
reach(arg) and
|
||||
reach(par) and
|
||||
reach(ret) and
|
||||
reach(out)
|
||||
Subpaths::subpaths(arg, par, ret, out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
|
||||
}
|
||||
|
||||
override predicate isSource() { sourceNode(node, state, config) }
|
||||
|
||||
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
|
||||
}
|
||||
|
||||
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
|
||||
string sourceGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() {
|
||||
result.getSourceGroup() = sourceGroup and
|
||||
result.getConfiguration() = config
|
||||
}
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sourceGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
|
||||
string sinkGroup;
|
||||
Configuration config;
|
||||
|
||||
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
|
||||
|
||||
override NodeEx getNodeEx() { none() }
|
||||
|
||||
override FlowState getState() { none() }
|
||||
|
||||
override Configuration getConfiguration() { result = config }
|
||||
|
||||
override PathNodeImpl getASuccessorImpl() { none() }
|
||||
|
||||
override predicate isSource() { none() }
|
||||
|
||||
override string toString() { result = sinkGroup }
|
||||
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
|
||||
}
|
||||
}
|
||||
|
||||
private predicate pathNode(
|
||||
@@ -3142,6 +3259,7 @@ private predicate pathNode(
|
||||
* Holds if data may flow from `mid` to `node`. The last step in or out of
|
||||
* a callable is recorded by `cc`.
|
||||
*/
|
||||
pragma[assume_small_delta]
|
||||
pragma[nomagic]
|
||||
private predicate pathStep(
|
||||
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
|
||||
@@ -3399,7 +3517,7 @@ private module Subpaths {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths02(
|
||||
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||
NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3407,14 +3525,14 @@ private module Subpaths {
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() }
|
||||
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() }
|
||||
|
||||
/**
|
||||
* Holds if `(arg, par, ret, out)` forms a subpath-tuple.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate subpaths03(
|
||||
PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout
|
||||
) {
|
||||
exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
|
||||
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and
|
||||
@@ -3444,7 +3562,7 @@ private module Subpaths {
|
||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||
*/
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) {
|
||||
predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNodeImpl out) {
|
||||
exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
|
||||
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and
|
||||
subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and
|
||||
@@ -3460,7 +3578,7 @@ private module Subpaths {
|
||||
* Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
|
||||
*/
|
||||
predicate retReach(PathNodeImpl n) {
|
||||
exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
exists(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
|
||||
or
|
||||
exists(PathNodeImpl mid |
|
||||
retReach(mid) and
|
||||
@@ -3476,12 +3594,22 @@ private module Subpaths {
|
||||
* Will only have results if `configuration` has non-empty sources and
|
||||
* sinks.
|
||||
*/
|
||||
private predicate hasFlowPath(
|
||||
PathNodeImpl flowsource, PathNodeImpl flowsink, Configuration configuration
|
||||
) {
|
||||
flowsource.isFlowSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.isFlowSink()
|
||||
}
|
||||
|
||||
private predicate flowsTo(
|
||||
PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
|
||||
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink,
|
||||
Configuration configuration
|
||||
) {
|
||||
flowsource.isSource() and
|
||||
flowsource.getConfiguration() = configuration and
|
||||
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and
|
||||
flowsource.getNodeEx().asNode() = source and
|
||||
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
|
||||
flowsink.getNodeEx().asNode() = sink
|
||||
}
|
||||
@@ -3504,14 +3632,14 @@ private predicate finalStats(
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
|
||||
tuples = count(PathNode pn)
|
||||
tuples = count(PathNodeImpl pn)
|
||||
or
|
||||
fwd = false and
|
||||
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and
|
||||
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and
|
||||
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and
|
||||
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
|
||||
tuples = count(PathNode pn | reach(pn))
|
||||
tuples = count(PathNode pn)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -136,6 +136,18 @@ module Consistency {
|
||||
msg = "Local flow step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
query predicate readStepIsLocal(Node n1, Node n2, string msg) {
|
||||
readStep(n1, _, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Read step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
query predicate storeStepIsLocal(Node n1, Node n2, string msg) {
|
||||
storeStep(n1, _, n2) and
|
||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
||||
msg = "Store step does not preserve enclosing callable."
|
||||
}
|
||||
|
||||
private DataFlowType typeRepr() { result = getNodeType(_) }
|
||||
|
||||
query predicate compatibleTypesReflexive(DataFlowType t, string msg) {
|
||||
|
||||
@@ -98,16 +98,16 @@ class ParameterPosition = Position;
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition = Position;
|
||||
|
||||
class Position extends TPosition {
|
||||
abstract class Position extends TPosition {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
class DirectPosition extends TDirectPosition {
|
||||
class DirectPosition extends Position, TDirectPosition {
|
||||
int index;
|
||||
|
||||
DirectPosition() { this = TDirectPosition(index) }
|
||||
|
||||
string toString() {
|
||||
override string toString() {
|
||||
index = -1 and
|
||||
result = "this"
|
||||
or
|
||||
@@ -118,12 +118,12 @@ class DirectPosition extends TDirectPosition {
|
||||
int getIndex() { result = index }
|
||||
}
|
||||
|
||||
class IndirectionPosition extends TIndirectionPosition {
|
||||
class IndirectionPosition extends Position, TIndirectionPosition {
|
||||
int index;
|
||||
|
||||
IndirectionPosition() { this = TIndirectionPosition(index) }
|
||||
|
||||
string toString() {
|
||||
override string toString() {
|
||||
index = -1 and
|
||||
result = "this"
|
||||
or
|
||||
|
||||
@@ -16,21 +16,23 @@ private class StdBasicString extends ClassTemplateInstantiation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional model for `std::string` constructors that reference the character
|
||||
* type of the container, or an iterator. For example construction from
|
||||
* iterators:
|
||||
* ```
|
||||
* std::string b(a.begin(), a.end());
|
||||
* ```
|
||||
* The `std::basic_string::iterator` declaration.
|
||||
*/
|
||||
private class StdStringConstructor extends Constructor, TaintFunction {
|
||||
StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString }
|
||||
private class StdBasicStringIterator extends Iterator, Type {
|
||||
StdBasicStringIterator() {
|
||||
this.getEnclosingElement() instanceof StdBasicString and this.hasName("iterator")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `std::string` function for which taint should be propagated.
|
||||
*/
|
||||
abstract private class StdStringTaintFunction extends TaintFunction {
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
* character).
|
||||
*/
|
||||
int getAStringParameterIndex() {
|
||||
final int getAStringParameterIndex() {
|
||||
exists(Type paramType | paramType = this.getParameter(result).getUnspecifiedType() |
|
||||
// e.g. `std::basic_string::CharT *`
|
||||
paramType instanceof PointerType
|
||||
@@ -41,15 +43,28 @@ private class StdStringConstructor extends Constructor, TaintFunction {
|
||||
this.getDeclaringType().getTemplateArgument(2).(Type).getUnspecifiedType()
|
||||
or
|
||||
// i.e. `std::basic_string::CharT`
|
||||
this.getParameter(result).getUnspecifiedType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType()
|
||||
paramType = this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
*/
|
||||
int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator }
|
||||
final int getAnIteratorParameterIndex() {
|
||||
this.getParameter(result).getType() instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional model for `std::string` constructors that reference the character
|
||||
* type of the container, or an iterator. For example construction from
|
||||
* iterators:
|
||||
* ```
|
||||
* std::string b(a.begin(), a.end());
|
||||
* ```
|
||||
*/
|
||||
private class StdStringConstructor extends Constructor, StdStringTaintFunction {
|
||||
StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// taint flow from any parameter of the value type to the returned object
|
||||
@@ -68,7 +83,7 @@ private class StdStringConstructor extends Constructor, TaintFunction {
|
||||
/**
|
||||
* The `std::string` function `c_str`.
|
||||
*/
|
||||
private class StdStringCStr extends TaintFunction {
|
||||
private class StdStringCStr extends StdStringTaintFunction {
|
||||
StdStringCStr() { this.getClassAndName("c_str") instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -81,7 +96,7 @@ private class StdStringCStr extends TaintFunction {
|
||||
/**
|
||||
* The `std::string` function `data`.
|
||||
*/
|
||||
private class StdStringData extends TaintFunction {
|
||||
private class StdStringData extends StdStringTaintFunction {
|
||||
StdStringData() { this.getClassAndName("data") instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -99,7 +114,7 @@ private class StdStringData extends TaintFunction {
|
||||
/**
|
||||
* The `std::string` function `push_back`.
|
||||
*/
|
||||
private class StdStringPush extends TaintFunction {
|
||||
private class StdStringPush extends StdStringTaintFunction {
|
||||
StdStringPush() { this.getClassAndName("push_back") instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -112,7 +127,7 @@ private class StdStringPush extends TaintFunction {
|
||||
/**
|
||||
* The `std::string` functions `front` and `back`.
|
||||
*/
|
||||
private class StdStringFrontBack extends TaintFunction {
|
||||
private class StdStringFrontBack extends StdStringTaintFunction {
|
||||
StdStringFrontBack() { this.getClassAndName(["front", "back"]) instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -125,7 +140,7 @@ private class StdStringFrontBack extends TaintFunction {
|
||||
/**
|
||||
* The (non-member) `std::string` function `operator+`.
|
||||
*/
|
||||
private class StdStringPlus extends TaintFunction {
|
||||
private class StdStringPlus extends StdStringTaintFunction {
|
||||
StdStringPlus() {
|
||||
this.hasQualifiedName(["std", "bsl"], "operator+") and
|
||||
this.getUnspecifiedType() instanceof StdBasicString
|
||||
@@ -142,31 +157,15 @@ private class StdStringPlus extends TaintFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` functions `operator+=`, `append`, `insert` and
|
||||
* `replace`. All of these functions combine the existing string
|
||||
* with a new string (or character) from one of the arguments.
|
||||
* The `std::string` functions `operator+=`, `append` and `replace`.
|
||||
* All of these functions combine the existing string with a new
|
||||
* string (or character) from one of the arguments.
|
||||
*/
|
||||
private class StdStringAppend extends TaintFunction {
|
||||
private class StdStringAppend extends StdStringTaintFunction {
|
||||
StdStringAppend() {
|
||||
this.getClassAndName(["operator+=", "append", "insert", "replace"]) instanceof StdBasicString
|
||||
this.getClassAndName(["operator+=", "append", "replace"]) instanceof StdBasicString
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
* character).
|
||||
*/
|
||||
int getAStringParameterIndex() {
|
||||
this.getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *`
|
||||
this.getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &`
|
||||
this.getParameter(result).getUnspecifiedType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT`
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
*/
|
||||
int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from string and parameter to string (qualifier) and return value
|
||||
(
|
||||
@@ -186,28 +185,44 @@ private class StdStringAppend extends TaintFunction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `std::string` function `insert`.
|
||||
*/
|
||||
private class StdStringInsert extends StdStringTaintFunction {
|
||||
StdStringInsert() { this.getClassAndName("insert") instanceof StdBasicString }
|
||||
|
||||
/**
|
||||
* Holds if the return type is an iterator.
|
||||
*/
|
||||
predicate hasIteratorReturnValue() { this.getType() instanceof Iterator }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from string and parameter to string (qualifier) and return value
|
||||
(
|
||||
input.isQualifierObject() or
|
||||
input.isParameterDeref(this.getAStringParameterIndex()) or
|
||||
input.isParameter(this.getAnIteratorParameterIndex())
|
||||
) and
|
||||
(
|
||||
output.isQualifierObject()
|
||||
or
|
||||
if this.hasIteratorReturnValue() then output.isReturnValue() else output.isReturnValueDeref()
|
||||
)
|
||||
or
|
||||
// reverse flow from returned reference to the qualifier (for writes to
|
||||
// the result)
|
||||
not this.hasIteratorReturnValue() and
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard function `std::string.assign`.
|
||||
*/
|
||||
private class StdStringAssign extends TaintFunction {
|
||||
private class StdStringAssign extends StdStringTaintFunction {
|
||||
StdStringAssign() { this.getClassAndName("assign") instanceof StdBasicString }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a string (or
|
||||
* character).
|
||||
*/
|
||||
int getAStringParameterIndex() {
|
||||
this.getParameter(result).getType() instanceof PointerType or // e.g. `std::basic_string::CharT *`
|
||||
this.getParameter(result).getType() instanceof ReferenceType or // e.g. `std::basic_string &`
|
||||
this.getParameter(result).getUnspecifiedType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. `std::basic_string::CharT`
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
*/
|
||||
int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to string itself (qualifier) and return value
|
||||
(
|
||||
@@ -229,7 +244,7 @@ private class StdStringAssign extends TaintFunction {
|
||||
/**
|
||||
* The standard function `std::string.copy`.
|
||||
*/
|
||||
private class StdStringCopy extends TaintFunction {
|
||||
private class StdStringCopy extends StdStringTaintFunction {
|
||||
StdStringCopy() { this.getClassAndName("copy") instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -242,7 +257,7 @@ private class StdStringCopy extends TaintFunction {
|
||||
/**
|
||||
* The standard function `std::string.substr`.
|
||||
*/
|
||||
private class StdStringSubstr extends TaintFunction {
|
||||
private class StdStringSubstr extends StdStringTaintFunction {
|
||||
StdStringSubstr() { this.getClassAndName("substr") instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -255,7 +270,7 @@ private class StdStringSubstr extends TaintFunction {
|
||||
/**
|
||||
* The `std::string` functions `at` and `operator[]`.
|
||||
*/
|
||||
private class StdStringAt extends TaintFunction {
|
||||
private class StdStringAt extends StdStringTaintFunction {
|
||||
StdStringAt() { this.getClassAndName(["at", "operator[]"]) instanceof StdBasicString }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
|
||||
@@ -50,19 +50,18 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
this.getName() = ["strncat", "wcsncat", "_mbsncat", "_mbsncat_l"] and
|
||||
input.isParameter(2) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
this.getName() = ["_mbsncat_l", "_mbsnbcat_l"] and
|
||||
input.isParameter(3) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
input.isParameterDeref(0) and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
input.isParameterDeref(1) and
|
||||
output.isParameterDeref(0)
|
||||
(
|
||||
this.getName() = ["strncat", "wcsncat", "_mbsncat", "_mbsncat_l"] and
|
||||
input.isParameter(2)
|
||||
or
|
||||
this.getName() = ["_mbsncat_l", "_mbsnbcat_l"] and
|
||||
input.isParameter(3)
|
||||
or
|
||||
input.isParameterDeref(0)
|
||||
or
|
||||
input.isParameterDeref(1)
|
||||
) and
|
||||
(output.isParameterDeref(0) or output.isReturnValueDeref())
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int param) {
|
||||
|
||||
@@ -16,15 +16,36 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Exclusions
|
||||
|
||||
/** Gets the sub-expression of 'e' with the earliest-starting Location */
|
||||
/**
|
||||
* Gets a child of `e`, including conversions but excluding call arguments.
|
||||
*/
|
||||
pragma[inline]
|
||||
Expr getAChildWithConversions(Expr e) {
|
||||
result.getParentWithConversions() = e and
|
||||
not result = any(Call c).getAnArgument()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the left-most column position of any transitive child of `e` (including
|
||||
* conversions but excluding call arguments).
|
||||
*/
|
||||
int getCandidateColumn(Expr e) {
|
||||
result = e.getLocation().getStartColumn() or
|
||||
result = getCandidateColumn(getAChildWithConversions(e))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the transitive child of `e` (including conversions but excluding call
|
||||
* arguments) at the left-most column position, preferring less deeply nested
|
||||
* expressions if there is a choice.
|
||||
*/
|
||||
Expr normalizeExpr(Expr e) {
|
||||
result =
|
||||
min(Expr child |
|
||||
child.getParentWithConversions*() = e.getFullyConverted() and
|
||||
not child.getParentWithConversions*() = any(Call c).getAnArgument()
|
||||
|
|
||||
child order by child.getLocation().getStartColumn(), count(child.getParentWithConversions*())
|
||||
)
|
||||
e.getLocation().getStartColumn() = min(getCandidateColumn(e)) and
|
||||
result = e
|
||||
or
|
||||
not e.getLocation().getStartColumn() = min(getCandidateColumn(e)) and
|
||||
result = normalizeExpr(getAChildWithConversions(e)) and
|
||||
result.getLocation().getStartColumn() = min(getCandidateColumn(e))
|
||||
}
|
||||
|
||||
predicate isParenthesized(CommaExpr ce) {
|
||||
@@ -43,8 +64,8 @@ from CommaExpr ce, Expr left, Expr right, Location leftLoc, Location rightLoc
|
||||
where
|
||||
ce.fromSource() and
|
||||
not isFromMacroDefinition(ce) and
|
||||
left = normalizeExpr(ce.getLeftOperand()) and
|
||||
right = normalizeExpr(ce.getRightOperand()) and
|
||||
left = normalizeExpr(ce.getLeftOperand().getFullyConverted()) and
|
||||
right = normalizeExpr(ce.getRightOperand().getFullyConverted()) and
|
||||
leftLoc = left.getLocation() and
|
||||
rightLoc = right.getLocation() and
|
||||
not isParenthesized(ce) and
|
||||
|
||||
@@ -13,16 +13,32 @@
|
||||
|
||||
import cpp
|
||||
|
||||
pragma[noinline]
|
||||
predicate possiblyIncompleteFile(File f) {
|
||||
exists(Diagnostic d | d.getFile() = f and d.getSeverity() >= 3)
|
||||
}
|
||||
|
||||
predicate immediatelyReachableFunction(Function f) {
|
||||
not f.isStatic() or
|
||||
exists(BlockExpr be | be.getFunction() = f) or
|
||||
f instanceof MemberFunction or
|
||||
f instanceof TemplateFunction or
|
||||
f.getFile() instanceof HeaderFile or
|
||||
f.getAnAttribute().hasName("constructor") or
|
||||
f.getAnAttribute().hasName("destructor") or
|
||||
f.getAnAttribute().hasName("used") or
|
||||
not f.isStatic()
|
||||
or
|
||||
exists(BlockExpr be | be.getFunction() = f)
|
||||
or
|
||||
f instanceof MemberFunction
|
||||
or
|
||||
f instanceof TemplateFunction
|
||||
or
|
||||
f.getFile() instanceof HeaderFile
|
||||
or
|
||||
f.getAnAttribute().hasName("constructor")
|
||||
or
|
||||
f.getAnAttribute().hasName("destructor")
|
||||
or
|
||||
f.getAnAttribute().hasName("used")
|
||||
or
|
||||
f.getAnAttribute().hasName("unused")
|
||||
or
|
||||
// a compiler error in the same file suggests we may be missing data
|
||||
possiblyIncompleteFile(f.getFile())
|
||||
}
|
||||
|
||||
predicate immediatelyReachableVariable(Variable v) {
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
## 0.4.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Fixed a bug in `cpp/jsf/av-rule-76` that caused the query to miss results when an implicitly-defined copy constructor or copy assignment operator was generated.
|
||||
|
||||
## 0.4.2
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new medium-precision query, `cpp/comma-before-misleading-indentation`, which detects instances of whitespace that have readability issues.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Unterminated variadic call" (`cpp/unterminated-variadic-call`) query has been tuned to produce fewer false positive results.
|
||||
* Fixed false positives from the "Unused static function" (`cpp/unused-static-function`) query in files that had errors during compilation.
|
||||
|
||||
## 0.4.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -13,11 +13,18 @@
|
||||
|
||||
import cpp
|
||||
|
||||
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given
|
||||
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given, string ffcName
|
||||
where
|
||||
ffc = fl.getUse() and
|
||||
expected = fl.getNumArgNeeded() and
|
||||
given = ffc.getNumFormatArgument() and
|
||||
expected < given and
|
||||
fl.specsAreKnown()
|
||||
select ffc, "Format expects " + expected.toString() + " arguments but given " + given.toString()
|
||||
fl.specsAreKnown() and
|
||||
(
|
||||
if ffc.isInMacroExpansion()
|
||||
then ffcName = ffc.getTarget().getName() + " (in a macro expansion)"
|
||||
else ffcName = ffc.getTarget().getName()
|
||||
)
|
||||
select ffc,
|
||||
"Format for " + ffcName + " expects " + expected.toString() + " arguments but given " +
|
||||
given.toString()
|
||||
|
||||
@@ -16,11 +16,18 @@
|
||||
|
||||
import cpp
|
||||
|
||||
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given
|
||||
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given, string ffcName
|
||||
where
|
||||
ffc = fl.getUse() and
|
||||
expected = fl.getNumArgNeeded() and
|
||||
given = ffc.getNumFormatArgument() and
|
||||
expected > given and
|
||||
fl.specsAreKnown()
|
||||
select ffc, "Format expects " + expected.toString() + " arguments but given " + given.toString()
|
||||
fl.specsAreKnown() and
|
||||
(
|
||||
if ffc.isInMacroExpansion()
|
||||
then ffcName = ffc.getTarget().getName() + " (in a macro expansion)"
|
||||
else ffcName = ffc.getTarget().getName()
|
||||
)
|
||||
select ffc,
|
||||
"Format for " + ffcName + " expects " + expected.toString() + " arguments but given " +
|
||||
given.toString()
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new medium-precision query, `cpp/comma-before-misleading-indentation`, which detects instances of whitespace that have readability issues.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The "Unterminated variadic call" (`cpp/unterminated-variadic-call`) query has been tuned to produce fewer false positive results.
|
||||
10
cpp/ql/src/change-notes/released/0.4.2.md
Normal file
10
cpp/ql/src/change-notes/released/0.4.2.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## 0.4.2
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new medium-precision query, `cpp/comma-before-misleading-indentation`, which detects instances of whitespace that have readability issues.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Unterminated variadic call" (`cpp/unterminated-variadic-call`) query has been tuned to produce fewer false positive results.
|
||||
* Fixed false positives from the "Unused static function" (`cpp/unused-static-function`) query in files that had errors during compilation.
|
||||
5
cpp/ql/src/change-notes/released/0.4.3.md
Normal file
5
cpp/ql/src/change-notes/released/0.4.3.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.4.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Fixed a bug in `cpp/jsf/av-rule-76` that caused the query to miss results when an implicitly-defined copy constructor or copy assignment operator was generated.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.1
|
||||
lastReleaseVersion: 0.4.3
|
||||
|
||||
@@ -38,9 +38,9 @@ predicate hasNontrivialDestructor(Class c) {
|
||||
from Class c
|
||||
where
|
||||
(hasPointerMember(c) or hasNontrivialDestructor(c)) and
|
||||
not (
|
||||
c.getAMemberFunction() instanceof CopyConstructor and
|
||||
c.getAMemberFunction() instanceof CopyAssignmentOperator
|
||||
(
|
||||
c.hasImplicitCopyAssignmentOperator() or
|
||||
c.hasImplicitCopyConstructor()
|
||||
) and
|
||||
not c instanceof Struct
|
||||
select c,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.4.2-dev
|
||||
version: 0.4.4-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
dependencies:
|
||||
codeql/cpp-all: "*"
|
||||
codeql/suite-helpers: "*"
|
||||
codeql/cpp-all: ${workspace}
|
||||
codeql/suite-helpers: ${workspace}
|
||||
suites: codeql-suites
|
||||
extractor: cpp
|
||||
defaultSuiteFile: codeql-suites/cpp-code-scanning.qls
|
||||
|
||||
@@ -137,6 +137,7 @@ abstract class InlineExpectationsTest extends string {
|
||||
final predicate hasFailureMessage(FailureLocatable element, string message) {
|
||||
exists(ActualResult actualResult |
|
||||
actualResult.getTest() = this and
|
||||
actualResult.getTag() = this.getARelevantTag() and
|
||||
element = actualResult and
|
||||
(
|
||||
exists(FalseNegativeExpectation falseNegative |
|
||||
@@ -150,9 +151,18 @@ abstract class InlineExpectationsTest extends string {
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(ActualResult actualResult |
|
||||
actualResult.getTest() = this and
|
||||
not actualResult.getTag() = this.getARelevantTag() and
|
||||
element = actualResult and
|
||||
message =
|
||||
"Tag mismatch: Actual result with tag '" + actualResult.getTag() +
|
||||
"' that is not part of getARelevantTag()"
|
||||
)
|
||||
or
|
||||
exists(ValidExpectation expectation |
|
||||
not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and
|
||||
expectation.getTag() = getARelevantTag() and
|
||||
expectation.getTag() = this.getARelevantTag() and
|
||||
element = expectation and
|
||||
(
|
||||
expectation instanceof GoodExpectation and
|
||||
|
||||
@@ -2,43 +2,21 @@ edges
|
||||
| test.cpp:4:15:4:20 | call to malloc | test.cpp:5:15:5:15 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:6:15:6:15 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:7:16:7:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:20 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:20 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:20 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:8:16:8:20 | ... + ... |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:9:16:9:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:10:16:10:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:5:15:5:15 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:5:15:5:22 | Store |
|
||||
| test.cpp:5:15:5:22 | ... + ... | test.cpp:5:15:5:22 | Store |
|
||||
@@ -116,7 +94,6 @@ edges
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:11:16:11:16 | Load |
|
||||
| test.cpp:8:16:8:16 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:8:16:8:20 | ... + ... | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:8:16:8:20 | ... + ... | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:9:16:9:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
@@ -139,62 +116,30 @@ edges
|
||||
| test.cpp:11:16:11:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:11:16:11:16 | Load | test.cpp:12:16:12:16 | Load |
|
||||
| test.cpp:12:16:12:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:12:16:12:16 | Load | test.cpp:6:14:6:15 | Load: * ... |
|
||||
| test.cpp:12:16:12:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:12:16:12:16 | Load | test.cpp:8:14:8:21 | Load: * ... |
|
||||
| test.cpp:16:15:16:20 | call to malloc | test.cpp:17:15:17:15 | Load |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:17:15:17:22 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:17:15:17:22 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:17:15:17:22 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:17:15:17:22 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:20:16:20:20 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:20:16:20:20 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:20:16:20:20 | ... + ... |
|
||||
| test.cpp:17:15:17:15 | Load | test.cpp:20:16:20:20 | ... + ... |
|
||||
| test.cpp:17:15:17:22 | ... + ... | test.cpp:20:14:20:21 | Load: * ... |
|
||||
| test.cpp:17:15:17:22 | ... + ... | test.cpp:20:14:20:21 | Load: * ... |
|
||||
| test.cpp:20:16:20:20 | ... + ... | test.cpp:20:14:20:21 | Load: * ... |
|
||||
| test.cpp:20:16:20:20 | ... + ... | test.cpp:20:14:20:21 | Load: * ... |
|
||||
| test.cpp:28:15:28:20 | call to malloc | test.cpp:29:15:29:15 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:30:15:30:15 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:31:16:31:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:20 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:20 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:20 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:32:16:32:20 | ... + ... |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:33:16:33:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:34:16:34:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:29:15:29:15 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:29:15:29:28 | Store |
|
||||
| test.cpp:29:15:29:28 | ... + ... | test.cpp:29:15:29:28 | Store |
|
||||
@@ -272,7 +217,6 @@ edges
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:35:16:35:16 | Load |
|
||||
| test.cpp:32:16:32:16 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:32:16:32:20 | ... + ... | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:32:16:32:20 | ... + ... | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:33:16:33:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
@@ -295,49 +239,25 @@ edges
|
||||
| test.cpp:35:16:35:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:35:16:35:16 | Load | test.cpp:36:16:36:16 | Load |
|
||||
| test.cpp:36:16:36:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:36:16:36:16 | Load | test.cpp:30:14:30:15 | Load: * ... |
|
||||
| test.cpp:36:16:36:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:36:16:36:16 | Load | test.cpp:32:14:32:21 | Load: * ... |
|
||||
| test.cpp:40:15:40:20 | call to malloc | test.cpp:41:15:41:15 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:42:15:42:15 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:43:16:43:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:20 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:20 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:20 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:44:16:44:20 | ... + ... |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:45:16:45:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:46:16:46:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:41:15:41:15 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:41:15:41:28 | Store |
|
||||
| test.cpp:41:15:41:28 | ... + ... | test.cpp:41:15:41:28 | Store |
|
||||
@@ -415,7 +335,6 @@ edges
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:47:16:47:16 | Load |
|
||||
| test.cpp:44:16:44:16 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:44:16:44:20 | ... + ... | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:44:16:44:20 | ... + ... | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:45:16:45:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
@@ -438,8 +357,6 @@ edges
|
||||
| test.cpp:47:16:47:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:47:16:47:16 | Load | test.cpp:48:16:48:16 | Load |
|
||||
| test.cpp:48:16:48:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:48:16:48:16 | Load | test.cpp:42:14:42:15 | Load: * ... |
|
||||
| test.cpp:48:16:48:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:48:16:48:16 | Load | test.cpp:44:14:44:21 | Load: * ... |
|
||||
| test.cpp:51:7:51:14 | VariableAddress indirection | test.cpp:62:39:62:39 | Load |
|
||||
| test.cpp:51:7:51:14 | VariableAddress indirection | test.cpp:66:39:66:39 | Load |
|
||||
@@ -449,18 +366,13 @@ edges
|
||||
| test.cpp:52:19:52:24 | call to malloc | test.cpp:53:12:53:16 | Load |
|
||||
| test.cpp:53:5:53:23 | Store | test.cpp:51:33:51:35 | Load indirection |
|
||||
| test.cpp:53:12:53:16 | Load | test.cpp:53:5:53:23 | Store |
|
||||
| test.cpp:53:12:53:16 | Load | test.cpp:53:5:53:23 | Store |
|
||||
| test.cpp:53:12:53:16 | Load | test.cpp:53:12:53:23 | ... + ... |
|
||||
| test.cpp:53:12:53:16 | Load | test.cpp:53:12:53:23 | ... + ... |
|
||||
| test.cpp:53:12:53:23 | ... + ... | test.cpp:51:33:51:35 | Load indirection |
|
||||
| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:62:32:62:34 | Load |
|
||||
| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:66:32:66:34 | Load |
|
||||
| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:70:31:70:33 | Load |
|
||||
| test.cpp:62:32:62:34 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:62:32:62:34 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:66:32:66:34 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:66:32:66:34 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:70:31:70:33 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:70:31:70:33 | Load | test.cpp:67:9:67:14 | Store: ... = ... |
|
||||
| test.cpp:80:9:80:16 | VariableAddress indirection [begin] | test.cpp:91:20:91:22 | arr indirection [begin] |
|
||||
| test.cpp:80:9:80:16 | VariableAddress indirection [begin] | test.cpp:95:20:95:22 | arr indirection [begin] |
|
||||
@@ -479,8 +391,6 @@ edges
|
||||
| test.cpp:83:15:83:17 | arr indirection [begin] | test.cpp:83:19:83:23 | begin |
|
||||
| test.cpp:83:15:83:30 | ... + ... | test.cpp:83:5:83:30 | Store |
|
||||
| test.cpp:83:19:83:23 | Load | test.cpp:83:5:83:30 | Store |
|
||||
| test.cpp:83:19:83:23 | Load | test.cpp:83:5:83:30 | Store |
|
||||
| test.cpp:83:19:83:23 | Load | test.cpp:83:15:83:30 | ... + ... |
|
||||
| test.cpp:83:19:83:23 | Load | test.cpp:83:15:83:30 | ... + ... |
|
||||
| test.cpp:83:19:83:23 | begin | test.cpp:83:19:83:23 | Load |
|
||||
| test.cpp:91:20:91:22 | arr indirection [begin] | test.cpp:91:24:91:28 | begin |
|
||||
@@ -488,21 +398,18 @@ edges
|
||||
| test.cpp:91:24:91:28 | begin | test.cpp:91:47:91:47 | Load |
|
||||
| test.cpp:91:36:91:38 | arr indirection [end] | test.cpp:91:40:91:42 | end |
|
||||
| test.cpp:91:40:91:42 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:91:40:91:42 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:91:40:91:42 | end | test.cpp:91:40:91:42 | Load |
|
||||
| test.cpp:95:20:95:22 | arr indirection [begin] | test.cpp:95:24:95:28 | begin |
|
||||
| test.cpp:95:20:95:22 | arr indirection [begin] | test.cpp:95:47:95:47 | Load |
|
||||
| test.cpp:95:24:95:28 | begin | test.cpp:95:47:95:47 | Load |
|
||||
| test.cpp:95:36:95:38 | arr indirection [end] | test.cpp:95:40:95:42 | end |
|
||||
| test.cpp:95:40:95:42 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:95:40:95:42 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:95:40:95:42 | end | test.cpp:95:40:95:42 | Load |
|
||||
| test.cpp:99:20:99:22 | arr indirection [begin] | test.cpp:99:24:99:28 | begin |
|
||||
| test.cpp:99:20:99:22 | arr indirection [begin] | test.cpp:99:46:99:46 | Load |
|
||||
| test.cpp:99:24:99:28 | begin | test.cpp:99:46:99:46 | Load |
|
||||
| test.cpp:99:35:99:37 | arr indirection [end] | test.cpp:99:39:99:41 | end |
|
||||
| test.cpp:99:39:99:41 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:99:39:99:41 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
|
||||
| test.cpp:99:39:99:41 | end | test.cpp:99:39:99:41 | Load |
|
||||
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:105:20:105:22 | arr indirection [begin] |
|
||||
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:109:20:109:22 | arr indirection [begin] |
|
||||
@@ -515,21 +422,18 @@ edges
|
||||
| test.cpp:105:24:105:28 | begin | test.cpp:105:47:105:47 | Load |
|
||||
| test.cpp:105:36:105:38 | arr indirection [end] | test.cpp:105:40:105:42 | end |
|
||||
| test.cpp:105:40:105:42 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:105:40:105:42 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:105:40:105:42 | end | test.cpp:105:40:105:42 | Load |
|
||||
| test.cpp:109:20:109:22 | arr indirection [begin] | test.cpp:109:24:109:28 | begin |
|
||||
| test.cpp:109:20:109:22 | arr indirection [begin] | test.cpp:109:47:109:47 | Load |
|
||||
| test.cpp:109:24:109:28 | begin | test.cpp:109:47:109:47 | Load |
|
||||
| test.cpp:109:36:109:38 | arr indirection [end] | test.cpp:109:40:109:42 | end |
|
||||
| test.cpp:109:40:109:42 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:109:40:109:42 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:109:40:109:42 | end | test.cpp:109:40:109:42 | Load |
|
||||
| test.cpp:113:20:113:22 | arr indirection [begin] | test.cpp:113:24:113:28 | begin |
|
||||
| test.cpp:113:20:113:22 | arr indirection [begin] | test.cpp:113:46:113:46 | Load |
|
||||
| test.cpp:113:24:113:28 | begin | test.cpp:113:46:113:46 | Load |
|
||||
| test.cpp:113:35:113:37 | arr indirection [end] | test.cpp:113:39:113:41 | end |
|
||||
| test.cpp:113:39:113:41 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:113:39:113:41 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
|
||||
| test.cpp:113:39:113:41 | end | test.cpp:113:39:113:41 | Load |
|
||||
| test.cpp:119:18:119:25 | call to mk_array [begin] | test.cpp:104:27:104:29 | arr [begin] |
|
||||
| test.cpp:119:18:119:25 | call to mk_array [end] | test.cpp:104:27:104:29 | arr [end] |
|
||||
@@ -558,8 +462,6 @@ edges
|
||||
| test.cpp:144:16:144:18 | Load indirection [begin] | test.cpp:144:21:144:25 | begin |
|
||||
| test.cpp:144:16:144:32 | ... + ... | test.cpp:144:5:144:32 | Store |
|
||||
| test.cpp:144:21:144:25 | Load | test.cpp:144:5:144:32 | Store |
|
||||
| test.cpp:144:21:144:25 | Load | test.cpp:144:5:144:32 | Store |
|
||||
| test.cpp:144:21:144:25 | Load | test.cpp:144:16:144:32 | ... + ... |
|
||||
| test.cpp:144:21:144:25 | Load | test.cpp:144:16:144:32 | ... + ... |
|
||||
| test.cpp:144:21:144:25 | begin | test.cpp:144:21:144:25 | Load |
|
||||
| test.cpp:150:20:150:29 | Call indirection [begin] | test.cpp:152:20:152:22 | Load indirection [begin] |
|
||||
@@ -574,7 +476,6 @@ edges
|
||||
| test.cpp:156:25:156:29 | begin | test.cpp:156:49:156:49 | Load |
|
||||
| test.cpp:156:37:156:39 | Load indirection [end] | test.cpp:156:42:156:44 | end |
|
||||
| test.cpp:156:42:156:44 | Load | test.cpp:157:9:157:14 | Store: ... = ... |
|
||||
| test.cpp:156:42:156:44 | Load | test.cpp:157:9:157:14 | Store: ... = ... |
|
||||
| test.cpp:156:42:156:44 | end | test.cpp:156:42:156:44 | Load |
|
||||
| test.cpp:160:20:160:22 | Load indirection [begin] | test.cpp:160:25:160:29 | begin |
|
||||
| test.cpp:160:20:160:22 | Load indirection [begin] | test.cpp:160:48:160:48 | Load |
|
||||
@@ -590,21 +491,18 @@ edges
|
||||
| test.cpp:166:25:166:29 | begin | test.cpp:166:49:166:49 | Load |
|
||||
| test.cpp:166:37:166:39 | Load indirection [end] | test.cpp:166:42:166:44 | end |
|
||||
| test.cpp:166:42:166:44 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:166:42:166:44 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:166:42:166:44 | end | test.cpp:166:42:166:44 | Load |
|
||||
| test.cpp:170:20:170:22 | Load indirection [begin] | test.cpp:170:25:170:29 | begin |
|
||||
| test.cpp:170:20:170:22 | Load indirection [begin] | test.cpp:170:49:170:49 | Load |
|
||||
| test.cpp:170:25:170:29 | begin | test.cpp:170:49:170:49 | Load |
|
||||
| test.cpp:170:37:170:39 | Load indirection [end] | test.cpp:170:42:170:44 | end |
|
||||
| test.cpp:170:42:170:44 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:170:42:170:44 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:170:42:170:44 | end | test.cpp:170:42:170:44 | Load |
|
||||
| test.cpp:174:20:174:22 | Load indirection [begin] | test.cpp:174:25:174:29 | begin |
|
||||
| test.cpp:174:20:174:22 | Load indirection [begin] | test.cpp:174:48:174:48 | Load |
|
||||
| test.cpp:174:25:174:29 | begin | test.cpp:174:48:174:48 | Load |
|
||||
| test.cpp:174:36:174:38 | Load indirection [end] | test.cpp:174:41:174:43 | end |
|
||||
| test.cpp:174:41:174:43 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:174:41:174:43 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
|
||||
| test.cpp:174:41:174:43 | end | test.cpp:174:41:174:43 | Load |
|
||||
| test.cpp:180:19:180:28 | call to mk_array_p indirection [begin] | test.cpp:165:29:165:31 | arr indirection [begin] |
|
||||
| test.cpp:180:19:180:28 | call to mk_array_p indirection [end] | test.cpp:165:29:165:31 | arr indirection [end] |
|
||||
@@ -614,19 +512,9 @@ edges
|
||||
| test.cpp:194:23:194:28 | call to malloc | test.cpp:201:5:201:5 | Load |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:195:17:195:23 | ... + ... |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:195:17:195:23 | ... + ... |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:195:17:195:23 | ... + ... |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:195:17:195:23 | ... + ... |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:195:17:195:23 | Store |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:195:17:195:23 | Store |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:195:17:195:23 | Store |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:195:17:195:23 | Store |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:197:20:197:22 | Load |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:197:20:197:22 | Load |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:197:20:197:22 | Load |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:197:20:197:22 | Load |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:201:5:201:12 | access to array |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:201:5:201:12 | access to array |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:201:5:201:12 | access to array |
|
||||
| test.cpp:195:17:195:17 | Load | test.cpp:201:5:201:12 | access to array |
|
||||
| test.cpp:195:17:195:23 | ... + ... | test.cpp:195:17:195:23 | Store |
|
||||
| test.cpp:195:17:195:23 | ... + ... | test.cpp:195:17:195:23 | Store |
|
||||
@@ -637,29 +525,15 @@ edges
|
||||
| test.cpp:195:17:195:23 | Store | test.cpp:201:5:201:19 | Store: ... = ... |
|
||||
| test.cpp:195:17:195:23 | Store | test.cpp:201:5:201:19 | Store: ... = ... |
|
||||
| test.cpp:197:20:197:22 | Load | test.cpp:201:5:201:19 | Store: ... = ... |
|
||||
| test.cpp:197:20:197:22 | Load | test.cpp:201:5:201:19 | Store: ... = ... |
|
||||
| test.cpp:201:5:201:12 | access to array | test.cpp:201:5:201:19 | Store: ... = ... |
|
||||
| test.cpp:201:5:201:12 | access to array | test.cpp:201:5:201:19 | Store: ... = ... |
|
||||
| test.cpp:205:23:205:28 | call to malloc | test.cpp:206:17:206:17 | Load |
|
||||
| test.cpp:205:23:205:28 | call to malloc | test.cpp:208:15:208:15 | Load |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:206:17:206:23 | ... + ... |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:206:17:206:23 | ... + ... |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:206:17:206:23 | ... + ... |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:206:17:206:23 | ... + ... |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:206:17:206:23 | Store |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:206:17:206:23 | Store |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:206:17:206:23 | Store |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:206:17:206:23 | Store |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:209:12:209:14 | Load |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:209:12:209:14 | Load |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:209:12:209:14 | Load |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:209:12:209:14 | Load |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:213:5:213:6 | * ... |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:213:5:213:6 | * ... |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:213:5:213:6 | * ... |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:213:5:213:6 | * ... |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:213:6:213:6 | Load |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:213:6:213:6 | Load |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:213:6:213:6 | Load |
|
||||
| test.cpp:206:17:206:17 | Load | test.cpp:213:6:213:6 | Load |
|
||||
| test.cpp:206:17:206:23 | ... + ... | test.cpp:206:17:206:23 | Store |
|
||||
@@ -671,8 +545,6 @@ edges
|
||||
| test.cpp:206:17:206:23 | Store | test.cpp:213:5:213:13 | Store: ... = ... |
|
||||
| test.cpp:206:17:206:23 | Store | test.cpp:213:5:213:13 | Store: ... = ... |
|
||||
| test.cpp:209:12:209:14 | Load | test.cpp:213:5:213:13 | Store: ... = ... |
|
||||
| test.cpp:209:12:209:14 | Load | test.cpp:213:5:213:13 | Store: ... = ... |
|
||||
| test.cpp:213:5:213:6 | * ... | test.cpp:213:5:213:13 | Store: ... = ... |
|
||||
| test.cpp:213:5:213:6 | * ... | test.cpp:213:5:213:13 | Store: ... = ... |
|
||||
| test.cpp:213:6:213:6 | Load | test.cpp:213:5:213:6 | * ... |
|
||||
| test.cpp:213:6:213:6 | Load | test.cpp:213:5:213:13 | Store: ... = ... |
|
||||
|
||||
@@ -6,6 +6,8 @@ uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
storeStepIsLocal
|
||||
compatibleTypesReflexive
|
||||
unreachableNodeCCtx
|
||||
localCallNodes
|
||||
@@ -87,4 +89,7 @@ postWithInFlow
|
||||
| test.cpp:465:3:465:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:465:4:465:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:470:22:470:22 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:499:3:499:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:499:4:499:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:505:35:505:35 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
|
||||
@@ -21,6 +21,8 @@ uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
storeStepIsLocal
|
||||
compatibleTypesReflexive
|
||||
unreachableNodeCCtx
|
||||
localCallNodes
|
||||
@@ -582,6 +584,13 @@ postWithInFlow
|
||||
| test.cpp:489:7:489:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:491:5:491:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:494:5:494:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:499:3:499:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:499:4:499:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:499:4:499:4 | p [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:504:7:504:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:505:34:505:35 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:505:34:505:35 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:505:35:505:35 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| true_upon_entry.cpp:9:7:9:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| true_upon_entry.cpp:10:12:10:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| true_upon_entry.cpp:10:27:10:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -87,7 +87,7 @@ Top *identity(Top *top) {
|
||||
|
||||
void callIdentityFunctions(Top *top, Bottom *bottom) {
|
||||
identity(bottom)->isSink(source()); // $ MISSING: ast,ir
|
||||
identity(top)->isSink(source()); // now flow
|
||||
identity(top)->isSink(source()); // no flow
|
||||
}
|
||||
|
||||
using SinkFunctionType = void (*)(int);
|
||||
|
||||
@@ -329,21 +329,21 @@ namespace NestedTests {
|
||||
namespace FlowThroughGlobals {
|
||||
int globalVar;
|
||||
|
||||
int taintGlobal() {
|
||||
void taintGlobal() {
|
||||
globalVar = source();
|
||||
}
|
||||
|
||||
int f() {
|
||||
void f() {
|
||||
sink(globalVar); // $ ir=333:17 ir=347:17 // tainted or clean? Not sure.
|
||||
taintGlobal();
|
||||
sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast
|
||||
}
|
||||
|
||||
int calledAfterTaint() {
|
||||
void calledAfterTaint() {
|
||||
sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast
|
||||
}
|
||||
|
||||
int taintAndCall() {
|
||||
void taintAndCall() {
|
||||
globalVar = source();
|
||||
calledAfterTaint();
|
||||
sink(globalVar); // $ ast ir=333:17 ir=347:17
|
||||
@@ -494,3 +494,14 @@ void regression_with_phi_flow(int clean1) {
|
||||
x = source();
|
||||
}
|
||||
}
|
||||
|
||||
int intOutparamSourceMissingReturn(int *p) {
|
||||
*p = source();
|
||||
// return deliberately omitted to test IR dataflow behavior
|
||||
}
|
||||
|
||||
void viaOutparamMissingReturn() {
|
||||
int x = 0;
|
||||
intOutparamSourceMissingReturn(&x);
|
||||
sink(x); // $ ast,ir
|
||||
}
|
||||
|
||||
@@ -77,27 +77,9 @@ module IRTest {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(GlobalOrNamespaceVariable var | var.getName().matches("flowTestGlobal%") |
|
||||
writesVariable(n1.asInstruction(), var) and
|
||||
var = n2.asVariable()
|
||||
or
|
||||
readsVariable(n2.asInstruction(), var) and
|
||||
var = n1.asVariable()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isBarrier(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") or
|
||||
barrier = DataFlow::InstructionBarrierGuard<testBarrierGuard/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
private predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
private predicate writesVariable(StoreInstruction store, Variable var) {
|
||||
store.getDestinationAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ int source();
|
||||
void sink(...);
|
||||
bool random();
|
||||
|
||||
int test1() {
|
||||
void test1() {
|
||||
int x = source();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
x = 0;
|
||||
@@ -13,7 +13,7 @@ int test1() {
|
||||
sink(x); // $ SPURIOUS: ir
|
||||
}
|
||||
|
||||
int test2(int iterations) {
|
||||
void test2(int iterations) {
|
||||
int x = source();
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
x = 0;
|
||||
@@ -21,7 +21,7 @@ int test2(int iterations) {
|
||||
sink(x); // $ ast,ir
|
||||
}
|
||||
|
||||
int test3() {
|
||||
void test3() {
|
||||
int x = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
x = source();
|
||||
@@ -29,7 +29,7 @@ int test3() {
|
||||
sink(x); // $ ast,ir
|
||||
}
|
||||
|
||||
int test4() {
|
||||
void test4() {
|
||||
int x = source();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (random())
|
||||
@@ -39,7 +39,7 @@ int test4() {
|
||||
sink(x); // $ ast,ir
|
||||
}
|
||||
|
||||
int test5() {
|
||||
void test5() {
|
||||
int x = source();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (random())
|
||||
@@ -49,7 +49,7 @@ int test5() {
|
||||
sink(x); // $ ast,ir
|
||||
}
|
||||
|
||||
int test6() {
|
||||
void test6() {
|
||||
int y;
|
||||
int x = source();
|
||||
for (int i = 0; i < 10 && (y = 1); i++) {
|
||||
@@ -57,7 +57,7 @@ int test6() {
|
||||
sink(x); // $ ast,ir
|
||||
}
|
||||
|
||||
int test7() {
|
||||
void test7() {
|
||||
int y;
|
||||
int x = source();
|
||||
for (int i = 0; i < 10 && (y = 1); i++) {
|
||||
@@ -66,7 +66,7 @@ int test7() {
|
||||
sink(x); // $ SPURIOUS: ir
|
||||
}
|
||||
|
||||
int test8() {
|
||||
void test8() {
|
||||
int x = source();
|
||||
// It appears to the analysis that the condition can exit after `i < 10`
|
||||
// without having assigned to `x`. That is an effect of how the
|
||||
@@ -78,7 +78,7 @@ int test8() {
|
||||
sink(x); // $ SPURIOUS: ast,ir
|
||||
}
|
||||
|
||||
int test9() {
|
||||
void test9() {
|
||||
int y;
|
||||
int x = source();
|
||||
for (int i = 0; (y = 1) && i < 10; i++) {
|
||||
@@ -86,21 +86,21 @@ int test9() {
|
||||
sink(x); // $ ast,ir
|
||||
}
|
||||
|
||||
int test10() {
|
||||
void test10() {
|
||||
int x = source();
|
||||
for (int i = 0; (x = 1) && i < 10; i++) {
|
||||
}
|
||||
sink(x); // no flow
|
||||
}
|
||||
|
||||
int test10(int b, int d) {
|
||||
void test10(int b, int d) {
|
||||
int i = 0;
|
||||
int x = source();
|
||||
if (b)
|
||||
goto L;
|
||||
for (; i < 10; i += d) {
|
||||
x = 0;
|
||||
L:
|
||||
L: ;
|
||||
}
|
||||
sink(x); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
storeStepIsLocal
|
||||
compatibleTypesReflexive
|
||||
unreachableNodeCCtx
|
||||
localCallNodes
|
||||
|
||||
@@ -15,6 +15,8 @@ uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
storeStepIsLocal
|
||||
compatibleTypesReflexive
|
||||
unreachableNodeCCtx
|
||||
localCallNodes
|
||||
|
||||
@@ -9,7 +9,7 @@ struct sockaddr {
|
||||
char* sa_data;
|
||||
};
|
||||
|
||||
int accept(int, const sockaddr*, int*);
|
||||
int accept(int, sockaddr*, int*);
|
||||
|
||||
void sink(sockaddr);
|
||||
|
||||
@@ -20,5 +20,5 @@ void test_accept() {
|
||||
int a = accept(s, &addr, &size);
|
||||
|
||||
sink(a); // $ ast=17:11 ir SPURIOUS: ast=18:12
|
||||
sink(addr); // $ ast,ir
|
||||
sink(addr); // $ ast=17:11 ir SPURIOUS: ast=18:12
|
||||
}
|
||||
|
||||
@@ -142,9 +142,14 @@
|
||||
| bsd.cpp:19:14:19:29 | sizeof(sockaddr) | bsd.cpp:20:29:20:32 | size | |
|
||||
| bsd.cpp:20:11:20:16 | call to accept | bsd.cpp:22:8:22:8 | a | |
|
||||
| bsd.cpp:20:18:20:18 | s | bsd.cpp:20:11:20:16 | call to accept | TAINT |
|
||||
| bsd.cpp:20:18:20:18 | s | bsd.cpp:20:21:20:25 | ref arg & ... | TAINT |
|
||||
| bsd.cpp:20:21:20:25 | & ... | bsd.cpp:20:11:20:16 | call to accept | TAINT |
|
||||
| bsd.cpp:20:21:20:25 | & ... | bsd.cpp:20:21:20:25 | ref arg & ... | TAINT |
|
||||
| bsd.cpp:20:21:20:25 | ref arg & ... | bsd.cpp:20:22:20:25 | addr [inner post update] | |
|
||||
| bsd.cpp:20:21:20:25 | ref arg & ... | bsd.cpp:23:8:23:11 | addr | |
|
||||
| bsd.cpp:20:22:20:25 | addr | bsd.cpp:20:11:20:16 | call to accept | TAINT |
|
||||
| bsd.cpp:20:22:20:25 | addr | bsd.cpp:20:21:20:25 | & ... | |
|
||||
| bsd.cpp:20:22:20:25 | addr | bsd.cpp:20:21:20:25 | ref arg & ... | TAINT |
|
||||
| bsd.cpp:20:28:20:32 | ref arg & ... | bsd.cpp:20:29:20:32 | size [inner post update] | |
|
||||
| bsd.cpp:20:29:20:32 | size | bsd.cpp:20:28:20:32 | & ... | |
|
||||
| constructor_delegation.cpp:8:2:8:8 | this | constructor_delegation.cpp:8:20:8:24 | constructor init of field x [pre-this] | |
|
||||
@@ -5964,6 +5969,7 @@
|
||||
| taint.cpp:172:10:172:15 | buffer | taint.cpp:172:3:172:8 | call to strcat | |
|
||||
| taint.cpp:172:10:172:15 | buffer | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
|
||||
| taint.cpp:172:10:172:15 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
|
||||
| taint.cpp:172:18:172:24 | tainted | taint.cpp:172:3:172:8 | call to strcat | TAINT |
|
||||
| taint.cpp:172:18:172:24 | tainted | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
|
||||
| taint.cpp:180:19:180:19 | p | taint.cpp:180:19:180:19 | p | |
|
||||
| taint.cpp:180:19:180:19 | p | taint.cpp:181:9:181:9 | p | |
|
||||
@@ -6373,12 +6379,14 @@
|
||||
| taint.cpp:561:9:561:13 | dest1 | taint.cpp:561:9:561:13 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:561:9:561:13 | ref arg dest1 | taint.cpp:560:24:560:28 | dest1 | |
|
||||
| taint.cpp:561:9:561:13 | ref arg dest1 | taint.cpp:562:7:562:11 | dest1 | |
|
||||
| taint.cpp:561:16:561:21 | source | taint.cpp:561:2:561:7 | call to strcat | TAINT |
|
||||
| taint.cpp:561:16:561:21 | source | taint.cpp:561:9:561:13 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:562:7:562:11 | ref arg dest1 | taint.cpp:560:24:560:28 | dest1 | |
|
||||
| taint.cpp:564:9:564:13 | dest2 | taint.cpp:564:2:564:7 | call to strcat | |
|
||||
| taint.cpp:564:9:564:13 | dest2 | taint.cpp:564:9:564:13 | ref arg dest2 | TAINT |
|
||||
| taint.cpp:564:9:564:13 | ref arg dest2 | taint.cpp:560:37:560:41 | dest2 | |
|
||||
| taint.cpp:564:9:564:13 | ref arg dest2 | taint.cpp:565:7:565:11 | dest2 | |
|
||||
| taint.cpp:564:16:564:20 | clean | taint.cpp:564:2:564:7 | call to strcat | TAINT |
|
||||
| taint.cpp:564:16:564:20 | clean | taint.cpp:564:9:564:13 | ref arg dest2 | TAINT |
|
||||
| taint.cpp:565:7:565:11 | ref arg dest2 | taint.cpp:560:37:560:41 | dest2 | |
|
||||
| taint.cpp:572:37:572:41 | dest1 | taint.cpp:572:37:572:41 | dest1 | |
|
||||
@@ -6405,9 +6413,12 @@
|
||||
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:572:37:572:41 | dest1 | |
|
||||
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:575:7:575:11 | dest1 | |
|
||||
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:576:8:576:12 | dest1 | |
|
||||
| taint.cpp:574:43:574:45 | ptr | taint.cpp:574:25:574:34 | call to _mbsncat_l | TAINT |
|
||||
| taint.cpp:574:43:574:45 | ptr | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:574:48:574:48 | n | taint.cpp:574:25:574:34 | call to _mbsncat_l | TAINT |
|
||||
| taint.cpp:574:48:574:48 | n | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:574:51:574:56 | ref arg source | taint.cpp:573:49:573:54 | source | |
|
||||
| taint.cpp:574:51:574:56 | source | taint.cpp:574:25:574:34 | call to _mbsncat_l | TAINT |
|
||||
| taint.cpp:574:51:574:56 | source | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
|
||||
| taint.cpp:575:7:575:11 | ref arg dest1 | taint.cpp:572:37:572:41 | dest1 | |
|
||||
| taint.cpp:575:7:575:11 | ref arg dest1 | taint.cpp:576:8:576:12 | dest1 | |
|
||||
@@ -6421,8 +6432,11 @@
|
||||
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:572:85:572:89 | dest3 | |
|
||||
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:581:7:581:11 | dest3 | |
|
||||
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:582:8:582:12 | dest3 | |
|
||||
| taint.cpp:580:43:580:45 | ptr | taint.cpp:580:25:580:34 | call to _mbsncat_l | TAINT |
|
||||
| taint.cpp:580:43:580:45 | ptr | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:580:48:580:48 | n | taint.cpp:580:25:580:34 | call to _mbsncat_l | TAINT |
|
||||
| taint.cpp:580:48:580:48 | n | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:580:51:580:55 | clean | taint.cpp:580:25:580:34 | call to _mbsncat_l | TAINT |
|
||||
| taint.cpp:580:51:580:55 | clean | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
|
||||
| taint.cpp:580:51:580:55 | ref arg clean | taint.cpp:573:32:573:36 | clean | |
|
||||
| taint.cpp:581:7:581:11 | ref arg dest3 | taint.cpp:572:85:572:89 | dest3 | |
|
||||
|
||||
@@ -574,8 +574,8 @@ void test__mbsncat_l(unsigned char* dest1, unsigned const char* ptr, unsigned ch
|
||||
unsigned char* dest2 = _mbsncat_l(dest1, ptr, n, source);
|
||||
sink(dest1); // $ SPURIOUS: ast,ir
|
||||
sink(*dest1); // $ ast,ir
|
||||
sink(dest2); // $ SPURIOUS: ir
|
||||
sink(*dest2); // $ ir
|
||||
sink(dest2); // $ SPURIOUS: ast,ir
|
||||
sink(*dest2); // $ ast,ir
|
||||
|
||||
unsigned char* dest4 = _mbsncat_l(dest3, ptr, n, clean);
|
||||
sink(dest3);
|
||||
|
||||
@@ -95,16 +95,7 @@ module IRTest {
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asConvertedExpr() = call.getAnArgument()
|
||||
or
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = call.getAnArgument() and
|
||||
sink.asConvertedExpr() instanceof ReferenceDereferenceExpr
|
||||
)
|
||||
or
|
||||
exists(ReadSideEffectInstruction read |
|
||||
read.getSideEffectOperand() = sink.asOperand() and
|
||||
read.getPrimaryInstruction().(CallInstruction).getStaticCallTarget().hasName("sink")
|
||||
sink.asExpr() = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
typedef void *va_list;
|
||||
|
||||
int myPrintf(const char *format, ...) __attribute__((format(printf, 1, 2)));
|
||||
int mySprintf(char *buffer, const char *format, ...) __attribute__((format(__printf__, 2, 3)));
|
||||
int myVprintf(const char *format, va_list arg) __attribute__((format(printf, 1, 0)));
|
||||
@@ -0,0 +1,2 @@
|
||||
| AttributeFormattingFunction.cpp:4:5:4:12 | myPrintf | 0 | char | wchar_t | wchar_t |
|
||||
| AttributeFormattingFunction.cpp:5:5:5:13 | mySprintf | 1 | char | wchar_t | wchar_t |
|
||||
@@ -0,0 +1,5 @@
|
||||
import cpp
|
||||
|
||||
from AttributeFormattingFunction f
|
||||
select f, f.getFormatParameterIndex(), concat(f.getDefaultCharType().toString(), ", "),
|
||||
concat(f.getWideCharType().toString(), ", "), concat(f.getNonDefaultCharType().toString(), ", ")
|
||||
@@ -0,0 +1,3 @@
|
||||
| AttributeFormattingFunction.cpp:4:54:4:59 | format | printf | 0 | 1 |
|
||||
| AttributeFormattingFunction.cpp:5:69:5:74 | format | __printf__ | 1 | 2 |
|
||||
| AttributeFormattingFunction.cpp:6:63:6:68 | format | printf | 0 | |
|
||||
@@ -0,0 +1,5 @@
|
||||
import cpp
|
||||
|
||||
from FormatAttribute fa
|
||||
select fa, fa.getArchetype(), concat(fa.getFormatIndex().toString(), ", "),
|
||||
concat(fa.getFirstFormatArgIndex().toString(), ", ")
|
||||
@@ -0,0 +1,53 @@
|
||||
| test.c:14:9:14:10 | | 1 |
|
||||
| test.c:15:9:15:14 | | 2 |
|
||||
| test.c:16:9:16:12 | \t | 2 |
|
||||
| test.c:17:9:17:12 | %% | 2 |
|
||||
| test.c:20:9:20:12 | %c | 2 |
|
||||
| test.c:21:9:21:16 | %c%c%c | 4 |
|
||||
| test.c:24:9:24:23 | Hello, world! | 14 |
|
||||
| test.c:25:9:25:12 | %s | 14 |
|
||||
| test.c:26:9:26:14 | %.4s | 5 |
|
||||
| test.c:27:9:27:16 | %s, %s | 14 |
|
||||
| test.c:30:9:30:12 | %i | 12 |
|
||||
| test.c:31:9:31:14 | %lli | 12 |
|
||||
| test.c:32:9:32:12 | %i | 12 |
|
||||
| test.c:33:9:33:14 | %lli | 21 |
|
||||
| test.c:34:9:34:12 | %d | 12 |
|
||||
| test.c:35:9:35:12 | %u | 11 |
|
||||
| test.c:36:9:36:12 | %x | 9 |
|
||||
| test.c:37:9:37:12 | %X | 9 |
|
||||
| test.c:38:9:38:13 | %#x | 11 |
|
||||
| test.c:39:9:39:12 | %o | 12 |
|
||||
| test.c:40:9:40:13 | %#o | 13 |
|
||||
| test.c:43:9:43:12 | %f | 318 |
|
||||
| test.c:44:9:44:14 | %.2f | 314 |
|
||||
| test.c:45:9:45:12 | %e | 15 |
|
||||
| test.c:59:10:59:14 | %Ii | 12 |
|
||||
| test.c:66:10:66:14 | %zu | 21 |
|
||||
| test.c:67:10:67:14 | %Zu | 21 |
|
||||
| test.c:74:10:74:14 | %lc | 2 |
|
||||
| test.c:78:9:78:20 | %2$i, %1$i | 5 |
|
||||
| test.c:79:9:79:20 | %2$i, %1$i | 25 |
|
||||
| test.c:81:9:81:24 | %2$02i %1$4.2f | |
|
||||
| test.c:85:10:85:18 | %2$*1$d | |
|
||||
| test.c:86:10:86:19 | %2$0*1$d | |
|
||||
| test.c:92:10:92:19 | %2$.*1$f | |
|
||||
| test.c:99:10:99:12 | # | 2 |
|
||||
| test.c:100:10:100:13 | %% | 2 |
|
||||
| test.c:101:10:101:15 | %%%% | 3 |
|
||||
| test.c:102:10:102:15 | %%%f | 319 |
|
||||
| test.c:103:10:103:17 | %%%%%f | 320 |
|
||||
| test.c:104:10:104:18 | %4.2f%% | 315 |
|
||||
| test.c:105:10:105:17 | %%%f%% | 320 |
|
||||
| test.c:112:10:112:13 | %f | 318 |
|
||||
| test.c:113:10:113:15 | %.1f | 313 |
|
||||
| test.c:114:10:114:14 | %1f | 318 |
|
||||
| test.c:115:10:115:16 | %1.1f | 313 |
|
||||
| test.c:116:10:116:13 | %e | 15 |
|
||||
| test.c:117:10:117:15 | %.2e | 11 |
|
||||
| test.c:118:10:118:14 | %3e | 15 |
|
||||
| test.c:119:10:119:16 | %3.2e | 11 |
|
||||
| test.c:120:10:120:13 | %g | 15 |
|
||||
| test.c:121:10:121:15 | %.1g | 10 |
|
||||
| test.c:122:10:122:14 | %4g | 15 |
|
||||
| test.c:123:10:123:16 | %4.1g | 10 |
|
||||
@@ -0,0 +1,4 @@
|
||||
import semmle.code.cpp.commons.Printf
|
||||
|
||||
from FormatLiteral fl
|
||||
select fl, concat(fl.getMaxConvertedLength().toString(), ", ")
|
||||
@@ -0,0 +1,51 @@
|
||||
| test.c:20:9:20:12 | %c | 0 | | c | | file://:0:0:0:0 | char |
|
||||
| test.c:21:9:21:16 | %c%c%c | 0 | | c | | file://:0:0:0:0 | char |
|
||||
| test.c:21:9:21:16 | %c%c%c | 1 | | c | | file://:0:0:0:0 | char |
|
||||
| test.c:21:9:21:16 | %c%c%c | 2 | | c | | file://:0:0:0:0 | char |
|
||||
| test.c:25:9:25:12 | %s | 0 | | s | | file://:0:0:0:0 | char * |
|
||||
| test.c:26:9:26:14 | %.4s | 0 | | s | | file://:0:0:0:0 | char * |
|
||||
| test.c:27:9:27:16 | %s, %s | 0 | | s | | file://:0:0:0:0 | char * |
|
||||
| test.c:27:9:27:16 | %s, %s | 1 | | s | | file://:0:0:0:0 | char * |
|
||||
| test.c:30:9:30:12 | %i | 0 | | i | | file://:0:0:0:0 | int |
|
||||
| test.c:31:9:31:14 | %lli | 0 | | i | ll | file://:0:0:0:0 | long long |
|
||||
| test.c:32:9:32:12 | %i | 0 | | i | | file://:0:0:0:0 | int |
|
||||
| test.c:33:9:33:14 | %lli | 0 | | i | ll | file://:0:0:0:0 | long long |
|
||||
| test.c:34:9:34:12 | %d | 0 | | d | | file://:0:0:0:0 | int |
|
||||
| test.c:35:9:35:12 | %u | 0 | | u | | file://:0:0:0:0 | unsigned int |
|
||||
| test.c:36:9:36:12 | %x | 0 | | x | | file://:0:0:0:0 | unsigned int |
|
||||
| test.c:37:9:37:12 | %X | 0 | | X | | file://:0:0:0:0 | unsigned int |
|
||||
| test.c:38:9:38:13 | %#x | 0 | | x | | file://:0:0:0:0 | unsigned int |
|
||||
| test.c:39:9:39:12 | %o | 0 | | o | | file://:0:0:0:0 | unsigned int |
|
||||
| test.c:40:9:40:13 | %#o | 0 | | o | | file://:0:0:0:0 | unsigned int |
|
||||
| test.c:43:9:43:12 | %f | 0 | | f | | file://:0:0:0:0 | double |
|
||||
| test.c:44:9:44:14 | %.2f | 0 | | f | | file://:0:0:0:0 | double |
|
||||
| test.c:45:9:45:12 | %e | 0 | | e | | file://:0:0:0:0 | double |
|
||||
| test.c:59:10:59:14 | %Ii | 0 | | i | | file://:0:0:0:0 | int |
|
||||
| test.c:66:10:66:14 | %zu | 0 | | u | z | test.c:50:27:50:32 | size_t |
|
||||
| test.c:67:10:67:14 | %Zu | 0 | | u | Z | test.c:50:27:50:32 | size_t |
|
||||
| test.c:74:10:74:14 | %lc | 0 | | c | l | file://:0:0:0:0 | wchar_t |
|
||||
| test.c:78:9:78:20 | %2$i, %1$i | 0 | 2$ | i | | file://:0:0:0:0 | int |
|
||||
| test.c:78:9:78:20 | %2$i, %1$i | 1 | 1$ | i | | file://:0:0:0:0 | int |
|
||||
| test.c:79:9:79:20 | %2$i, %1$i | 0 | 2$ | i | | file://:0:0:0:0 | int |
|
||||
| test.c:79:9:79:20 | %2$i, %1$i | 1 | 1$ | i | | file://:0:0:0:0 | int |
|
||||
| test.c:81:9:81:24 | %2$02i %1$4.2f | 0 | 2$ | i | | file://:0:0:0:0 | int |
|
||||
| test.c:81:9:81:24 | %2$02i %1$4.2f | 1 | 1$ | f | | file://:0:0:0:0 | double |
|
||||
| test.c:85:10:85:18 | %2$*1$d | 0 | 2$ | d | | file://:0:0:0:0 | int |
|
||||
| test.c:86:10:86:19 | %2$0*1$d | 0 | 2$ | d | | file://:0:0:0:0 | int |
|
||||
| test.c:92:10:92:19 | %2$.*1$f | 0 | 2$ | f | | file://:0:0:0:0 | double |
|
||||
| test.c:102:10:102:15 | %%%f | 0 | | f | | file://:0:0:0:0 | double |
|
||||
| test.c:103:10:103:17 | %%%%%f | 0 | | f | | file://:0:0:0:0 | double |
|
||||
| test.c:104:10:104:18 | %4.2f%% | 0 | | f | | file://:0:0:0:0 | double |
|
||||
| test.c:105:10:105:17 | %%%f%% | 0 | | f | | file://:0:0:0:0 | double |
|
||||
| test.c:112:10:112:13 | %f | 0 | | f | | file://:0:0:0:0 | double |
|
||||
| test.c:113:10:113:15 | %.1f | 0 | | f | | file://:0:0:0:0 | double |
|
||||
| test.c:114:10:114:14 | %1f | 0 | | f | | file://:0:0:0:0 | double |
|
||||
| test.c:115:10:115:16 | %1.1f | 0 | | f | | file://:0:0:0:0 | double |
|
||||
| test.c:116:10:116:13 | %e | 0 | | e | | file://:0:0:0:0 | double |
|
||||
| test.c:117:10:117:15 | %.2e | 0 | | e | | file://:0:0:0:0 | double |
|
||||
| test.c:118:10:118:14 | %3e | 0 | | e | | file://:0:0:0:0 | double |
|
||||
| test.c:119:10:119:16 | %3.2e | 0 | | e | | file://:0:0:0:0 | double |
|
||||
| test.c:120:10:120:13 | %g | 0 | | g | | file://:0:0:0:0 | double |
|
||||
| test.c:121:10:121:15 | %.1g | 0 | | g | | file://:0:0:0:0 | double |
|
||||
| test.c:122:10:122:14 | %4g | 0 | | g | | file://:0:0:0:0 | double |
|
||||
| test.c:123:10:123:16 | %4.1g | 0 | | g | | file://:0:0:0:0 | double |
|
||||
@@ -0,0 +1,6 @@
|
||||
import semmle.code.cpp.commons.Printf
|
||||
|
||||
from FormatLiteral fl, int i
|
||||
select fl, i, concat(fl.getParameterField(i).toString(), ", "), fl.getConversionChar(i),
|
||||
fl.getLength(i), concat(fl.getConversionType(i).getLocation().toString(), ", "),
|
||||
concat(fl.getConversionType(i).toString(), ", ")
|
||||
125
cpp/ql/test/library-tests/printf/formatLiteral/test.c
Normal file
125
cpp/ql/test/library-tests/printf/formatLiteral/test.c
Normal file
@@ -0,0 +1,125 @@
|
||||
/** standard printf functions */
|
||||
|
||||
int printf(const char *format, ...);
|
||||
|
||||
/** test program */
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
long long int lli;
|
||||
double d;
|
||||
int i;
|
||||
|
||||
// constant expressions
|
||||
printf("");
|
||||
printf("\x20");
|
||||
printf("\t");
|
||||
printf("%%");
|
||||
|
||||
// characters
|
||||
printf("%c", 'a');
|
||||
printf("%c%c%c", 'a', 'b', 'c');
|
||||
|
||||
// strings
|
||||
printf("Hello, world!");
|
||||
printf("%s", "Hello, world!");
|
||||
printf("%.4s", "Hello, world!");
|
||||
printf("%s, %s", "Hello", "world!");
|
||||
|
||||
// integers
|
||||
printf("%i", i);
|
||||
printf("%lli", i);
|
||||
printf("%i", lli);
|
||||
printf("%lli", lli);
|
||||
printf("%d", i);
|
||||
printf("%u", i);
|
||||
printf("%x", i);
|
||||
printf("%X", i);
|
||||
printf("%#x", i);
|
||||
printf("%o", i);
|
||||
printf("%#o", i);
|
||||
|
||||
// doubles
|
||||
printf("%f", d);
|
||||
printf("%.2f", d);
|
||||
printf("%e", d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef long unsigned int size_t;
|
||||
typedef unsigned int wint_t;
|
||||
|
||||
void more_cases(int a, int b)
|
||||
{
|
||||
// integers
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("%Ii", i); // glibc 2.2 'I' prefix
|
||||
}
|
||||
|
||||
// size_t
|
||||
{
|
||||
size_t st;
|
||||
|
||||
printf("%zu", st); // size_t
|
||||
printf("%Zu", st); // non-standard synonym for 'z'
|
||||
}
|
||||
|
||||
// wint_t
|
||||
{
|
||||
wint_t wt;
|
||||
|
||||
printf("%lc", wt); // wide character
|
||||
}
|
||||
|
||||
// posix indexed format arguments
|
||||
printf("%2$i, %1$i", 1, 2); // '2, 1'
|
||||
printf("%2$i, %1$i", a, b);
|
||||
|
||||
printf("%2$02i %1$4.2f", 3.3333f, 6); // 06, 3.33
|
||||
{
|
||||
int width, num;
|
||||
|
||||
printf("%2$*1$d", width, num);
|
||||
printf("%2$0*1$d", width, num);
|
||||
}
|
||||
{
|
||||
int precision;
|
||||
float num;
|
||||
|
||||
printf("%2$.*1$f", precision, num);
|
||||
}
|
||||
|
||||
// %%
|
||||
{
|
||||
float num;
|
||||
|
||||
printf("#");
|
||||
printf("%%");
|
||||
printf("%%%%");
|
||||
printf("%%%f", num);
|
||||
printf("%%%%%f", num);
|
||||
printf("%4.2f%%", num);
|
||||
printf("%%%f%%", num);
|
||||
}
|
||||
|
||||
// more tests of width and precision
|
||||
{
|
||||
float num;
|
||||
|
||||
printf("%f", num);
|
||||
printf("%.1f", num);
|
||||
printf("%1f", num);
|
||||
printf("%1.1f", num);
|
||||
printf("%e", num);
|
||||
printf("%.2e", num);
|
||||
printf("%3e", num);
|
||||
printf("%3.2e", num);
|
||||
printf("%g", num);
|
||||
printf("%.1g", num);
|
||||
printf("%4g", num);
|
||||
printf("%4.1g", num);
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,8 @@ uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
storeStepIsLocal
|
||||
compatibleTypesReflexive
|
||||
unreachableNodeCCtx
|
||||
localCallNodes
|
||||
|
||||
@@ -1463,6 +1463,8 @@ uniqueNodeToString
|
||||
missingToString
|
||||
parameterCallable
|
||||
localFlowIsLocal
|
||||
readStepIsLocal
|
||||
storeStepIsLocal
|
||||
compatibleTypesReflexive
|
||||
unreachableNodeCCtx
|
||||
localCallNodes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user