Merge branch 'main' into python-clean-up-import-resolution

This commit is contained in:
Taus
2022-11-17 22:47:50 +00:00
2847 changed files with 159486 additions and 141642 deletions

View File

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

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

View File

@@ -1,14 +1,22 @@
name: Fetch CodeQL name: Fetch CodeQL
description: Fetches the latest version of CodeQL description: Fetches the latest version of CodeQL
inputs:
channel:
description: 'The CodeQL channel to use'
required: false
default: 'nightly'
runs: runs:
using: composite using: composite
steps: steps:
- name: Fetch CodeQL - name: Fetch CodeQL
shell: bash 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: env:
GITHUB_TOKEN: ${{ github.token }} 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
View File

@@ -43,3 +43,14 @@ documentation:
"QL-for-QL": "QL-for-QL":
- ql/**/* - ql/**/*
- .github/workflows/ql-for-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/**/*

View 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

View File

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

71
.github/workflows/compile-queries.yml vendored Normal file
View 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

View File

@@ -23,19 +23,6 @@ defaults:
working-directory: javascript/ql/experimental/adaptivethreatmodeling working-directory: javascript/ql/experimental/adaptivethreatmodeling
jobs: 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: qlcompile:
name: Check QL compilation name: Check QL compilation
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@@ -24,13 +24,13 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Find codeql - name: Find codeql
id: find-codeql id: find-codeql
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
with: with:
languages: javascript # does not matter languages: javascript # does not matter
- name: Get CodeQL version - name: Get CodeQL version
id: get-codeql-version id: get-codeql-version
run: | 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 shell: bash
env: env:
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }} CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
@@ -133,7 +133,7 @@ jobs:
env: env:
CONF: ./ql-for-ql-config.yml CONF: ./ql-for-ql-config.yml
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
with: with:
languages: ql languages: ql
db-location: ${{ runner.temp }}/db db-location: ${{ runner.temp }}/db
@@ -145,7 +145,7 @@ jobs:
PACK: ${{ runner.temp }}/pack PACK: ${{ runner.temp }}/pack
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca uses: github/codeql-action/analyze@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
with: with:
category: "ql-for-ql" category: "ql-for-ql"
- name: Copy sarif file to CWD - name: Copy sarif file to CWD

View File

@@ -25,7 +25,7 @@ jobs:
- name: Find codeql - name: Find codeql
id: find-codeql id: find-codeql
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
with: with:
languages: javascript # does not matter languages: javascript # does not matter
- uses: actions/cache@v3 - uses: actions/cache@v3

View File

@@ -22,7 +22,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Find codeql - name: Find codeql
id: find-codeql id: find-codeql
uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
with: with:
languages: javascript # does not matter languages: javascript # does not matter
- uses: actions/cache@v3 - uses: actions/cache@v3

View File

@@ -96,8 +96,8 @@ jobs:
- name: Build Query Pack - name: Build Query Pack
run: | run: |
codeql pack create ../shared/ssa --output target/packs 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 create ql/lib --output target/packs
codeql pack install ql/src
codeql pack create ql/src --output target/packs codeql pack create ql/src --output target/packs
PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*) PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*)
codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
@@ -202,7 +202,7 @@ jobs:
echo 'name: sample-tests echo 'name: sample-tests
version: 0.0.0 version: 0.0.0
dependencies: dependencies:
codeql/ruby-all: 0.0.1 codeql/ruby-all: "*"
extractor: ruby extractor: ruby
tests: . tests: .
' > qlpack.yml ' > qlpack.yml

View File

@@ -28,13 +28,6 @@ defaults:
working-directory: ruby working-directory: ruby
jobs: 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: qlcompile:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

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

View File

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

View File

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

View File

@@ -19,7 +19,7 @@ repos:
rev: v1.6.0 rev: v1.6.0
hooks: hooks:
- id: autopep8 - id: autopep8
files: ^swift/codegen/.*\.py files: ^swift/.*\.py
- repo: local - repo: local
hooks: hooks:
@@ -31,7 +31,7 @@ repos:
- id: sync-files - id: sync-files
name: Fix files required to be identical name: Fix files required to be identical
files: \.(qll?|qhelp|swift)$ files: \.(qll?|qhelp|swift)$|^config/identical-files\.json$
language: system language: system
entry: python3 config/sync-files.py --latest entry: python3 config/sync-files.py --latest
pass_filenames: false pass_filenames: false
@@ -44,7 +44,7 @@ repos:
- id: swift-codegen - id: swift-codegen
name: Run Swift checked in code generation 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 language: system
entry: bazel run //swift/codegen -- --quiet entry: bazel run //swift/codegen -- --quiet
pass_filenames: false pass_filenames: false

View File

@@ -20,9 +20,9 @@
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go /java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
# CodeQL tools and associated docs # CodeQL tools and associated docs
/docs/codeql-cli/ @github/codeql-cli-reviewers /docs/codeql/codeql-cli/ @github/codeql-cli-reviewers
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers /docs/codeql/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
/docs/ql-language-reference/ @github/codeql-frontend-reviewers /docs/codeql/ql-language-reference/ @github/codeql-frontend-reviewers
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers /docs/query-*-style-guide.md @github/codeql-analysis-reviewers
# QL for QL reviewers # QL for QL reviewers
@@ -40,8 +40,9 @@ WORKSPACE.bazel @github/codeql-ci-reviewers
# Workflows # Workflows
/.github/workflows/ @github/codeql-ci-reviewers /.github/workflows/ @github/codeql-ci-reviewers
/.github/workflows/atm-* @github/codeql-ml-powered-queries-reviewers
/.github/workflows/go-* @github/codeql-go /.github/workflows/go-* @github/codeql-go
/.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers /.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/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
/.github/workflows/ruby-* @github/codeql-ruby /.github/workflows/ruby-* @github/codeql-ruby
/.github/workflows/swift-* @github/codeql-c /.github/workflows/swift.yml @github/codeql-c

View File

@@ -52,7 +52,7 @@
| Unneeded defensive code | More true positive and fewer false positive results | This query now recognizes additional defensive code patterns. | | 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. | | 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 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. | | 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 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. | | Useless conditional | Fewer results | Additional defensive coding patterns are now ignored. |

View File

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

View File

@@ -91,7 +91,7 @@
## Changes to libraries ## 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. * 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 * Taint-tracking configurations now interact differently with the `data` flow label, which may affect queries
that combine taint-tracking and flow labels. that combine taint-tracking and flow labels.

View File

@@ -17,6 +17,7 @@ provide:
# - "javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml" # - "javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml"
- "javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml" - "javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml"
- "javascript/ql/experimental/adaptivethreatmodeling/src/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/lib/qlpack.yml"
- "csharp/ql/campaigns/Solorigate/src/qlpack.yml" - "csharp/ql/campaigns/Solorigate/src/qlpack.yml"
- "csharp/ql/campaigns/Solorigate/test/qlpack.yml" - "csharp/ql/campaigns/Solorigate/test/qlpack.yml"

View 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();
})();

View 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');
};

View 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();

View File

@@ -0,0 +1,5 @@
module.exports.Logger = class {
log(message, ...objs) {
console.log(message, objs);
}
};

View 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
}));

View File

@@ -0,0 +1,6 @@
const mongoose = require('mongoose');
module.exports.User = mongoose.model('User', new mongoose.Schema({
name: String,
token: String
}));

View 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)
});
});

View 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();
})();

View 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
});
});

View File

@@ -94,8 +94,8 @@
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll" "csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll"
], ],
"Model as Data Generation Java/C# - CaptureModels": [ "Model as Data Generation Java/C# - CaptureModels": [
"java/ql/src/utils/model-generator/internal/CaptureModels.qll", "java/ql/src/utils/modelgenerator/internal/CaptureModels.qll",
"csharp/ql/src/utils/model-generator/internal/CaptureModels.qll" "csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll"
], ],
"Sign Java/C#": [ "Sign Java/C#": [
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll", "java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
@@ -486,40 +486,6 @@
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll", "python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll",
"ruby/ql/lib/codeql/ruby/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": [ "CFG": [
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll", "csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
"ruby/ql/lib/codeql/ruby/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/extractor-tests/patterns/patterns.swift",
"swift/ql/test/library-tests/ast/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": [ "IncompleteMultiCharacterSanitization JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll", "javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",
"ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll" "ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll"
] ]
} }

View File

@@ -11,11 +11,12 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" /> <PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" /> <PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -17,7 +17,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Build" Version="16.11.0" /> <PackageReference Include="Microsoft.Build" Version="17.3.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

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

View File

@@ -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 ## 0.4.1
No user-facing changes. No user-facing changes.

View File

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

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

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 0.4.1 lastReleaseVersion: 0.4.3

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -136,6 +136,18 @@ module Consistency {
msg = "Local flow step does not preserve enclosing callable." 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(_) } private DataFlowType typeRepr() { result = getNodeType(_) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) { query predicate compatibleTypesReflexive(DataFlowType t, string msg) {

View File

@@ -4,6 +4,12 @@
* variable), and `v` is an integer in the range `[0 .. m-1]`. * 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 ModulusAnalysisSpecific::Private
private import experimental.semmle.code.cpp.semantic.Semantic private import experimental.semmle.code.cpp.semantic.Semantic
private import ConstantAnalysis private import ConstantAnalysis
@@ -162,6 +168,11 @@ private predicate phiModulusInit(SemSsaPhiNode phi, SemBound b, int val, int mod
*/ */
pragma[nomagic] pragma[nomagic]
private predicate phiModulusRankStep(SemSsaPhiNode phi, SemBound b, int val, int mod, int rix) { 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 rix = 0 and
phiModulusInit(phi, b, val, mod) phiModulusInit(phi, b, val, mod)
or or
@@ -169,6 +180,12 @@ private predicate phiModulusRankStep(SemSsaPhiNode phi, SemBound b, int val, int
mod != 1 and mod != 1 and
val = remainder(v1, mod) 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 | exists(int v2, int m2 |
rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and
phiModulusRankStep(phi, b, v1, m1, rix - 1) 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) mod = m1.gcd(m2).gcd(v1 - v2)
) )
or 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 | exists(int m2 |
rankedPhiInput(phi, inp, edge, rix) and rankedPhiInput(phi, inp, edge, rix) and
phiModulusRankStep(phi, b, v1, m1, rix - 1) and phiModulusRankStep(phi, b, v1, m1, rix - 1) and

View File

@@ -1,9 +1,9 @@
name: codeql/cpp-all name: codeql/cpp-all
version: 0.4.2-dev version: 0.4.4-dev
groups: cpp groups: cpp
dbscheme: semmlecode.cpp.dbscheme dbscheme: semmlecode.cpp.dbscheme
extractor: cpp extractor: cpp
library: true library: true
upgrades: upgrades upgrades: upgrades
dependencies: dependencies:
codeql/ssa: 0.0.1 codeql/ssa: ${workspace}

View File

@@ -1125,12 +1125,12 @@ class FormatLiteral extends Literal {
exists(int dot, int afterdot | exists(int dot, int afterdot |
(if this.getPrecision(n) = 0 then dot = 0 else dot = 1) and (if this.getPrecision(n) = 0 then dot = 0 else dot = 1) and
( (
( if this.hasExplicitPrecision(n)
if this.hasExplicitPrecision(n) then afterdot = this.getPrecision(n)
then afterdot = this.getPrecision(n) else (
else not this.hasImplicitPrecision(n) not this.hasImplicitPrecision(n) and
) and afterdot = 6
afterdot = 6 )
) and ) and
len = 1 + 309 + dot + afterdot len = 1 + 309 + dot + afterdot
) and ) and
@@ -1140,12 +1140,12 @@ class FormatLiteral extends Literal {
exists(int dot, int afterdot | exists(int dot, int afterdot |
(if this.getPrecision(n) = 0 then dot = 0 else dot = 1) and (if this.getPrecision(n) = 0 then dot = 0 else dot = 1) and
( (
( if this.hasExplicitPrecision(n)
if this.hasExplicitPrecision(n) then afterdot = this.getPrecision(n)
then afterdot = this.getPrecision(n) else (
else not this.hasImplicitPrecision(n) not this.hasImplicitPrecision(n) and
) and afterdot = 6
afterdot = 6 )
) and ) and
len = 1 + 1 + dot + afterdot + 1 + 1 + 3 len = 1 + 1 + dot + afterdot + 1 + 1 + 3
) and ) and
@@ -1155,12 +1155,12 @@ class FormatLiteral extends Literal {
exists(int dot, int afterdot | exists(int dot, int afterdot |
(if this.getPrecision(n) = 0 then dot = 0 else dot = 1) and (if this.getPrecision(n) = 0 then dot = 0 else dot = 1) and
( (
( if this.hasExplicitPrecision(n)
if this.hasExplicitPrecision(n) then afterdot = this.getPrecision(n)
then afterdot = this.getPrecision(n) else (
else not this.hasImplicitPrecision(n) not this.hasImplicitPrecision(n) and
) and afterdot = 6
afterdot = 6 )
) and ) and
// note: this could be displayed in the style %e or %f; // note: this could be displayed in the style %e or %f;
// however %f is only used when 'P > X >= -4' // however %f is only used when 'P > X >= -4'

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -136,6 +136,18 @@ module Consistency {
msg = "Local flow step does not preserve enclosing callable." 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(_) } private DataFlowType typeRepr() { result = getNodeType(_) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) { query predicate compatibleTypesReflexive(DataFlowType t, string msg) {

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -147,6 +147,12 @@ abstract class Configuration extends string {
*/ */
FlowFeature getAFeature() { none() } 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. * 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 * The corresponding paths are generated from the end-points and the graph
* included in the module `PathGraph`. * 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. * 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`. * Gets the number of `AccessPath`s that correspond to `apa`.
*/ */
pragma[assume_small_delta]
private int countAps(AccessPathApprox apa, Configuration config) { private int countAps(AccessPathApprox apa, Configuration config) {
evalUnfold(apa, false, config) and evalUnfold(apa, false, config) and
result = 1 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. * that it is expanded to a precise head-tail representation.
*/ */
language[monotonicAggregates] language[monotonicAggregates]
pragma[assume_small_delta]
private int countPotentialAps(AccessPathApprox apa, Configuration config) { private int countPotentialAps(AccessPathApprox apa, Configuration config) {
apa instanceof AccessPathApproxNil and result = 1 apa instanceof AccessPathApproxNil and result = 1
or or
@@ -2681,6 +2689,7 @@ private newtype TAccessPath =
} }
private newtype TPathNode = private newtype TPathNode =
pragma[assume_small_delta]
TPathNodeMid( TPathNodeMid(
NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config
) { ) {
@@ -2709,6 +2718,18 @@ private newtype TPathNode =
state = sink.getState() and state = sink.getState() and
config = sink.getConfiguration() 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) } override AccessPathFrontHead getFront() { result = TFrontHead(head) }
pragma[assume_small_delta]
override AccessPathApproxCons getApprox() { override AccessPathApproxCons getApprox() {
result = TConsNil(head, tail.(AccessPathNil).getType()) result = TConsNil(head, tail.(AccessPathNil).getType())
or or
@@ -2786,6 +2808,7 @@ private class AccessPathCons extends AccessPath, TAccessPathCons {
result = TCons1(head, this.length()) result = TCons1(head, this.length())
} }
pragma[assume_small_delta]
override int length() { result = 1 + tail.length() } override int length() { result = 1 + tail.length() }
private string toStringImpl(boolean needsSuffix) { private string toStringImpl(boolean needsSuffix) {
@@ -2874,54 +2897,16 @@ private class AccessPathCons1 extends AccessPath, TAccessPathCons1 {
} }
} }
/** abstract private class PathNodeImpl extends TPathNode {
* 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 }
/** Gets the `FlowState` of this node. */ /** Gets the `FlowState` of this node. */
FlowState getState() { none() } abstract FlowState getState();
/** Gets the associated configuration. */ /** Gets the associated configuration. */
Configuration getConfiguration() { none() } abstract Configuration getConfiguration();
/** Gets a successor of this node, if any. */
final PathNode getASuccessor() {
result = this.(PathNodeImpl).getANonHiddenSuccessor() and
reach(this) and
reach(result)
}
/** Holds if this node is a source. */ /** Holds if this node is a source. */
predicate isSource() { none() } abstract predicate isSource();
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { 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() { private string ppAp() {
this instanceof PathNodeSink and result = "" this instanceof PathNodeSink and result = ""
or or
@@ -2967,13 +2968,23 @@ abstract private class PathNodeImpl extends PathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" 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 string filepath, int startline, int startcolumn, int endline, int endcolumn
) { ) {
this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, 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. */ /** Holds if `n` can reach a sink. */
private predicate directReach(PathNodeImpl n) { 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. */ /** 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. */ /** 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) 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. * 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. */ /** Holds if `n` is a node in the graph of data flow path explanations. */
query predicate nodes(PathNode n, string key, string val) { 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`. * `ret -> out` is summarized as the edge `arg -> out`.
*/ */
query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) {
Subpaths::subpaths(arg, par, ret, out) and Subpaths::subpaths(arg, par, ret, out)
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3118,9 +3178,66 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, 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( 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 * Holds if data may flow from `mid` to `node`. The last step in or out of
* a callable is recorded by `cc`. * a callable is recorded by `cc`.
*/ */
pragma[assume_small_delta]
pragma[nomagic] pragma[nomagic]
private predicate pathStep( private predicate pathStep(
PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap
@@ -3399,7 +3517,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( 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 NodeEx out, FlowState sout, AccessPath apout
) { ) {
subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and
@@ -3407,14 +3525,14 @@ private module Subpaths {
} }
pragma[nomagic] 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. * Holds if `(arg, par, ret, out)` forms a subpath-tuple.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths03( 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 | exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode |
subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and 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 * a subpath between `par` and `ret` with the connecting edges `arg -> par` and
* `ret -> out` is summarized as the edge `arg -> out`. * `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 | exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 |
pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and 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 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. * Holds if `n` can reach a return node in a summarized subpath that can reach a sink.
*/ */
predicate retReach(PathNodeImpl n) { 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 or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3476,12 +3594,22 @@ private module Subpaths {
* Will only have results if `configuration` has non-empty sources and * Will only have results if `configuration` has non-empty sources and
* sinks. * 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( 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.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.(PathNodeImpl).getNodeEx().asNode() = source and flowsource.getNodeEx().asNode() = source and
(flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and
flowsink.getNodeEx().asNode() = sink flowsink.getNodeEx().asNode() = sink
} }
@@ -3504,14 +3632,14 @@ private predicate finalStats(
fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and
conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and
states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and
tuples = count(PathNode pn) tuples = count(PathNodeImpl pn)
or or
fwd = false and fwd = false and
nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) 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 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 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 states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and
tuples = count(PathNode pn | reach(pn)) tuples = count(PathNode pn)
} }
/** /**

View File

@@ -136,6 +136,18 @@ module Consistency {
msg = "Local flow step does not preserve enclosing callable." 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(_) } private DataFlowType typeRepr() { result = getNodeType(_) }
query predicate compatibleTypesReflexive(DataFlowType t, string msg) { query predicate compatibleTypesReflexive(DataFlowType t, string msg) {

View File

@@ -98,16 +98,16 @@ class ParameterPosition = Position;
/** An argument position represented by an integer. */ /** An argument position represented by an integer. */
class ArgumentPosition = Position; class ArgumentPosition = Position;
class Position extends TPosition { abstract class Position extends TPosition {
abstract string toString(); abstract string toString();
} }
class DirectPosition extends TDirectPosition { class DirectPosition extends Position, TDirectPosition {
int index; int index;
DirectPosition() { this = TDirectPosition(index) } DirectPosition() { this = TDirectPosition(index) }
string toString() { override string toString() {
index = -1 and index = -1 and
result = "this" result = "this"
or or
@@ -118,12 +118,12 @@ class DirectPosition extends TDirectPosition {
int getIndex() { result = index } int getIndex() { result = index }
} }
class IndirectionPosition extends TIndirectionPosition { class IndirectionPosition extends Position, TIndirectionPosition {
int index; int index;
IndirectionPosition() { this = TIndirectionPosition(index) } IndirectionPosition() { this = TIndirectionPosition(index) }
string toString() { override string toString() {
index = -1 and index = -1 and
result = "this" result = "this"
or or

View File

@@ -16,21 +16,23 @@ private class StdBasicString extends ClassTemplateInstantiation {
} }
/** /**
* Additional model for `std::string` constructors that reference the character * The `std::basic_string::iterator` declaration.
* type of the container, or an iterator. For example construction from
* iterators:
* ```
* std::string b(a.begin(), a.end());
* ```
*/ */
private class StdStringConstructor extends Constructor, TaintFunction { private class StdBasicStringIterator extends Iterator, Type {
StdStringConstructor() { this.getDeclaringType() instanceof StdBasicString } 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 * Gets the index of a parameter to this function that is a string (or
* character). * character).
*/ */
int getAStringParameterIndex() { final int getAStringParameterIndex() {
exists(Type paramType | paramType = this.getParameter(result).getUnspecifiedType() | exists(Type paramType | paramType = this.getParameter(result).getUnspecifiedType() |
// e.g. `std::basic_string::CharT *` // e.g. `std::basic_string::CharT *`
paramType instanceof PointerType paramType instanceof PointerType
@@ -41,15 +43,28 @@ private class StdStringConstructor extends Constructor, TaintFunction {
this.getDeclaringType().getTemplateArgument(2).(Type).getUnspecifiedType() this.getDeclaringType().getTemplateArgument(2).(Type).getUnspecifiedType()
or or
// i.e. `std::basic_string::CharT` // i.e. `std::basic_string::CharT`
this.getParameter(result).getUnspecifiedType() = paramType = this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType()
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType()
) )
} }
/** /**
* Gets the index of a parameter to this function that is an iterator. * 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) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// taint flow from any parameter of the value type to the returned object // 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`. * The `std::string` function `c_str`.
*/ */
private class StdStringCStr extends TaintFunction { private class StdStringCStr extends StdStringTaintFunction {
StdStringCStr() { this.getClassAndName("c_str") instanceof StdBasicString } StdStringCStr() { this.getClassAndName("c_str") instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -81,7 +96,7 @@ private class StdStringCStr extends TaintFunction {
/** /**
* The `std::string` function `data`. * The `std::string` function `data`.
*/ */
private class StdStringData extends TaintFunction { private class StdStringData extends StdStringTaintFunction {
StdStringData() { this.getClassAndName("data") instanceof StdBasicString } StdStringData() { this.getClassAndName("data") instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -99,7 +114,7 @@ private class StdStringData extends TaintFunction {
/** /**
* The `std::string` function `push_back`. * The `std::string` function `push_back`.
*/ */
private class StdStringPush extends TaintFunction { private class StdStringPush extends StdStringTaintFunction {
StdStringPush() { this.getClassAndName("push_back") instanceof StdBasicString } StdStringPush() { this.getClassAndName("push_back") instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -112,7 +127,7 @@ private class StdStringPush extends TaintFunction {
/** /**
* The `std::string` functions `front` and `back`. * The `std::string` functions `front` and `back`.
*/ */
private class StdStringFrontBack extends TaintFunction { private class StdStringFrontBack extends StdStringTaintFunction {
StdStringFrontBack() { this.getClassAndName(["front", "back"]) instanceof StdBasicString } StdStringFrontBack() { this.getClassAndName(["front", "back"]) instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -125,7 +140,7 @@ private class StdStringFrontBack extends TaintFunction {
/** /**
* The (non-member) `std::string` function `operator+`. * The (non-member) `std::string` function `operator+`.
*/ */
private class StdStringPlus extends TaintFunction { private class StdStringPlus extends StdStringTaintFunction {
StdStringPlus() { StdStringPlus() {
this.hasQualifiedName(["std", "bsl"], "operator+") and this.hasQualifiedName(["std", "bsl"], "operator+") and
this.getUnspecifiedType() instanceof StdBasicString this.getUnspecifiedType() instanceof StdBasicString
@@ -142,31 +157,15 @@ private class StdStringPlus extends TaintFunction {
} }
/** /**
* The `std::string` functions `operator+=`, `append`, `insert` and * The `std::string` functions `operator+=`, `append` and `replace`.
* `replace`. All of these functions combine the existing string * All of these functions combine the existing string with a new
* with a new string (or character) from one of the arguments. * string (or character) from one of the arguments.
*/ */
private class StdStringAppend extends TaintFunction { private class StdStringAppend extends StdStringTaintFunction {
StdStringAppend() { 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) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from string and parameter to string (qualifier) and return value // 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`. * The standard function `std::string.assign`.
*/ */
private class StdStringAssign extends TaintFunction { private class StdStringAssign extends StdStringTaintFunction {
StdStringAssign() { this.getClassAndName("assign") instanceof StdBasicString } 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) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
// flow from parameter to string itself (qualifier) and return value // 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`. * The standard function `std::string.copy`.
*/ */
private class StdStringCopy extends TaintFunction { private class StdStringCopy extends StdStringTaintFunction {
StdStringCopy() { this.getClassAndName("copy") instanceof StdBasicString } StdStringCopy() { this.getClassAndName("copy") instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -242,7 +257,7 @@ private class StdStringCopy extends TaintFunction {
/** /**
* The standard function `std::string.substr`. * The standard function `std::string.substr`.
*/ */
private class StdStringSubstr extends TaintFunction { private class StdStringSubstr extends StdStringTaintFunction {
StdStringSubstr() { this.getClassAndName("substr") instanceof StdBasicString } StdStringSubstr() { this.getClassAndName("substr") instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
@@ -255,7 +270,7 @@ private class StdStringSubstr extends TaintFunction {
/** /**
* The `std::string` functions `at` and `operator[]`. * The `std::string` functions `at` and `operator[]`.
*/ */
private class StdStringAt extends TaintFunction { private class StdStringAt extends StdStringTaintFunction {
StdStringAt() { this.getClassAndName(["at", "operator[]"]) instanceof StdBasicString } StdStringAt() { this.getClassAndName(["at", "operator[]"]) instanceof StdBasicString }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {

View File

@@ -50,19 +50,18 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid
} }
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
this.getName() = ["strncat", "wcsncat", "_mbsncat", "_mbsncat_l"] and (
input.isParameter(2) and this.getName() = ["strncat", "wcsncat", "_mbsncat", "_mbsncat_l"] and
output.isParameterDeref(0) input.isParameter(2)
or or
this.getName() = ["_mbsncat_l", "_mbsnbcat_l"] and this.getName() = ["_mbsncat_l", "_mbsnbcat_l"] and
input.isParameter(3) and input.isParameter(3)
output.isParameterDeref(0) or
or input.isParameterDeref(0)
input.isParameterDeref(0) and or
output.isParameterDeref(0) input.isParameterDeref(1)
or ) and
input.isParameterDeref(1) and (output.isParameterDeref(0) or output.isReturnValueDeref())
output.isParameterDeref(0)
} }
override predicate hasArrayInput(int param) { override predicate hasArrayInput(int param) {

View File

@@ -16,15 +16,36 @@
import cpp import cpp
import semmle.code.cpp.commons.Exclusions 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) { Expr normalizeExpr(Expr e) {
result = e.getLocation().getStartColumn() = min(getCandidateColumn(e)) and
min(Expr child | result = e
child.getParentWithConversions*() = e.getFullyConverted() and or
not child.getParentWithConversions*() = any(Call c).getAnArgument() not e.getLocation().getStartColumn() = min(getCandidateColumn(e)) and
| result = normalizeExpr(getAChildWithConversions(e)) and
child order by child.getLocation().getStartColumn(), count(child.getParentWithConversions*()) result.getLocation().getStartColumn() = min(getCandidateColumn(e))
)
} }
predicate isParenthesized(CommaExpr ce) { predicate isParenthesized(CommaExpr ce) {
@@ -43,8 +64,8 @@ from CommaExpr ce, Expr left, Expr right, Location leftLoc, Location rightLoc
where where
ce.fromSource() and ce.fromSource() and
not isFromMacroDefinition(ce) and not isFromMacroDefinition(ce) and
left = normalizeExpr(ce.getLeftOperand()) and left = normalizeExpr(ce.getLeftOperand().getFullyConverted()) and
right = normalizeExpr(ce.getRightOperand()) and right = normalizeExpr(ce.getRightOperand().getFullyConverted()) and
leftLoc = left.getLocation() and leftLoc = left.getLocation() and
rightLoc = right.getLocation() and rightLoc = right.getLocation() and
not isParenthesized(ce) and not isParenthesized(ce) and

View File

@@ -13,16 +13,32 @@
import cpp import cpp
pragma[noinline]
predicate possiblyIncompleteFile(File f) {
exists(Diagnostic d | d.getFile() = f and d.getSeverity() >= 3)
}
predicate immediatelyReachableFunction(Function f) { predicate immediatelyReachableFunction(Function f) {
not f.isStatic() or not f.isStatic()
exists(BlockExpr be | be.getFunction() = f) or or
f instanceof MemberFunction or exists(BlockExpr be | be.getFunction() = f)
f instanceof TemplateFunction or or
f.getFile() instanceof HeaderFile or f instanceof MemberFunction
f.getAnAttribute().hasName("constructor") or or
f.getAnAttribute().hasName("destructor") or f instanceof TemplateFunction
f.getAnAttribute().hasName("used") or 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") 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) { predicate immediatelyReachableVariable(Variable v) {

View File

@@ -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 ## 0.4.1
### Minor Analysis Improvements ### Minor Analysis Improvements

View File

@@ -13,11 +13,18 @@
import cpp import cpp
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given, string ffcName
where where
ffc = fl.getUse() and ffc = fl.getUse() and
expected = fl.getNumArgNeeded() and expected = fl.getNumArgNeeded() and
given = ffc.getNumFormatArgument() and given = ffc.getNumFormatArgument() and
expected < given and expected < given and
fl.specsAreKnown() fl.specsAreKnown() and
select ffc, "Format expects " + expected.toString() + " arguments but given " + given.toString() (
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()

View File

@@ -16,11 +16,18 @@
import cpp import cpp
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given, string ffcName
where where
ffc = fl.getUse() and ffc = fl.getUse() and
expected = fl.getNumArgNeeded() and expected = fl.getNumArgNeeded() and
given = ffc.getNumFormatArgument() and given = ffc.getNumFormatArgument() and
expected > given and expected > given and
fl.specsAreKnown() fl.specsAreKnown() and
select ffc, "Format expects " + expected.toString() + " arguments but given " + given.toString() (
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()

View File

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

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The "Unterminated variadic call" (`cpp/unterminated-variadic-call`) query has been tuned to produce fewer false positive results.

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

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

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 0.4.1 lastReleaseVersion: 0.4.3

View File

@@ -38,9 +38,9 @@ predicate hasNontrivialDestructor(Class c) {
from Class c from Class c
where where
(hasPointerMember(c) or hasNontrivialDestructor(c)) and (hasPointerMember(c) or hasNontrivialDestructor(c)) and
not ( (
c.getAMemberFunction() instanceof CopyConstructor and c.hasImplicitCopyAssignmentOperator() or
c.getAMemberFunction() instanceof CopyAssignmentOperator c.hasImplicitCopyConstructor()
) and ) and
not c instanceof Struct not c instanceof Struct
select c, select c,

View File

@@ -1,11 +1,11 @@
name: codeql/cpp-queries name: codeql/cpp-queries
version: 0.4.2-dev version: 0.4.4-dev
groups: groups:
- cpp - cpp
- queries - queries
dependencies: dependencies:
codeql/cpp-all: "*" codeql/cpp-all: ${workspace}
codeql/suite-helpers: "*" codeql/suite-helpers: ${workspace}
suites: codeql-suites suites: codeql-suites
extractor: cpp extractor: cpp
defaultSuiteFile: codeql-suites/cpp-code-scanning.qls defaultSuiteFile: codeql-suites/cpp-code-scanning.qls

View File

@@ -137,6 +137,7 @@ abstract class InlineExpectationsTest extends string {
final predicate hasFailureMessage(FailureLocatable element, string message) { final predicate hasFailureMessage(FailureLocatable element, string message) {
exists(ActualResult actualResult | exists(ActualResult actualResult |
actualResult.getTest() = this and actualResult.getTest() = this and
actualResult.getTag() = this.getARelevantTag() and
element = actualResult and element = actualResult and
( (
exists(FalseNegativeExpectation falseNegative | exists(FalseNegativeExpectation falseNegative |
@@ -150,9 +151,18 @@ abstract class InlineExpectationsTest extends string {
) )
) )
or 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 | exists(ValidExpectation expectation |
not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and
expectation.getTag() = getARelevantTag() and expectation.getTag() = this.getARelevantTag() and
element = expectation and element = expectation and
( (
expectation instanceof GoodExpectation and expectation instanceof GoodExpectation and

View File

@@ -2,43 +2,21 @@ edges
| test.cpp:4:15:4:20 | call to malloc | test.cpp:5:15:5:15 | Load | | 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 | ... + ... |
| 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: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: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: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: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: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: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: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: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: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 |
| 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:11:16:11:16 | Load |
| test.cpp:8:16:8:16 | Load | test.cpp:12:16:12: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: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: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: * ... | | 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:8:14:8:21 | Load: * ... |
| test.cpp:11:16:11:16 | Load | test.cpp:12:16:12:16 | 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: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: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: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: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: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: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: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: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 | ... + ... |
| 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: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: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: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: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: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: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: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: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: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 |
| 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:35:16:35:16 | Load |
| test.cpp:32:16:32:16 | Load | test.cpp:36:16:36: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: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: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: * ... | | 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:32:14:32:21 | Load: * ... |
| test.cpp:35:16:35:16 | Load | test.cpp:36:16:36:16 | 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: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: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: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 | ... + ... |
| 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: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: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: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: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: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: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: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: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: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 |
| 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:47:16:47:16 | Load |
| test.cpp:44:16:44:16 | Load | test.cpp:48:16:48: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: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: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: * ... | | 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:44:14:44:21 | Load: * ... |
| test.cpp:47:16:47:16 | Load | test.cpp:48:16:48:16 | 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: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: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:62:39:62:39 | Load |
| test.cpp:51:7:51:14 | VariableAddress indirection | test.cpp:66:39:66: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: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: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: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:16 | Load | test.cpp:53:12:53:23 | ... + ... |
| test.cpp:53:12:53:23 | ... + ... | test.cpp:51:33:51:35 | Load indirection | | 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: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:66:32:66:34 | Load |
| test.cpp:60:34:60:37 | mk_array output argument | test.cpp:70:31:70:33 | 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: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: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: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: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] | | 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: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: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: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 | Load | test.cpp:83:15:83:30 | ... + ... |
| test.cpp:83:19:83:23 | begin | test.cpp:83:19:83:23 | Load | | 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 | | 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: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: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 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
| test.cpp:91:40:91:42 | end | test.cpp:91:40:91:42 | Load | | 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:24:95:28 | begin |
| test.cpp:95:20:95:22 | arr indirection [begin] | test.cpp:95:47:95:47 | Load | | 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: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: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 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
| test.cpp:95:40:95:42 | end | test.cpp:95:40:95:42 | Load | | 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:24:99:28 | begin |
| test.cpp:99:20:99:22 | arr indirection [begin] | test.cpp:99:46:99:46 | Load | | 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: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: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 | Load | test.cpp:96:9:96:14 | Store: ... = ... |
| test.cpp:99:39:99:41 | end | test.cpp:99:39:99:41 | Load | | 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:105:20:105:22 | arr indirection [begin] |
| test.cpp:104:27:104:29 | arr [begin] | test.cpp:109:20:109: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: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: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 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
| test.cpp:105:40:105:42 | end | test.cpp:105:40:105:42 | Load | | 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:24:109:28 | begin |
| test.cpp:109:20:109:22 | arr indirection [begin] | test.cpp:109:47:109:47 | Load | | 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: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: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 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
| test.cpp:109:40:109:42 | end | test.cpp:109:40:109:42 | Load | | 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:24:113:28 | begin |
| test.cpp:113:20:113:22 | arr indirection [begin] | test.cpp:113:46:113:46 | Load | | 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: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: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 | Load | test.cpp:110:9:110:14 | Store: ... = ... |
| test.cpp:113:39:113:41 | end | test.cpp:113:39:113:41 | Load | | 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 [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] | | 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: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: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: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 | Load | test.cpp:144:16:144:32 | ... + ... |
| test.cpp:144:21:144:25 | begin | test.cpp:144:21:144:25 | Load | | 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] | | 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: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: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 | Load | test.cpp:157:9:157:14 | Store: ... = ... |
| test.cpp:156:42:156:44 | end | test.cpp:156:42:156:44 | Load | | 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:25:160:29 | begin |
| test.cpp:160:20:160:22 | Load indirection [begin] | test.cpp:160:48:160:48 | Load | | 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: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: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 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
| test.cpp:166:42:166:44 | end | test.cpp:166:42:166:44 | Load | | 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:25:170:29 | begin |
| test.cpp:170:20:170:22 | Load indirection [begin] | test.cpp:170:49:170:49 | Load | | 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: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: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 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
| test.cpp:170:42:170:44 | end | test.cpp:170:42:170:44 | Load | | 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:25:174:29 | begin |
| test.cpp:174:20:174:22 | Load indirection [begin] | test.cpp:174:48:174:48 | Load | | 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: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: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 | Load | test.cpp:171:9:171:14 | Store: ... = ... |
| test.cpp:174:41:174:43 | end | test.cpp:174:41:174:43 | Load | | 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 [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] | | 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: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 | ... + ... |
| 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: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: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: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 |
| 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: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: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: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:206:17:206:17 | Load |
| test.cpp:205:23:205:28 | call to malloc | test.cpp:208:15:208:15 | 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 | ... + ... |
| 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: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: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: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: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 | | 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: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: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: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:6 | * ... |
| test.cpp:213:6:213:6 | Load | test.cpp:213:5:213:13 | Store: ... = ... | | test.cpp:213:6:213:6 | Load | test.cpp:213:5:213:13 | Store: ... = ... |

View File

@@ -6,6 +6,8 @@ uniqueNodeToString
missingToString missingToString
parameterCallable parameterCallable
localFlowIsLocal localFlowIsLocal
readStepIsLocal
storeStepIsLocal
compatibleTypesReflexive compatibleTypesReflexive
unreachableNodeCCtx unreachableNodeCCtx
localCallNodes 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: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: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: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 viableImplInCallContextTooLarge

View File

@@ -21,6 +21,8 @@ uniqueNodeToString
missingToString missingToString
parameterCallable parameterCallable
localFlowIsLocal localFlowIsLocal
readStepIsLocal
storeStepIsLocal
compatibleTypesReflexive compatibleTypesReflexive
unreachableNodeCCtx unreachableNodeCCtx
localCallNodes 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: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: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: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: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: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. | | true_upon_entry.cpp:10:27:10:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |

View File

@@ -87,7 +87,7 @@ Top *identity(Top *top) {
void callIdentityFunctions(Top *top, Bottom *bottom) { void callIdentityFunctions(Top *top, Bottom *bottom) {
identity(bottom)->isSink(source()); // $ MISSING: ast,ir identity(bottom)->isSink(source()); // $ MISSING: ast,ir
identity(top)->isSink(source()); // now flow identity(top)->isSink(source()); // no flow
} }
using SinkFunctionType = void (*)(int); using SinkFunctionType = void (*)(int);

View File

@@ -329,21 +329,21 @@ namespace NestedTests {
namespace FlowThroughGlobals { namespace FlowThroughGlobals {
int globalVar; int globalVar;
int taintGlobal() { void taintGlobal() {
globalVar = source(); globalVar = source();
} }
int f() { void f() {
sink(globalVar); // $ ir=333:17 ir=347:17 // tainted or clean? Not sure. sink(globalVar); // $ ir=333:17 ir=347:17 // tainted or clean? Not sure.
taintGlobal(); taintGlobal();
sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast
} }
int calledAfterTaint() { void calledAfterTaint() {
sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast
} }
int taintAndCall() { void taintAndCall() {
globalVar = source(); globalVar = source();
calledAfterTaint(); calledAfterTaint();
sink(globalVar); // $ ast ir=333:17 ir=347:17 sink(globalVar); // $ ast ir=333:17 ir=347:17
@@ -494,3 +494,14 @@ void regression_with_phi_flow(int clean1) {
x = source(); 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
}

View File

@@ -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) { override predicate isBarrier(DataFlow::Node barrier) {
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") or barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") or
barrier = DataFlow::InstructionBarrierGuard<testBarrierGuard/3>::getABarrierNode() 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
}
} }

View File

@@ -5,7 +5,7 @@ int source();
void sink(...); void sink(...);
bool random(); bool random();
int test1() { void test1() {
int x = source(); int x = source();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
x = 0; x = 0;
@@ -13,7 +13,7 @@ int test1() {
sink(x); // $ SPURIOUS: ir sink(x); // $ SPURIOUS: ir
} }
int test2(int iterations) { void test2(int iterations) {
int x = source(); int x = source();
for (int i = 0; i < iterations; i++) { for (int i = 0; i < iterations; i++) {
x = 0; x = 0;
@@ -21,7 +21,7 @@ int test2(int iterations) {
sink(x); // $ ast,ir sink(x); // $ ast,ir
} }
int test3() { void test3() {
int x = 0; int x = 0;
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
x = source(); x = source();
@@ -29,7 +29,7 @@ int test3() {
sink(x); // $ ast,ir sink(x); // $ ast,ir
} }
int test4() { void test4() {
int x = source(); int x = source();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
if (random()) if (random())
@@ -39,7 +39,7 @@ int test4() {
sink(x); // $ ast,ir sink(x); // $ ast,ir
} }
int test5() { void test5() {
int x = source(); int x = source();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
if (random()) if (random())
@@ -49,7 +49,7 @@ int test5() {
sink(x); // $ ast,ir sink(x); // $ ast,ir
} }
int test6() { void test6() {
int y; int y;
int x = source(); int x = source();
for (int i = 0; i < 10 && (y = 1); i++) { for (int i = 0; i < 10 && (y = 1); i++) {
@@ -57,7 +57,7 @@ int test6() {
sink(x); // $ ast,ir sink(x); // $ ast,ir
} }
int test7() { void test7() {
int y; int y;
int x = source(); int x = source();
for (int i = 0; i < 10 && (y = 1); i++) { for (int i = 0; i < 10 && (y = 1); i++) {
@@ -66,7 +66,7 @@ int test7() {
sink(x); // $ SPURIOUS: ir sink(x); // $ SPURIOUS: ir
} }
int test8() { void test8() {
int x = source(); int x = source();
// It appears to the analysis that the condition can exit after `i < 10` // 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 // without having assigned to `x`. That is an effect of how the
@@ -78,7 +78,7 @@ int test8() {
sink(x); // $ SPURIOUS: ast,ir sink(x); // $ SPURIOUS: ast,ir
} }
int test9() { void test9() {
int y; int y;
int x = source(); int x = source();
for (int i = 0; (y = 1) && i < 10; i++) { for (int i = 0; (y = 1) && i < 10; i++) {
@@ -86,21 +86,21 @@ int test9() {
sink(x); // $ ast,ir sink(x); // $ ast,ir
} }
int test10() { void test10() {
int x = source(); int x = source();
for (int i = 0; (x = 1) && i < 10; i++) { for (int i = 0; (x = 1) && i < 10; i++) {
} }
sink(x); // no flow sink(x); // no flow
} }
int test10(int b, int d) { void test10(int b, int d) {
int i = 0; int i = 0;
int x = source(); int x = source();
if (b) if (b)
goto L; goto L;
for (; i < 10; i += d) { for (; i < 10; i += d) {
x = 0; x = 0;
L: L: ;
} }
sink(x); // $ ir MISSING: ast sink(x); // $ ir MISSING: ast
} }

View File

@@ -12,6 +12,8 @@ uniqueNodeToString
missingToString missingToString
parameterCallable parameterCallable
localFlowIsLocal localFlowIsLocal
readStepIsLocal
storeStepIsLocal
compatibleTypesReflexive compatibleTypesReflexive
unreachableNodeCCtx unreachableNodeCCtx
localCallNodes localCallNodes

View File

@@ -15,6 +15,8 @@ uniqueNodeToString
missingToString missingToString
parameterCallable parameterCallable
localFlowIsLocal localFlowIsLocal
readStepIsLocal
storeStepIsLocal
compatibleTypesReflexive compatibleTypesReflexive
unreachableNodeCCtx unreachableNodeCCtx
localCallNodes localCallNodes

View File

@@ -9,7 +9,7 @@ struct sockaddr {
char* sa_data; char* sa_data;
}; };
int accept(int, const sockaddr*, int*); int accept(int, sockaddr*, int*);
void sink(sockaddr); void sink(sockaddr);
@@ -20,5 +20,5 @@ void test_accept() {
int a = accept(s, &addr, &size); int a = accept(s, &addr, &size);
sink(a); // $ ast=17:11 ir SPURIOUS: ast=18:12 sink(a); // $ ast=17:11 ir SPURIOUS: ast=18:12
sink(addr); // $ ast,ir sink(addr); // $ ast=17:11 ir SPURIOUS: ast=18:12
} }

View File

@@ -142,9 +142,14 @@
| bsd.cpp:19:14:19:29 | sizeof(sockaddr) | bsd.cpp:20:29:20:32 | size | | | 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: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: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: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: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 | & ... | |
| 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: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 | & ... | | | 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] | | | 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: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 | 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: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: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:180:19:180:19 | p | |
| taint.cpp:180:19:180:19 | p | taint.cpp:181:9:181:9 | 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 | 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:560:24:560:28 | dest1 | |
| taint.cpp:561:9:561:13 | ref arg dest1 | taint.cpp:562:7:562:11 | 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: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: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: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 | 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:560:37:560:41 | dest2 | |
| taint.cpp:564:9:564:13 | ref arg dest2 | taint.cpp:565:7:565:11 | 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: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: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 | | | 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: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:575:7:575:11 | dest1 | |
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:576:8:576:12 | 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: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: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 | 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: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:572:37:572:41 | dest1 | |
| taint.cpp:575:7:575:11 | ref arg dest1 | taint.cpp:576:8:576:12 | 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: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:581:7:581:11 | dest3 | |
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:582:8:582:12 | 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: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: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 | 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: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 | | | taint.cpp:581:7:581:11 | ref arg dest3 | taint.cpp:572:85:572:89 | dest3 | |

View File

@@ -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); unsigned char* dest2 = _mbsncat_l(dest1, ptr, n, source);
sink(dest1); // $ SPURIOUS: ast,ir sink(dest1); // $ SPURIOUS: ast,ir
sink(*dest1); // $ ast,ir sink(*dest1); // $ ast,ir
sink(dest2); // $ SPURIOUS: ir sink(dest2); // $ SPURIOUS: ast,ir
sink(*dest2); // $ ir sink(*dest2); // $ ast,ir
unsigned char* dest4 = _mbsncat_l(dest3, ptr, n, clean); unsigned char* dest4 = _mbsncat_l(dest3, ptr, n, clean);
sink(dest3); sink(dest3);

View File

@@ -95,16 +95,7 @@ module IRTest {
override predicate isSink(DataFlow::Node sink) { override predicate isSink(DataFlow::Node sink) {
exists(FunctionCall call | exists(FunctionCall call |
call.getTarget().getName() = "sink" and call.getTarget().getName() = "sink" and
sink.asConvertedExpr() = call.getAnArgument() sink.asExpr() = 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")
) )
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
import cpp
from FormatAttribute fa
select fa, fa.getArchetype(), concat(fa.getFormatIndex().toString(), ", "),
concat(fa.getFirstFormatArgIndex().toString(), ", ")

View File

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

View File

@@ -0,0 +1,4 @@
import semmle.code.cpp.commons.Printf
from FormatLiteral fl
select fl, concat(fl.getMaxConvertedLength().toString(), ", ")

View File

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

View File

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

View 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);
}
}

View File

@@ -52,6 +52,8 @@ uniqueNodeToString
missingToString missingToString
parameterCallable parameterCallable
localFlowIsLocal localFlowIsLocal
readStepIsLocal
storeStepIsLocal
compatibleTypesReflexive compatibleTypesReflexive
unreachableNodeCCtx unreachableNodeCCtx
localCallNodes localCallNodes

View File

@@ -1463,6 +1463,8 @@ uniqueNodeToString
missingToString missingToString
parameterCallable parameterCallable
localFlowIsLocal localFlowIsLocal
readStepIsLocal
storeStepIsLocal
compatibleTypesReflexive compatibleTypesReflexive
unreachableNodeCCtx unreachableNodeCCtx
localCallNodes localCallNodes

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