Compare commits

..

1 Commits

Author SHA1 Message Date
github-actions[bot]
76dec1f262 use ATM model from training run classification_test 2022-10-11 14:02:13 +00:00
4570 changed files with 165055 additions and 207700 deletions

View File

@@ -0,0 +1,24 @@
---
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

@@ -1,36 +0,0 @@
---
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,61 +0,0 @@
name: Cache query compilation
description: Caches CodeQL compilation caches - should be run both on PRs and pushes to main.
inputs:
key:
description: 'The cache key to use - should be unique to the workflow'
required: true
outputs:
cache-dir:
description: "The directory where the cache was stored"
value: ${{ steps.fill-compilation-dir.outputs.compdir }}
runs:
using: composite
steps:
# Cache the query compilation caches.
# calculate the merge-base with main, in a way that works both on PRs and pushes to main.
- name: Calculate merge-base
shell: bash
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: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6
with:
path: '**/.cache'
read-only: true
key: codeql-compile-${{ inputs.key }}-pr-${{ github.sha }} # deliberately not using the `compile-compile-main` keys here.
restore-keys: |
codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-${{ env.merge_base }}
codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-
codeql-compile-${{ inputs.key }}-main-
- name: Fill CodeQL query compilation cache - main
if: ${{ github.event_name != 'pull_request' }}
uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6
with:
path: '**/.cache'
key: codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-${{ github.sha }} # just fill on main
restore-keys: | # restore from another random commit, to speed up compilation.
codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-
codeql-compile-${{ inputs.key }}-main-
- name: Fill compilation cache directory
id: fill-compilation-dir
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 -f **/.cache/{lock,size} # -f to avoid errors if the cache is empty.
# copy the contents of the .cache folders into the combined cache folder.
cp -r **/.cache/* ${COMBINED_CACHE_DIR}/ || : # ignore missing files
# clean up the .cache folders
rm -rf **/.cache/*
echo "compdir=${COMBINED_CACHE_DIR}" >> $GITHUB_OUTPUT
env:
COMBINED_CACHE_DIR: ${{ github.workspace }}/compilation-dir

View File

@@ -1,22 +1,14 @@
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
env:
GITHUB_TOKEN: ${{ github.token }}
CHANNEL: ${{ inputs.channel }}
run: | run: |
gh extension install github/gh-codeql gh extension install github/gh-codeql
gh codeql set-channel "$CHANNEL" gh codeql set-channel nightly
gh codeql version gh codeql version
gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}" gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}"
env:
GITHUB_TOKEN: ${{ github.token }}

11
.github/labeler.yml vendored
View File

@@ -43,14 +43,3 @@ 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

@@ -1,93 +0,0 @@
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

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

View File

@@ -1,39 +0,0 @@
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
- name: Setup CodeQL
uses: ./.github/actions/fetch-codeql
with:
channel: 'release'
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: all-queries
- 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,examples} --keep-going --warnings=error --check-only --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
- name: compile queries - full
# do full compile if running on main - this populates the cache
if : ${{ github.event_name != 'pull_request' }}
shell: bash
run: codeql query compile -j0 */ql/{src,examples} --keep-going --warnings=error --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
COMBINED_CACHE_DIR: ${{ github.workspace }}/compilation-dir

View File

@@ -1,86 +0,0 @@
name: "C#: Run QL Tests"
on:
push:
paths:
- "csharp/**"
- "shared/**"
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
- "rc/*"
pull_request:
paths:
- "csharp/**"
- "shared/**"
- .github/workflows/csharp-qltest.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
- "rc/*"
defaults:
run:
working-directory: csharp
jobs:
qlupgrade:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- name: Check DB upgrade scripts
run: |
echo >empty.trap
codeql dataset import -S ql/lib/upgrades/initial/semmlecode.csharp.dbscheme testdb empty.trap
codeql dataset upgrade testdb --additional-packs ql/lib
diff -q testdb/semmlecode.csharp.dbscheme ql/lib/semmlecode.csharp.dbscheme
- name: Check DB downgrade scripts
run: |
echo >empty.trap
rm -rf testdb; codeql dataset import -S ql/lib/semmlecode.csharp.dbscheme testdb empty.trap
codeql resolve upgrades --format=lines --allow-downgrades --additional-packs downgrades \
--dbscheme=ql/lib/semmlecode.csharp.dbscheme --target-dbscheme=downgrades/initial/semmlecode.csharp.dbscheme |
xargs codeql execute upgrades testdb
diff -q testdb/semmlecode.csharp.dbscheme downgrades/initial/semmlecode.csharp.dbscheme
qltest:
runs-on: ubuntu-latest-xl
strategy:
fail-fast: false
matrix:
slice: ["1/2", "2/2"]
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- uses: ./csharp/actions/create-extractor-pack
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: csharp-qltest-${{ matrix.slice }}
- name: Run QL tests
run: |
CODEQL_PATH=$(gh codeql version --format=json | jq -r .unpackedLocation)
# The legacy ASP extractor is not in this repo, so take the one from the nightly build
mv "$CODEQL_PATH/csharp/tools/extractor-asp.jar" "${{ github.workspace }}/csharp/extractor-pack/tools"
# Safe guard against using the bundled extractor
rm -rf "$CODEQL_PATH/csharp"
codeql test run --threads=0 --ram 52000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}/csharp/extractor-pack" --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
env:
GITHUB_TOKEN: ${{ github.token }}
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup dotnet
uses: actions/setup-dotnet@v3
with:
dotnet-version: 6.0.202
- name: Extractor unit tests
run: |
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/extractor/Semmle.Util.Tests"
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/extractor/Semmle.Extraction.Tests"
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests"
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"

View File

@@ -43,7 +43,7 @@ jobs:
env QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown env QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown
- name: Upload qhelp markdown - name: Upload qhelp markdown
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v2
with: with:
name: qhelp-markdown name: qhelp-markdown
path: go/qhelp-out/**/*.md path: go/qhelp-out/**/*.md

View File

@@ -23,6 +23,19 @@ 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

@@ -27,7 +27,7 @@ on:
- main - main
- "rc/*" - "rc/*"
paths: paths:
- "**/*.qhelp" - "ruby/**/*.qhelp"
jobs: jobs:
qhelp: qhelp:
@@ -52,7 +52,7 @@ jobs:
id: changes id: changes
run: | run: |
(git diff -z --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep -z '.qhelp$' | grep -z -v '.inc.qhelp'; (git diff -z --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep -z '.qhelp$' | grep -z -v '.inc.qhelp';
git diff -z --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep -z '.inc.qhelp$' | xargs --null -rn1 basename -z | xargs --null -rn1 git grep -z -l) | git diff -z --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep -z '.inc.qhelp$' | xargs --null -rn1 basename | xargs --null -rn1 git grep -z -l) |
grep -z '.qhelp$' | grep -z -v '^-' | sort -z -u > "${RUNNER_TEMP}/paths.txt" grep -z '.qhelp$' | grep -z -v '^-' | sort -z -u > "${RUNNER_TEMP}/paths.txt"
- name: QHelp preview - name: QHelp preview

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@77a8d2d10c0b403a8b4aadbd223dc489ecd22683 uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
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 "version=$("${CODEQL}" --version | head -n 1 | rev | cut -d " " -f 1 | rev)" >> $GITHUB_OUTPUT echo "::set-output name=version::$("${CODEQL}" --version | head -n 1 | rev | cut -d " " -f 1 | rev)"
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@77a8d2d10c0b403a8b4aadbd223dc489ecd22683 uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
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@77a8d2d10c0b403a8b4aadbd223dc489ecd22683 uses: github/codeql-action/analyze@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
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@77a8d2d10c0b403a8b4aadbd223dc489ecd22683 uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
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@77a8d2d10c0b403a8b4aadbd223dc489ecd22683 uses: github/codeql-action/init@71a8b35ff4c80fcfcd05bc1cd932fe3c08f943ca
with: with:
languages: javascript # does not matter languages: javascript # does not matter
- uses: actions/cache@v3 - uses: actions/cache@v3
@@ -47,3 +47,8 @@ jobs:
find ql/ql/src "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 "${CODEQL}" query format --check-only find ql/ql/src "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 "${CODEQL}" query format --check-only
env: env:
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }} CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
- name: Check QL compilation
run: |
"${CODEQL}" query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ql/extractor-pack" "ql/ql/src" "ql/ql/examples"
env:
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}

View File

@@ -86,24 +86,19 @@ jobs:
ruby/target/release/ruby-extractor.exe ruby/target/release/ruby-extractor.exe
retention-days: 1 retention-days: 1
compile-queries: compile-queries:
runs-on: ubuntu-latest-xl runs-on: ubuntu-latest
env:
CODEQL_THREADS: 4 # TODO: remove this once it's set by the CLI
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Fetch CodeQL - name: Fetch CodeQL
uses: ./.github/actions/fetch-codeql uses: ./.github/actions/fetch-codeql
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: ruby-build
- name: Build Query Pack - name: Build Query Pack
run: | run: |
rm -rf target/packs
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 ../shared/regex --output target/packs
codeql pack create ql/lib --output target/packs codeql pack create ql/lib --output target/packs
codeql pack create -j0 ql/src --output target/packs --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" codeql pack install ql/src
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
(cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;) (cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;)
@@ -207,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: "*" codeql/ruby-all: 0.0.1
extractor: ruby extractor: ruby
tests: . tests: .
' > qlpack.yml ' > qlpack.yml

View File

@@ -4,7 +4,7 @@ on:
push: push:
paths: paths:
- "ruby/**" - "ruby/**"
- .github/workflows/ruby-build.yml - .github/workflows/ruby-qltest.yml
- .github/actions/fetch-codeql/action.yml - .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml - codeql-workspace.yml
branches: branches:
@@ -28,6 +28,23 @@ 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:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- name: Check QL compilation
run: |
codeql query compile --check-only --threads=0 --ram 5000 --warnings=error "ql/src" "ql/examples"
env:
GITHUB_TOKEN: ${{ github.token }}
qlupgrade: qlupgrade:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -48,20 +65,17 @@ jobs:
xargs codeql execute upgrades testdb xargs codeql execute upgrades testdb
diff -q testdb/ruby.dbscheme downgrades/initial/ruby.dbscheme diff -q testdb/ruby.dbscheme downgrades/initial/ruby.dbscheme
qltest: qltest:
runs-on: ubuntu-latest-xl runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix:
slice: ["1/2", "2/2"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql - uses: ./.github/actions/fetch-codeql
- uses: ./ruby/actions/create-extractor-pack - uses: ./ruby/actions/create-extractor-pack
- name: Cache compilation cache
id: query-cache
uses: ./.github/actions/cache-query-compilation
with:
key: ruby-qltest
- name: Run QL tests - name: Run QL tests
run: | run: |
codeql test run --threads=0 --ram 52000 --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --check-undefined-labels --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" codeql test run --threads=0 --ram 5000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test
env: env:
GITHUB_TOKEN: ${{ github.token }} GITHUB_TOKEN: ${{ github.token }}

39
.github/workflows/swift-codegen.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
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

@@ -0,0 +1,45 @@
name: "Swift: Run Integration Tests"
on:
pull_request:
paths:
- "swift/**"
- "misc/bazel/**"
- "*.bazel*"
- .github/workflows/swift-integration-tests.yml
- .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
defaults:
run:
working-directory: swift
jobs:
integration-tests:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os:
- ubuntu-20.04
# - macos-latest TODO
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- uses: bazelbuild/setup-bazelisk@v2
- uses: actions/setup-python@v3
- name: Build Swift extractor
run: |
bazel run //swift:create-extractor-pack
- name: Get Swift version
id: get_swift_version
run: |
VERSION=$(bazel run //swift/extractor -- --version | sed -ne 's/.*version \(\S*\).*/\1/p')
echo "::set-output name=version::$VERSION"
- uses: swift-actions/setup-swift@v1
with:
swift-version: "${{steps.get_swift_version.outputs.version}}"
- name: Run integration tests
run: |
python integration-tests/runner.py

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

@@ -0,0 +1,43 @@
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 }}

View File

@@ -1,121 +0,0 @@
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/**'
- 'swift/ql/.generated.list'
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

2
.gitignore vendored
View File

@@ -27,6 +27,8 @@
# It's useful (though not required) to be able to unpack codeql in the ql checkout itself # It's useful (though not required) to be able to unpack codeql in the ql checkout itself
/codeql/ /codeql/
csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
# Avoid committing cached package components # Avoid committing cached package components
.codeql .codeql

View File

@@ -19,7 +19,7 @@ repos:
rev: v1.6.0 rev: v1.6.0
hooks: hooks:
- id: autopep8 - id: autopep8
files: ^swift/.*\.py files: ^swift/codegen/.*\.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)$|^config/identical-files\.json$ files: \.(qll?|qhelp|swift)$
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/(schema.py$|codegen/|.*/generated/|ql/lib/(swift\.dbscheme$|codeql/swift/elements)|ql/\.generated.list) files: ^swift/(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

@@ -1,5 +1,3 @@
{ {
"omnisharp.autoStart": false, "omnisharp.autoStart": false
"cmake.sourceDirectory": "${workspaceFolder}/swift",
"cmake.buildDirectory": "${workspaceFolder}/bazel-cmake-build"
} }

View File

@@ -5,7 +5,7 @@
/javascript/ @github/codeql-javascript /javascript/ @github/codeql-javascript
/python/ @github/codeql-python /python/ @github/codeql-python
/ruby/ @github/codeql-ruby /ruby/ @github/codeql-ruby
/swift/ @github/codeql-swift /swift/ @github/codeql-c
/java/kotlin-extractor/ @github/codeql-kotlin /java/kotlin-extractor/ @github/codeql-kotlin
/java/kotlin-explorer/ @github/codeql-kotlin /java/kotlin-explorer/ @github/codeql-kotlin
@@ -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/codeql-cli/ @github/codeql-cli-reviewers /docs/codeql-cli/ @github/codeql-cli-reviewers
/docs/codeql/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers /docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
/docs/codeql/ql-language-reference/ @github/codeql-frontend-reviewers /docs/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,9 +40,8 @@ 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.yml @github/codeql-swift /.github/workflows/swift-* @github/codeql-c

View File

@@ -4,7 +4,8 @@ This open source repository contains the standard CodeQL libraries and queries t
## How do I learn CodeQL and run queries? ## How do I learn CodeQL and run queries?
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL using the [CodeQL extension for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) and the [CodeQL CLI](https://codeql.github.com/docs/codeql-cli/). There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL.
You can use the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension or the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com (Semmle Legacy product) to try out your queries on any open source project that's currently being analyzed.
## Contributing ## Contributing

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 implicitly 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 implictly 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 triggered by mismatching declarations of a formatting function. | | Non-constant format string (`cpp/non-constant-format`) | Fewer false positive results | Fixed false positive results triggrered 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 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. * The predicates `RegExpTerm.getSuccessor` and `RegExpTerm.getPredecessor` have been changed to reflect textual, not operational, matching order. This only makes a difference in lookbehind assertions, which are operationally matched backwards. Previously, `getSuccessor` would mimick this, so in an assertion `(?<=ab)` the term `b` would be considered the predecessor, not the successor, of `a`. Textually, however, `a` is still matched before `b`, and this is the order we now follow.
* An extensible model of the `EventEmitter` pattern has been implemented. * 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,7 +17,6 @@ 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"
@@ -25,7 +24,7 @@ provide:
- "misc/suite-helpers/qlpack.yml" - "misc/suite-helpers/qlpack.yml"
- "ruby/extractor-pack/codeql-extractor.yml" - "ruby/extractor-pack/codeql-extractor.yml"
- "swift/extractor-pack/codeql-extractor.yml" - "swift/extractor-pack/codeql-extractor.yml"
- "ql/extractor-pack/codeql-extractor.yml" - "ql/extractor-pack/codeql-extractor.ym"
versionPolicies: versionPolicies:
default: default:

View File

@@ -1,21 +0,0 @@
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

@@ -1,68 +0,0 @@
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

@@ -1,7 +0,0 @@
const startApp = require('./app').startApp;
Logger = require('./logger').Logger;
Note = require('./models/note').Note;
User = require('./models/user').User;
startApp();

View File

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

View File

@@ -1,8 +0,0 @@
const mongoose = require('mongoose');
module.exports.Note = mongoose.model('Note', new mongoose.Schema({
title: String,
body: String,
ownerToken: String,
isPublic: Boolean
}));

View File

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

View File

@@ -1,44 +0,0 @@
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

@@ -1,37 +0,0 @@
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

@@ -1,25 +0,0 @@
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

@@ -33,9 +33,8 @@
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll", "python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll", "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll", "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForRegExp.qll", "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll", "ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll" "swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll"
], ],
"DataFlow Java/C++/C#/Python Common": [ "DataFlow Java/C++/C#/Python Common": [
@@ -70,6 +69,7 @@
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll", "python/ql/lib/semmle/python/dataflow/new/internal/tainttracking3/TaintTrackingImpl.qll",
"python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll", "python/ql/lib/semmle/python/dataflow/new/internal/tainttracking4/TaintTrackingImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll", "ruby/ql/lib/codeql/ruby/dataflow/internal/tainttracking1/TaintTrackingImpl.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/tainttrackingforlibraries/TaintTrackingImpl.qll",
"swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll" "swift/ql/lib/codeql/swift/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
], ],
"DataFlow Java/C++/C#/Python Consistency checks": [ "DataFlow Java/C++/C#/Python Consistency checks": [
@@ -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/modelgenerator/internal/CaptureModels.qll", "java/ql/src/utils/model-generator/internal/CaptureModels.qll",
"csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll" "csharp/ql/src/utils/model-generator/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,6 +486,40 @@
"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",
@@ -566,12 +600,8 @@
"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

@@ -257,11 +257,11 @@ namespace Semmle.Autobuild.Cpp.Tests
Actions.GetCurrentDirectory = cwd; Actions.GetCurrentDirectory = cwd;
Actions.IsWindows = isWindows; Actions.IsWindows = isWindows;
var options = new CppAutobuildOptions(Actions); var options = new AutobuildOptions(Actions, Language.Cpp);
return new CppAutobuilder(Actions, options); return new CppAutobuilder(Actions, options);
} }
void TestAutobuilderScript(CppAutobuilder autobuilder, int expectedOutput, int commandsRun) void TestAutobuilderScript(Autobuilder autobuilder, int expectedOutput, int commandsRun)
{ {
Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(Actions, StartCallback, EndCallback)); Assert.Equal(expectedOutput, autobuilder.GetBuildScript().Run(Actions, StartCallback, EndCallback));
@@ -299,7 +299,7 @@ namespace Semmle.Autobuild.Cpp.Tests
{ {
Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1; Actions.RunProcess[@"cmd.exe /C nuget restore C:\Project\test.sln -DisableParallelProcessing"] = 1;
Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0; Actions.RunProcess[@"cmd.exe /C C:\Project\.nuget\nuget.exe restore C:\Project\test.sln -DisableParallelProcessing"] = 0;
Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"""] = 0; Actions.RunProcess[@"cmd.exe /C CALL ^""C:\Program Files ^(x86^)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat^"" && set Platform=&& type NUL && C:\odasa\tools\odasa index --auto msbuild C:\Project\test.sln /t:rebuild /p:Platform=""x86"" /p:Configuration=""Release"" /p:MvcBuildViews=true"] = 0;
Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = ""; Actions.RunProcessOut[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = "";
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1; Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationPath"] = 1;
Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0; Actions.RunProcess[@"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe -prerelease -legacy -property installationVersion"] = 0;

View File

@@ -11,12 +11,11 @@
<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.2" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<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

@@ -2,26 +2,9 @@
namespace Semmle.Autobuild.Cpp namespace Semmle.Autobuild.Cpp
{ {
/// <summary> public class CppAutobuilder : Autobuilder
/// Encapsulates C++ build options.
/// </summary>
public class CppAutobuildOptions : AutobuildOptionsShared
{ {
public override Language Language => Language.Cpp; public CppAutobuilder(IBuildActions actions, AutobuildOptions options) : base(actions, options) { }
/// <summary>
/// Reads options from environment variables.
/// Throws ArgumentOutOfRangeException for invalid arguments.
/// </summary>
public CppAutobuildOptions(IBuildActions actions) : base(actions)
{
}
}
public class CppAutobuilder : Autobuilder<CppAutobuildOptions>
{
public CppAutobuilder(IBuildActions actions, CppAutobuildOptions options) : base(actions, options) { }
public override BuildScript GetBuildScript() public override BuildScript GetBuildScript()
{ {

View File

@@ -11,14 +11,14 @@ namespace Semmle.Autobuild.Cpp
try try
{ {
var actions = SystemBuildActions.Instance; var actions = SystemBuildActions.Instance;
var options = new CppAutobuildOptions(actions); var options = new AutobuildOptions(actions, Language.Cpp);
try try
{ {
Console.WriteLine("CodeQL C++ autobuilder"); Console.WriteLine("CodeQL C++ autobuilder");
var builder = new CppAutobuilder(actions, options); var builder = new CppAutobuilder(actions, options);
return builder.AttemptBuild(); return builder.AttemptBuild();
} }
catch (InvalidEnvironmentException ex) catch(InvalidEnvironmentException ex)
{ {
Console.WriteLine("The environment is invalid: {0}", ex.Message); Console.WriteLine("The environment is invalid: {0}", ex.Message);
} }

View File

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

View File

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

View File

@@ -1,36 +1,3 @@
## 0.4.4
No user-facing changes.
## 0.4.3
### Minor Analysis Improvements
* Fixed bugs in the `FormatLiteral` class that were causing `getMaxConvertedLength` and related predicates to return no results when the format literal was `%e`, `%f` or `%g` and an explicit precision was specified.
## 0.4.2
No user-facing changes.
## 0.4.1
No user-facing changes.
## 0.4.0
### Deprecated APIs
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
The old name still exists as a deprecated alias.
### New Features
* Added subclasses of `BuiltInOperations` for `__is_same`, `__is_function`, `__is_layout_compatible`, `__is_pointer_interconvertible_base_of`, `__is_array`, `__array_rank`, `__array_extent`, `__is_arithmetic`, `__is_complete_type`, `__is_compound`, `__is_const`, `__is_floating_point`, `__is_fundamental`, `__is_integral`, `__is_lvalue_reference`, `__is_member_function_pointer`, `__is_member_object_pointer`, `__is_member_pointer`, `__is_object`, `__is_pointer`, `__is_reference`, `__is_rvalue_reference`, `__is_scalar`, `__is_signed`, `__is_unsigned`, `__is_void`, and `__is_volatile`.
### Bug Fixes
* Fixed an issue in the taint tracking analysis where implicit reads were not allowed by default in sinks or additional taint steps that used flow states.
## 0.3.5 ## 0.3.5
## 0.3.4 ## 0.3.4

View File

@@ -1,14 +1,4 @@
## 0.4.0 ---
category: feature
### Deprecated APIs ---
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
The old name still exists as a deprecated alias.
### New Features
* Added subclasses of `BuiltInOperations` for `__is_same`, `__is_function`, `__is_layout_compatible`, `__is_pointer_interconvertible_base_of`, `__is_array`, `__array_rank`, `__array_extent`, `__is_arithmetic`, `__is_complete_type`, `__is_compound`, `__is_const`, `__is_floating_point`, `__is_fundamental`, `__is_integral`, `__is_lvalue_reference`, `__is_member_function_pointer`, `__is_member_object_pointer`, `__is_member_pointer`, `__is_object`, `__is_pointer`, `__is_reference`, `__is_rvalue_reference`, `__is_scalar`, `__is_signed`, `__is_unsigned`, `__is_void`, and `__is_volatile`. * Added subclasses of `BuiltInOperations` for `__is_same`, `__is_function`, `__is_layout_compatible`, `__is_pointer_interconvertible_base_of`, `__is_array`, `__array_rank`, `__array_extent`, `__is_arithmetic`, `__is_complete_type`, `__is_compound`, `__is_const`, `__is_floating_point`, `__is_fundamental`, `__is_integral`, `__is_lvalue_reference`, `__is_member_function_pointer`, `__is_member_object_pointer`, `__is_member_pointer`, `__is_object`, `__is_pointer`, `__is_reference`, `__is_rvalue_reference`, `__is_scalar`, `__is_signed`, `__is_unsigned`, `__is_void`, and `__is_volatile`.
### Bug Fixes
* Fixed an issue in the taint tracking analysis where implicit reads were not allowed by default in sinks or additional taint steps that used flow states.

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* Fixed an issue in the taint tracking analysis where implicit reads were not allowed by default in sinks or additional taint steps that used flow states.

View File

@@ -1,6 +1,5 @@
## 0.3.0 ---
category: deprecated
### Deprecated APIs ---
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide. * Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
The old name still exists as a deprecated alias. The old name still exists as a deprecated alias.

View File

@@ -1,6 +0,0 @@
---
category: deprecated
---
* Deprecated `semmle.code.cpp.valuenumbering.GlobalValueNumberingImpl`. Use `semmle.code.cpp.valuenumbering.GlobalValueNumbering`, which exposes the same API.

View File

@@ -1,4 +0,0 @@
---
category: breaking
---
The predicates in the `MustFlow::Configuration` class used by the `MustFlow` library (`semmle.code.cpp.ir.dataflow.MustFlow`) have changed to be defined directly in terms of the C++ IR instead of IR dataflow nodes.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Deleted the deprecated `getName` and `getShortName` predicates from the `Folder` class.

View File

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

View File

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

View File

@@ -1,5 +0,0 @@
## 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,3 +0,0 @@
## 0.4.4
No user-facing changes.

View File

@@ -1,2 +1,2 @@
--- ---
lastReleaseVersion: 0.4.4 lastReleaseVersion: 0.3.5

View File

@@ -20,8 +20,7 @@ module ProductFlow {
* `source1` and `source2` must belong to the same callable. * `source1` and `source2` must belong to the same callable.
*/ */
predicate isSourcePair( predicate isSourcePair(
DataFlow::Node source1, DataFlow::FlowState state1, DataFlow::Node source2, DataFlow::Node source1, string state1, DataFlow::Node source2, string state2
DataFlow::FlowState state2
) { ) {
state1 = "" and state1 = "" and
state2 = "" and state2 = "" and
@@ -50,101 +49,6 @@ module ProductFlow {
this.isSinkPair(sink1, sink2) this.isSinkPair(sink1, sink2)
} }
/**
* Holds if data flow through `node` is prohibited through the first projection of the product
* dataflow graph when the flow state is `state`.
*/
predicate isBarrier1(DataFlow::Node node, DataFlow::FlowState state) {
this.isBarrier1(node) and state = ""
}
/**
* Holds if data flow through `node` is prohibited through the second projection of the product
* dataflow graph when the flow state is `state`.
*/
predicate isBarrier2(DataFlow::Node node, DataFlow::FlowState state) {
this.isBarrier2(node) and state = ""
}
/**
* Holds if data flow through `node` is prohibited through the first projection of the product
* dataflow graph.
*/
predicate isBarrier1(DataFlow::Node node) { none() }
/**
* Holds if data flow through `node` is prohibited through the second projection of the product
* dataflow graph.
*/
predicate isBarrier2(DataFlow::Node node) { none() }
/**
* Holds if data flow out of `node` is prohibited in the first projection of the product
* dataflow graph.
*/
predicate isBarrierOut1(DataFlow::Node node) { none() }
/**
* Holds if data flow out of `node` is prohibited in the second projection of the product
* dataflow graph.
*/
predicate isBarrierOut2(DataFlow::Node node) { none() }
/*
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the first projection of the product dataflow graph.
*/
predicate isAdditionalFlowStep1(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the first projection of the product dataflow graph.
*
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep1(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
state1 instanceof DataFlow::FlowStateEmpty and
state2 instanceof DataFlow::FlowStateEmpty and
this.isAdditionalFlowStep1(node1, node2)
}
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the second projection of the product dataflow graph.
*/
predicate isAdditionalFlowStep2(DataFlow::Node node1, DataFlow::Node node2) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps in
* the second projection of the product dataflow graph.
*
* This step is only applicable in `state1` and updates the flow state to `state2`.
*/
predicate isAdditionalFlowStep2(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
state1 instanceof DataFlow::FlowStateEmpty and
state2 instanceof DataFlow::FlowStateEmpty and
this.isAdditionalFlowStep2(node1, node2)
}
/**
* Holds if data flow into `node` is prohibited in the first projection of the product
* dataflow graph.
*/
predicate isBarrierIn1(DataFlow::Node node) { none() }
/**
* Holds if data flow into `node` is prohibited in the second projection of the product
* dataflow graph.
*/
predicate isBarrierIn2(DataFlow::Node node) { none() }
predicate hasFlowPath( predicate hasFlowPath(
DataFlow::PathNode source1, DataFlow2::PathNode source2, DataFlow::PathNode sink1, DataFlow::PathNode source1, DataFlow2::PathNode source2, DataFlow::PathNode sink1,
DataFlow2::PathNode sink2 DataFlow2::PathNode sink2
@@ -159,78 +63,38 @@ module ProductFlow {
class Conf1 extends DataFlow::Configuration { class Conf1 extends DataFlow::Configuration {
Conf1() { this = "Conf1" } Conf1() { this = "Conf1" }
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { override predicate isSource(DataFlow::Node source, string state) {
exists(Configuration conf | conf.isSourcePair(source, state, _, _)) exists(Configuration conf | conf.isSourcePair(source, state, _, _))
} }
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { override predicate isSink(DataFlow::Node sink, string state) {
exists(Configuration conf | conf.isSinkPair(sink, state, _, _)) exists(Configuration conf | conf.isSinkPair(sink, state, _, _))
} }
override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
exists(Configuration conf | conf.isBarrier1(node, state))
}
override predicate isBarrierOut(DataFlow::Node node) {
exists(Configuration conf | conf.isBarrierOut1(node))
}
override predicate isAdditionalFlowStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
exists(Configuration conf | conf.isAdditionalFlowStep1(node1, state1, node2, state2))
}
override predicate isBarrierIn(DataFlow::Node node) {
exists(Configuration conf | conf.isBarrierIn1(node))
}
} }
class Conf2 extends DataFlow2::Configuration { class Conf2 extends DataFlow2::Configuration {
Conf2() { this = "Conf2" } Conf2() { this = "Conf2" }
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) { override predicate isSource(DataFlow::Node source, string state) {
exists(Configuration conf, DataFlow::PathNode source1 | exists(Configuration conf, DataFlow::Node source1 |
conf.isSourcePair(source1.getNode(), source1.getState(), source, state) and conf.isSourcePair(source1, _, source, state) and
any(Conf1 c).hasFlowPath(source1, _) any(Conf1 c).hasFlow(source1, _)
) )
} }
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) { override predicate isSink(DataFlow::Node sink, string state) {
exists(Configuration conf, DataFlow::PathNode sink1 | exists(Configuration conf, DataFlow::Node sink1 |
conf.isSinkPair(sink1.getNode(), sink1.getState(), sink, state) and conf.isSinkPair(sink1, _, sink, state) and any(Conf1 c).hasFlow(_, sink1)
any(Conf1 c).hasFlowPath(_, sink1)
) )
} }
override predicate isBarrier(DataFlow::Node node, DataFlow::FlowState state) {
exists(Configuration conf | conf.isBarrier2(node, state))
}
override predicate isBarrierOut(DataFlow::Node node) {
exists(Configuration conf | conf.isBarrierOut2(node))
}
override predicate isAdditionalFlowStep(
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
DataFlow::FlowState state2
) {
exists(Configuration conf | conf.isAdditionalFlowStep2(node1, state1, node2, state2))
}
override predicate isBarrierIn(DataFlow::Node node) {
exists(Configuration conf | conf.isBarrierIn2(node))
}
} }
} }
pragma[nomagic]
private predicate reachableInterprocEntry( private predicate reachableInterprocEntry(
Configuration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2, Configuration conf, DataFlow::PathNode source1, DataFlow2::PathNode source2,
DataFlow::PathNode node1, DataFlow2::PathNode node2 DataFlow::PathNode node1, DataFlow2::PathNode node2
) { ) {
conf.isSourcePair(node1.getNode(), node1.getState(), node2.getNode(), node2.getState()) and conf.isSourcePair(node1.getNode(), _, node2.getNode(), _) and
node1 = source1 and node1 = source1 and
node2 = source2 node2 = source2
or or
@@ -293,7 +157,7 @@ module ProductFlow {
) { ) {
exists(DataFlow::PathNode mid1, DataFlow2::PathNode mid2 | exists(DataFlow::PathNode mid1, DataFlow2::PathNode mid2 |
reachableInterprocEntry(conf, source1, source2, mid1, mid2) and reachableInterprocEntry(conf, source1, source2, mid1, mid2) and
conf.isSinkPair(sink1.getNode(), sink1.getState(), sink2.getNode(), sink2.getState()) and conf.isSinkPair(sink1.getNode(), _, sink2.getNode(), _) and
localPathStep1*(mid1, sink1) and localPathStep1*(mid1, sink1) and
localPathStep2*(mid2, sink2) localPathStep2*(mid2, sink2)
) )

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -136,18 +136,6 @@ 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

@@ -137,7 +137,7 @@ private newtype TReturnKind =
exists(IndirectReturnNode return, ReturnIndirectionInstruction returnInd | exists(IndirectReturnNode return, ReturnIndirectionInstruction returnInd |
returnInd.hasIndex(argumentIndex) and returnInd.hasIndex(argumentIndex) and
return.getAddressOperand() = returnInd.getSourceAddressOperand() and return.getAddressOperand() = returnInd.getSourceAddressOperand() and
indirectionIndex = return.getIndirectionIndex() indirectionIndex = return.getIndirectionIndex() - 1 // We subtract one because the return loads the value.
) )
} }
@@ -197,7 +197,7 @@ class ReturnIndirectionNode extends IndirectReturnNode, ReturnNode {
exists(int argumentIndex, ReturnIndirectionInstruction returnInd | exists(int argumentIndex, ReturnIndirectionInstruction returnInd |
returnInd.hasIndex(argumentIndex) and returnInd.hasIndex(argumentIndex) and
this.getAddressOperand() = returnInd.getSourceAddressOperand() and this.getAddressOperand() = returnInd.getSourceAddressOperand() and
result = TIndirectReturnKind(argumentIndex, this.getIndirectionIndex()) and result = TIndirectReturnKind(argumentIndex, this.getIndirectionIndex() - 1) and
hasNonInitializeParameterDef(returnInd.getIRVariable()) hasNonInitializeParameterDef(returnInd.getIRVariable())
) )
or or
@@ -241,7 +241,7 @@ private Instruction getANonConversionUse(Operand operand) {
/** /**
* Gets the operand that represents the first use of the value of `call` following * Gets the operand that represents the first use of the value of `call` following
* a sequence of conversion-like instructions. * a sequnce of conversion-like instructions.
*/ */
predicate operandForfullyConvertedCall(Operand operand, CallInstruction call) { predicate operandForfullyConvertedCall(Operand operand, CallInstruction call) {
exists(getANonConversionUse(operand)) and exists(getANonConversionUse(operand)) and
@@ -254,7 +254,7 @@ predicate operandForfullyConvertedCall(Operand operand, CallInstruction call) {
/** /**
* Gets the instruction that represents the first use of the value of `call` following * Gets the instruction that represents the first use of the value of `call` following
* a sequence of conversion-like instructions. * a sequnce of conversion-like instructions.
* *
* This predicate only holds if there is no suitable operand (i.e., no operand of a non- * This predicate only holds if there is no suitable operand (i.e., no operand of a non-
* conversion instruction) to use to represent the value of `call` after conversions. * conversion instruction) to use to represent the value of `call` after conversions.
@@ -365,7 +365,7 @@ predicate jumpStep(Node n1, Node n2) {
predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) { predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) {
exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store | exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store |
nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and
node2.getIndirectionIndex() = 1 and node2.getIndirectionIndex() = 0 and
numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(), numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(),
numberOfLoads) numberOfLoads)
| |
@@ -465,20 +465,20 @@ predicate clearsContent(Node n, Content c) {
predicate expectsContent(Node n, ContentSet c) { none() } predicate expectsContent(Node n, ContentSet c) { none() }
/** Gets the type of `n` used for type pruning. */ /** Gets the type of `n` used for type pruning. */
DataFlowType getNodeType(Node n) { IRType getNodeType(Node n) {
suppressUnusedNode(n) and suppressUnusedNode(n) and
result instanceof VoidType // stub implementation result instanceof IRVoidType // stub implementation
} }
/** Gets a string representation of a type returned by `getNodeType`. */ /** Gets a string representation of a type returned by `getNodeType`. */
string ppReprType(DataFlowType t) { none() } // stub implementation string ppReprType(IRType t) { none() } // stub implementation
/** /**
* Holds if `t1` and `t2` are compatible, that is, whether data can flow from * Holds if `t1` and `t2` are compatible, that is, whether data can flow from
* a node of type `t1` to a node of type `t2`. * a node of type `t1` to a node of type `t2`.
*/ */
pragma[inline] pragma[inline]
predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { predicate compatibleTypes(IRType t1, IRType t2) {
any() // stub implementation any() // stub implementation
} }
@@ -502,7 +502,7 @@ class DataFlowCallable = Cpp::Declaration;
class DataFlowExpr = Expr; class DataFlowExpr = Expr;
class DataFlowType = Type; class DataFlowType = IRType;
/** A function call relevant for data flow. */ /** A function call relevant for data flow. */
class DataFlowCall extends CallInstruction { class DataFlowCall extends CallInstruction {

View File

@@ -38,12 +38,13 @@ private module Cached {
TVariableNode(Variable var) or TVariableNode(Variable var) or
TPostFieldUpdateNode(FieldAddress operand, int indirectionIndex) { TPostFieldUpdateNode(FieldAddress operand, int indirectionIndex) {
indirectionIndex = indirectionIndex =
[1 .. Ssa::countIndirectionsForCppType(operand.getObjectAddress().getResultLanguageType())] [0 .. Ssa::countIndirectionsForCppType(operand.getObjectAddress().getResultLanguageType()) -
1]
} or } or
TSsaPhiNode(Ssa::PhiNode phi) or TSsaPhiNode(Ssa::PhiNode phi) or
TIndirectArgumentOutNode(ArgumentOperand operand, int indirectionIndex) { TIndirectArgumentOutNode(ArgumentOperand operand, int indirectionIndex) {
Ssa::isModifiableByCall(operand) and Ssa::isModifiableByCall(operand) and
indirectionIndex = [1 .. Ssa::countIndirectionsForCppType(operand.getLanguageType())] indirectionIndex = [0 .. Ssa::countIndirectionsForCppType(operand.getLanguageType()) - 1]
} or } or
TIndirectOperand(Operand op, int indirectionIndex) { TIndirectOperand(Operand op, int indirectionIndex) {
Ssa::hasIndirectOperand(op, indirectionIndex) Ssa::hasIndirectOperand(op, indirectionIndex)
@@ -112,7 +113,7 @@ class Node extends TIRDataFlowNode {
Declaration getFunction() { none() } // overridden in subclasses Declaration getFunction() { none() } // overridden in subclasses
/** Gets the type of this node. */ /** Gets the type of this node. */
DataFlowType getType() { none() } // overridden in subclasses IRType getType() { none() } // overridden in subclasses
/** Gets the instruction corresponding to this node, if any. */ /** Gets the instruction corresponding to this node, if any. */
Instruction asInstruction() { result = this.(InstructionNode).getInstruction() } Instruction asInstruction() { result = this.(InstructionNode).getInstruction() }
@@ -229,13 +230,7 @@ class Node extends TIRDataFlowNode {
Expr asIndirectArgument() { result = this.asIndirectArgument(_) } Expr asIndirectArgument() { result = this.asIndirectArgument(_) }
/** Gets the positional parameter corresponding to this node, if any. */ /** Gets the positional parameter corresponding to this node, if any. */
Parameter asParameter() { result = this.asParameter(0) } Parameter asParameter() { result = asParameter(0) }
/**
* Gets the uninitialized local variable corresponding to this node, if
* any.
*/
LocalVariable asUninitialized() { result = this.(UninitializedNode).getLocalVariable() }
/** /**
* Gets the positional parameter corresponding to the node that represents * Gets the positional parameter corresponding to the node that represents
@@ -278,7 +273,7 @@ class Node extends TIRDataFlowNode {
/** /**
* Gets an upper bound on the type of this node. * Gets an upper bound on the type of this node.
*/ */
DataFlowType getTypeBound() { result = this.getType() } IRType getTypeBound() { result = this.getType() }
/** Gets the location of this element. */ /** Gets the location of this element. */
cached cached
@@ -327,7 +322,7 @@ class InstructionNode extends Node, TInstructionNode {
override Declaration getFunction() { result = instr.getEnclosingFunction() } override Declaration getFunction() { result = instr.getEnclosingFunction() }
override DataFlowType getType() { result = instr.getResultType() } override IRType getType() { result = instr.getResultIRType() }
final override Location getLocationImpl() { result = instr.getLocation() } final override Location getLocationImpl() { result = instr.getLocation() }
@@ -353,32 +348,13 @@ class OperandNode extends Node, TOperandNode {
override Declaration getFunction() { result = op.getUse().getEnclosingFunction() } override Declaration getFunction() { result = op.getUse().getEnclosingFunction() }
override DataFlowType getType() { result = op.getType() } override IRType getType() { result = op.getIRType() }
final override Location getLocationImpl() { result = op.getLocation() } final override Location getLocationImpl() { result = op.getLocation() }
override string toStringImpl() { result = this.getOperand().toString() } override string toStringImpl() { result = this.getOperand().toString() }
} }
/**
* Returns `t`, but stripped of the `n` outermost pointers, references, etc.
*
* For example, `stripPointers(int*&, 2)` is `int` and `stripPointers(int*, 0)` is `int*`.
*/
private Type stripPointers(Type t, int n) {
result = t and n = 0
or
result = stripPointers(t.(PointerType).getBaseType(), n - 1)
or
result = stripPointers(t.(ArrayType).getBaseType(), n - 1)
or
result = stripPointers(t.(ReferenceType).getBaseType(), n - 1)
or
result = stripPointers(t.(PointerToMemberType).getBaseType(), n - 1)
or
result = stripPointers(t.(FunctionPointerIshType).getBaseType(), n - 1)
}
/** /**
* INTERNAL: do not use. * INTERNAL: do not use.
* *
@@ -394,6 +370,8 @@ class PostFieldUpdateNode extends TPostFieldUpdateNode, PartialDefinitionNode {
override Declaration getEnclosingCallable() { result = this.getFunction() } override Declaration getEnclosingCallable() { result = this.getFunction() }
override IRType getType() { result = fieldAddress.getIRType() }
FieldAddress getFieldAddress() { result = fieldAddress } FieldAddress getFieldAddress() { result = fieldAddress }
Field getUpdatedField() { result = fieldAddress.getField() } Field getUpdatedField() { result = fieldAddress.getField() }
@@ -401,8 +379,10 @@ class PostFieldUpdateNode extends TPostFieldUpdateNode, PartialDefinitionNode {
int getIndirectionIndex() { result = indirectionIndex } int getIndirectionIndex() { result = indirectionIndex }
override Node getPreUpdateNode() { override Node getPreUpdateNode() {
// + 1 because we're storing into an lvalue, and the original node should be the rvalue of
// the same address.
hasOperandAndIndex(result, pragma[only_bind_into](fieldAddress).getObjectAddressOperand(), hasOperandAndIndex(result, pragma[only_bind_into](fieldAddress).getObjectAddressOperand(),
indirectionIndex) indirectionIndex + 1)
} }
override Expr getDefinedExpr() { override Expr getDefinedExpr() {
@@ -431,26 +411,11 @@ class SsaPhiNode extends Node, TSsaPhiNode {
override Declaration getFunction() { result = phi.getBasicBlock().getEnclosingFunction() } override Declaration getFunction() { result = phi.getBasicBlock().getEnclosingFunction() }
override DataFlowType getType() { result = this.getAnInput().getType() } override IRType getType() { result instanceof IRVoidType }
final override Location getLocationImpl() { result = phi.getBasicBlock().getLocation() } final override Location getLocationImpl() { result = phi.getBasicBlock().getLocation() }
override string toStringImpl() { result = "Phi" } override string toStringImpl() { result = "Phi" }
/**
* Gets a node that is used as input to this phi node.
* `fromBackEdge` is true if data flows along a back-edge,
* and `false` otherwise.
*/
final Node getAnInput(boolean fromBackEdge) {
localFlowStep(result, this) and
if phi.getBasicBlock().dominates(getBasicBlock(result))
then fromBackEdge = true
else fromBackEdge = false
}
/** Gets a node that is used as input to this phi node. */
final Node getAnInput() { result = this.getAnInput(_) }
} }
/** /**
@@ -474,6 +439,8 @@ class SideEffectOperandNode extends Node, IndirectOperand {
override Function getFunction() { result = call.getEnclosingFunction() } override Function getFunction() { result = call.getEnclosingFunction() }
override IRType getType() { result instanceof IRVoidType }
Expr getArgument() { result = call.getArgument(argumentIndex).getUnconvertedResultExpression() } Expr getArgument() { result = call.getArgument(argumentIndex).getUnconvertedResultExpression() }
} }
@@ -496,6 +463,8 @@ class IndirectParameterNode extends Node, IndirectInstruction {
override Function getFunction() { result = this.getInstruction().getEnclosingFunction() } override Function getFunction() { result = this.getInstruction().getEnclosingFunction() }
override IRType getType() { result instanceof IRVoidType }
override string toStringImpl() { override string toStringImpl() {
result = this.getParameter().toString() + " indirection" result = this.getParameter().toString() + " indirection"
or or
@@ -520,6 +489,8 @@ class IndirectReturnNode extends IndirectOperand {
Operand getAddressOperand() { result = operand } Operand getAddressOperand() { result = operand }
override Declaration getEnclosingCallable() { result = this.getFunction() } override Declaration getEnclosingCallable() { result = this.getFunction() }
override IRType getType() { result instanceof IRVoidType }
} }
/** /**
@@ -550,7 +521,9 @@ class IndirectArgumentOutNode extends Node, TIndirectArgumentOutNode, PostUpdate
override Function getFunction() { result = this.getCallInstruction().getEnclosingFunction() } override Function getFunction() { result = this.getCallInstruction().getEnclosingFunction() }
override Node getPreUpdateNode() { hasOperandAndIndex(result, operand, indirectionIndex) } override IRType getType() { result instanceof IRVoidType }
override Node getPreUpdateNode() { hasOperandAndIndex(result, operand, indirectionIndex + 1) }
override string toStringImpl() { override string toStringImpl() {
// This string should be unique enough to be helpful but common enough to // This string should be unique enough to be helpful but common enough to
@@ -606,38 +579,6 @@ class IndirectReturnOutNode extends Node {
int getIndirectionIndex() { result = indirectionIndex } int getIndirectionIndex() { result = indirectionIndex }
} }
private PointerType getGLValueType(Type t, int indirectionIndex) {
result.getBaseType() = stripPointers(t, indirectionIndex - 1)
}
bindingset[isGLValue]
private DataFlowType getTypeImpl(Type t, int indirectionIndex, boolean isGLValue) {
if isGLValue = true
then
result = getGLValueType(t, indirectionIndex)
or
// Ideally, the above case would cover all glvalue cases. However, consider the case where
// the database consists only of:
// ```
// void test() {
// int* x;
// x = nullptr;
// }
// ```
// and we want to compute the type of `*x` in the assignment `x = nullptr`. Here, `x` is an lvalue
// of type int* (which morally is an int**). So when we call `getTypeImpl` it will be with the
// parameters:
// - t = int*
// - indirectionIndex = 1 (when we want to model the dataflow node corresponding to *x)
// - isGLValue = true
// In this case, `getTypeImpl(t, indirectionIndex, isGLValue)` should give back `int**`. In this
// case, however, `int**` does not exist in the database. So instead we return int* (which is
// wrong, but at least we have a type).
not exists(getGLValueType(t, indirectionIndex)) and
result = stripPointers(t, indirectionIndex - 1)
else result = stripPointers(t, indirectionIndex)
}
/** /**
* INTERNAL: Do not use. * INTERNAL: Do not use.
* *
@@ -659,11 +600,7 @@ class IndirectOperand extends Node, TIndirectOperand {
override Declaration getEnclosingCallable() { result = this.getFunction() } override Declaration getEnclosingCallable() { result = this.getFunction() }
override DataFlowType getType() { override IRType getType() { result = this.getOperand().getIRType() }
exists(boolean isGLValue | if operand.isGLValue() then isGLValue = true else isGLValue = false |
result = getTypeImpl(operand.getType().getUnspecifiedType(), indirectionIndex, isGLValue)
)
}
final override Location getLocationImpl() { result = this.getOperand().getLocation() } final override Location getLocationImpl() { result = this.getOperand().getLocation() }
@@ -672,25 +609,6 @@ class IndirectOperand extends Node, TIndirectOperand {
} }
} }
/**
* The value of an uninitialized local variable, viewed as a node in a data
* flow graph.
*/
class UninitializedNode extends Node {
LocalVariable v;
UninitializedNode() {
exists(Ssa::Def def |
def.getDefiningInstruction() instanceof UninitializedInstruction and
Ssa::nodeToDefOrUse(this, def) and
v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable().getAst()
)
}
/** Gets the uninitialized local variable corresponding to this node. */
LocalVariable getLocalVariable() { result = v }
}
/** /**
* INTERNAL: Do not use. * INTERNAL: Do not use.
* *
@@ -712,11 +630,7 @@ class IndirectInstruction extends Node, TIndirectInstruction {
override Declaration getEnclosingCallable() { result = this.getFunction() } override Declaration getEnclosingCallable() { result = this.getFunction() }
override DataFlowType getType() { override IRType getType() { result = this.getInstruction().getResultIRType() }
exists(boolean isGLValue | if instr.isGLValue() then isGLValue = true else isGLValue = false |
result = getTypeImpl(instr.getResultType().getUnspecifiedType(), indirectionIndex, isGLValue)
)
}
final override Location getLocationImpl() { result = this.getInstruction().getLocation() } final override Location getLocationImpl() { result = this.getInstruction().getLocation() }
@@ -746,7 +660,7 @@ predicate exprNodeShouldBeOperand(Node node, Expr e) {
/** /**
* Holds if `load` is a `LoadInstruction` that is the result of evaluating `e` * Holds if `load` is a `LoadInstruction` that is the result of evaluating `e`
* and `node` is an `IndirectOperandNode` that should map `node.asExpr()` to `e`. * and `node` is an `IndirctOperandNode` that should map `node.asExpr()` to `e`.
* *
* We map `e` to `node.asExpr()` when `node` semantically represents the * We map `e` to `node.asExpr()` when `node` semantically represents the
* same value as `load`. A subsequent flow step will flow `node` to * same value as `load`. A subsequent flow step will flow `node` to
@@ -930,8 +844,6 @@ abstract class PostUpdateNode extends Node {
* Gets the node before the state update. * Gets the node before the state update.
*/ */
abstract Node getPreUpdateNode(); abstract Node getPreUpdateNode();
final override DataFlowType getType() { result = this.getPreUpdateNode().getType() }
} }
/** /**
@@ -995,7 +907,7 @@ class VariableNode extends Node, TVariableNode {
result = v result = v
} }
override DataFlowType getType() { result = v.getType() } override IRType getType() { result.getCanonicalLanguageType().hasUnspecifiedType(v.getType(), _) }
final override Location getLocationImpl() { result = v.getLocation() } final override Location getLocationImpl() { result = v.getLocation() }
@@ -1148,7 +1060,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
store.getDestinationAddressOperand() = address store.getDestinationAddressOperand() = address
) )
or or
Ssa::outNodeHasAddressAndIndex(nodeFrom, address, indirectionIndex) Ssa::outNodeHasAddressAndIndex(nodeFrom, address, indirectionIndex - 1)
) )
} }

View File

@@ -41,7 +41,7 @@ Node callOutput(CallInstruction call, FunctionOutput output) {
// The side effect of a call on the value pointed to by an argument or qualifier // The side effect of a call on the value pointed to by an argument or qualifier
exists(int index, int indirectionIndex | exists(int index, int indirectionIndex |
result.(IndirectArgumentOutNode).getArgumentIndex() = index and result.(IndirectArgumentOutNode).getArgumentIndex() = index and
result.(IndirectArgumentOutNode).getIndirectionIndex() = indirectionIndex and result.(IndirectArgumentOutNode).getIndirectionIndex() + 1 = indirectionIndex and
result.(IndirectArgumentOutNode).getCallInstruction() = call and result.(IndirectArgumentOutNode).getCallInstruction() = call and
output.isParameterDerefOrQualifierObject(index, indirectionIndex) output.isParameterDerefOrQualifierObject(index, indirectionIndex)
) )

View File

@@ -100,7 +100,7 @@ private string getNodeProperty(DataFlow::Node node, string key) {
or or
// Is there partial flow from a source to this node? // Is there partial flow from a source to this node?
// This property will only be emitted if partial flow is enabled by overriding // This property will only be emitted if partial flow is enabled by overriding
// `DataFlow::Configuration::explorationLimit()`. // `DataFlow::Configration::explorationLimit()`.
key = "pflow" and key = "pflow" and
result = result =
strictconcat(DataFlow::PartialPathNode sourceNode, DataFlow::PartialPathNode destNode, int dist, strictconcat(DataFlow::PartialPathNode sourceNode, DataFlow::PartialPathNode destNode, int dist,

View File

@@ -301,12 +301,7 @@ private predicate defToNode(Node nodeFrom, Def def) {
nodeHasInstruction(nodeFrom, def.getDefiningInstruction(), def.getIndirectionIndex()) nodeHasInstruction(nodeFrom, def.getDefiningInstruction(), def.getIndirectionIndex())
} }
/** private predicate nodeToDefOrUse(Node nodeFrom, SsaDefOrUse defOrUse) {
* INTERNAL: Do not use.
*
* Holds if `nodeFrom` is the node that correspond to the definition or use `defOrUse`.
*/
predicate nodeToDefOrUse(Node nodeFrom, SsaDefOrUse defOrUse) {
// Node -> Def // Node -> Def
defToNode(nodeFrom, defOrUse) defToNode(nodeFrom, defOrUse)
or or

View File

@@ -11,9 +11,7 @@ private import DataFlowUtil
* corresponding `(Indirect)OperandNode`. * corresponding `(Indirect)OperandNode`.
*/ */
predicate ignoreOperand(Operand operand) { predicate ignoreOperand(Operand operand) {
operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() or operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand()
operand = any(Instruction instr | ignoreInstruction(instr)).getAUse() or
operand instanceof MemoryOperand
} }
/** /**

View File

@@ -36,7 +36,7 @@ private module SourceVariables {
override string toString() { result = var.toString() } override string toString() { result = var.toString() }
override DataFlowType getType() { result = var.getType() } override DataFlowType getType() { result = var.getIRType() }
} }
class BaseCallVariable extends BaseSourceVariable, TBaseCallVariable { class BaseCallVariable extends BaseSourceVariable, TBaseCallVariable {
@@ -48,7 +48,7 @@ private module SourceVariables {
override string toString() { result = call.toString() } override string toString() { result = call.toString() }
override DataFlowType getType() { result = call.getResultType() } override DataFlowType getType() { result = call.getResultIRType() }
} }
private newtype TSourceVariable = private newtype TSourceVariable =

View File

@@ -7,7 +7,6 @@ private import semmle.code.cpp.ir.IR as IR
private import Semantic private import Semantic
private import experimental.semmle.code.cpp.rangeanalysis.Bound as IRBound private import experimental.semmle.code.cpp.rangeanalysis.Bound as IRBound
private import semmle.code.cpp.controlflow.IRGuards as IRGuards private import semmle.code.cpp.controlflow.IRGuards as IRGuards
private import semmle.code.cpp.ir.ValueNumbering
module SemanticExprConfig { module SemanticExprConfig {
class Location = Cpp::Location; class Location = Cpp::Location;
@@ -121,15 +120,7 @@ module SemanticExprConfig {
newtype TSsaVariable = newtype TSsaVariable =
TSsaInstruction(IR::Instruction instr) { instr.hasMemoryResult() } or TSsaInstruction(IR::Instruction instr) { instr.hasMemoryResult() } or
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() } or TSsaOperand(IR::Operand op) { op.isDefinitionInexact() }
TSsaPointerArithmeticGuard(IR::PointerArithmeticInstruction instr) {
exists(Guard g, IR::Operand use | use = instr.getAUse() |
g.comparesLt(use, _, _, _, _) or
g.comparesLt(_, use, _, _, _) or
g.comparesEq(use, _, _, _, _) or
g.comparesEq(_, use, _, _, _)
)
}
class SsaVariable extends TSsaVariable { class SsaVariable extends TSsaVariable {
string toString() { none() } string toString() { none() }
@@ -138,8 +129,6 @@ module SemanticExprConfig {
IR::Instruction asInstruction() { none() } IR::Instruction asInstruction() { none() }
IR::PointerArithmeticInstruction asPointerArithGuard() { none() }
IR::Operand asOperand() { none() } IR::Operand asOperand() { none() }
} }
@@ -155,18 +144,6 @@ module SemanticExprConfig {
final override IR::Instruction asInstruction() { result = instr } final override IR::Instruction asInstruction() { result = instr }
} }
class SsaPointerArithmeticGuard extends SsaVariable, TSsaPointerArithmeticGuard {
IR::PointerArithmeticInstruction instr;
SsaPointerArithmeticGuard() { this = TSsaPointerArithmeticGuard(instr) }
final override string toString() { result = instr.toString() }
final override Location getLocation() { result = instr.getLocation() }
final override IR::PointerArithmeticInstruction asPointerArithGuard() { result = instr }
}
class SsaOperand extends SsaVariable, TSsaOperand { class SsaOperand extends SsaVariable, TSsaOperand {
IR::Operand op; IR::Operand op;
@@ -191,11 +168,7 @@ module SemanticExprConfig {
) )
} }
Expr getAUse(SsaVariable v) { Expr getAUse(SsaVariable v) { result.(IR::LoadInstruction).getSourceValue() = v.asInstruction() }
result.(IR::LoadInstruction).getSourceValue() = v.asInstruction()
or
result = valueNumber(v.asPointerArithGuard()).getAnInstruction()
}
SemType getSsaVariableType(SsaVariable v) { SemType getSsaVariableType(SsaVariable v) {
result = getSemanticType(v.asInstruction().getResultIRType()) result = getSemanticType(v.asInstruction().getResultIRType())
@@ -235,9 +208,7 @@ module SemanticExprConfig {
final override predicate hasRead(SsaVariable v) { final override predicate hasRead(SsaVariable v) {
exists(IR::Operand operand | exists(IR::Operand operand |
operand.getDef() = v.asInstruction() or operand.getDef() = v.asInstruction() and
operand.getDef() = valueNumber(v.asPointerArithGuard()).getAnInstruction()
|
not operand instanceof IR::PhiInputOperand and not operand instanceof IR::PhiInputOperand and
operand.getUse().getBlock() = block operand.getUse().getBlock() = block
) )
@@ -256,9 +227,7 @@ module SemanticExprConfig {
final override predicate hasRead(SsaVariable v) { final override predicate hasRead(SsaVariable v) {
exists(IR::PhiInputOperand operand | exists(IR::PhiInputOperand operand |
operand.getDef() = v.asInstruction() or operand.getDef() = v.asInstruction() and
operand.getDef() = valueNumber(v.asPointerArithGuard()).getAnInstruction()
|
operand.getPredecessorBlock() = pred and operand.getPredecessorBlock() = pred and
operand.getUse().getBlock() = succ operand.getUse().getBlock() = succ
) )

View File

@@ -10,7 +10,7 @@ class SemSsaVariable instanceof Specific::SsaVariable {
final Specific::Location getLocation() { result = super.getLocation() } final Specific::Location getLocation() { result = super.getLocation() }
final SemExpr getAUse() { result = Specific::getAUse(this) } final SemLoadExpr getAUse() { result = Specific::getAUse(this) }
final SemType getType() { result = Specific::getSsaVariableType(this) } final SemType getType() { result = Specific::getSsaVariableType(this) }

View File

@@ -4,12 +4,6 @@
* 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
@@ -168,11 +162,6 @@ 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
@@ -180,12 +169,6 @@ 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
@@ -193,12 +176,6 @@ 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

@@ -71,7 +71,7 @@ abstract class CustomSignDef extends SignDef {
* Concrete implementations extend one of the following subclasses: * Concrete implementations extend one of the following subclasses:
* - `ConstantSignExpr`, for expressions with a compile-time constant value. * - `ConstantSignExpr`, for expressions with a compile-time constant value.
* - `FlowSignExpr`, for expressions whose sign can be computed from the signs of their operands. * - `FlowSignExpr`, for expressions whose sign can be computed from the signs of their operands.
* - `CustomsignExpr`, for expressions whose sign can be computed by a language-specific * - `CustomsignExpr`, for expressions shose sign can be computed by a language-specific
* implementation. * implementation.
* *
* If the same expression matches more than one of the above subclasses, the sign is computed as * If the same expression matches more than one of the above subclasses, the sign is computed as

View File

@@ -11,7 +11,7 @@ private import experimental.semmle.code.cpp.semantic.Semantic
predicate ignoreTypeRestrictions(SemExpr e) { none() } predicate ignoreTypeRestrictions(SemExpr e) { none() }
/** /**
* Workaround to track the sign of certain expressions even if the type of the expression is not * Workaround to track the sign of cetain expressions even if the type of the expression is not
* numeric. * numeric.
*/ */
predicate trackUnknownNonNumericExpr(SemExpr e) { none() } predicate trackUnknownNonNumericExpr(SemExpr e) { none() }

View File

@@ -1,9 +1,9 @@
name: codeql/cpp-all name: codeql/cpp-all
version: 0.4.5-dev version: 0.4.0-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: ${workspace} codeql/ssa: 0.0.1

View File

@@ -189,6 +189,18 @@ class Folder extends Container, @folder {
* Gets the URL of this folder. * Gets the URL of this folder.
*/ */
deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" } deprecated override string getURL() { result = "file://" + this.getAbsolutePath() + ":0:0:0:0" }
/**
* DEPRECATED: use `getAbsolutePath` instead.
* Gets the name of this folder.
*/
deprecated string getName() { folders(underlyingElement(this), result) }
/**
* DEPRECATED: use `getBaseName` instead.
* Gets the last part of the folder name.
*/
deprecated string getShortName() { result = this.getBaseName() }
} }
/** /**

View File

@@ -1,5 +1,5 @@
/** /**
* Provides the `LinkTarget` class representing linker invocations during the build process. * Proivdes the `LinkTarget` class representing linker invocations during the build process.
*/ */
import semmle.code.cpp.Class import semmle.code.cpp.Class

View File

@@ -144,7 +144,7 @@ class Variable extends Declaration, @variable {
* `Variable.getInitializer()` to get the variable's initializer, * `Variable.getInitializer()` to get the variable's initializer,
* or use `Variable.getAnAssignedValue()` to get an expression that * or use `Variable.getAnAssignedValue()` to get an expression that
* is the right-hand side of an assignment or an initialization of * is the right-hand side of an assignment or an initialization of
* the variable. * the varible.
*/ */
Assignment getAnAssignment() { result.getLValue() = this.getAnAccess() } Assignment getAnAssignment() { result.getLValue() = this.getAnAccess() }
@@ -173,7 +173,7 @@ class Variable extends Declaration, @variable {
} }
/** /**
* Holds if this variable is declared as part of a structured binding * Holds if this variable is declated as part of a structured binding
* declaration. For example, `x` in `auto [x, y] = ...`. * declaration. For example, `x` in `auto [x, y] = ...`.
*/ */
predicate isStructuredBinding() { is_structured_binding(underlyingElement(this)) } predicate isStructuredBinding() { is_structured_binding(underlyingElement(this)) }

View File

@@ -76,7 +76,7 @@ class TypeBoundsAnalysis extends BufferWriteEstimationReason, TTypeBoundsAnalysi
/** /**
* The estimation comes from non trivial bounds found via actual flow analysis, * The estimation comes from non trivial bounds found via actual flow analysis,
* but a widening approximation might have been used for variables in loops. * but a widening aproximation might have been used for variables in loops.
* For example * For example
* ``` * ```
* for (int i = 0; i < 10; ++i) { * for (int i = 0; i < 10; ++i) {
@@ -141,7 +141,7 @@ class AttributeFormattingFunction extends FormattingFunction {
* - `""` is a `vprintf` variant, `outputParamIndex` is `-1`. * - `""` is a `vprintf` variant, `outputParamIndex` is `-1`.
* - `"f"` is a `vfprintf` variant, `outputParamIndex` indicates the output stream parameter. * - `"f"` is a `vfprintf` variant, `outputParamIndex` indicates the output stream parameter.
* - `"s"` is a `vsprintf` variant, `outputParamIndex` indicates the output buffer parameter. * - `"s"` is a `vsprintf` variant, `outputParamIndex` indicates the output buffer parameter.
* - `"?"` if the type cannot be determined. `outputParamIndex` is `-1`. * - `"?"` if the type cannot be deteremined. `outputParamIndex` is `-1`.
*/ */
predicate primitiveVariadicFormatter( predicate primitiveVariadicFormatter(
TopLevelFunction f, string type, int formatParamIndex, int outputParamIndex TopLevelFunction f, string type, int formatParamIndex, int outputParamIndex
@@ -198,7 +198,7 @@ private predicate callsVariadicFormatter(
* - `""` is a `vprintf` variant, `outputParamIndex` is `-1`. * - `""` is a `vprintf` variant, `outputParamIndex` is `-1`.
* - `"f"` is a `vfprintf` variant, `outputParamIndex` indicates the output stream parameter. * - `"f"` is a `vfprintf` variant, `outputParamIndex` indicates the output stream parameter.
* - `"s"` is a `vsprintf` variant, `outputParamIndex` indicates the output buffer parameter. * - `"s"` is a `vsprintf` variant, `outputParamIndex` indicates the output buffer parameter.
* - `"?"` if the type cannot be determined. `outputParamIndex` is `-1`. * - `"?"` if the type cannot be deteremined. `outputParamIndex` is `-1`.
*/ */
predicate variadicFormatter(Function f, string type, int formatParamIndex, int outputParamIndex) { predicate variadicFormatter(Function f, string type, int formatParamIndex, int outputParamIndex) {
primitiveVariadicFormatter(f, type, formatParamIndex, outputParamIndex) primitiveVariadicFormatter(f, type, formatParamIndex, outputParamIndex)
@@ -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) (
then afterdot = this.getPrecision(n) if this.hasExplicitPrecision(n)
else ( then afterdot = this.getPrecision(n)
not this.hasImplicitPrecision(n) and else not this.hasImplicitPrecision(n)
afterdot = 6 ) and
) 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) (
then afterdot = this.getPrecision(n) if this.hasExplicitPrecision(n)
else ( then afterdot = this.getPrecision(n)
not this.hasImplicitPrecision(n) and else not this.hasImplicitPrecision(n)
afterdot = 6 ) and
) 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) (
then afterdot = this.getPrecision(n) if this.hasExplicitPrecision(n)
else ( then afterdot = this.getPrecision(n)
not this.hasImplicitPrecision(n) and else not this.hasImplicitPrecision(n)
afterdot = 6 ) and
) 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

@@ -6,7 +6,7 @@ import cpp
* A function that concatenates the string from its second argument * A function that concatenates the string from its second argument
* to the string from its first argument, for example `strcat`. * to the string from its first argument, for example `strcat`.
*/ */
deprecated class StrcatFunction extends Function { class StrcatFunction extends Function {
StrcatFunction() { StrcatFunction() {
getName() = getName() =
[ [

View File

@@ -12,7 +12,7 @@ private import internal.ConstantExprs
* relation). The refinement manifests itself in two changes: * relation). The refinement manifests itself in two changes:
* *
* - The successor relation on `BasicBlock`s uses `successors_adapted` * - The successor relation on `BasicBlock`s uses `successors_adapted`
* (instead of `successors_extended` used by `PrimitiveBasicBlock`s). Consequently, * (instead of `successors_extended` used by `PrimtiveBasicBlock`s). Consequently,
* some edges between `BasicBlock`s may be removed. Example: * some edges between `BasicBlock`s may be removed. Example:
* ``` * ```
* x = 1; // s1 * x = 1; // s1

View File

@@ -149,7 +149,7 @@ private predicate bbLoopEntryConditionAlwaysTrueAt(BasicBlock bb, int i, Control
/** /**
* Basic block `pred` contains all or part of the condition belonging to a loop, * Basic block `pred` contains all or part of the condition belonging to a loop,
* and there is an edge from `pred` to `succ` that concludes the condition. * and there is an edge from `pred` to `succ` that concludes the condition.
* If the edge corresponds with the loop condition being found to be `true`, then * If the edge corrseponds with the loop condition being found to be `true`, then
* `skipsLoop` is `false`. Otherwise the edge corresponds with the loop condition * `skipsLoop` is `false`. Otherwise the edge corresponds with the loop condition
* being found to be `false` and `skipsLoop` is `true`. Non-concluding edges * being found to be `false` and `skipsLoop` is `true`. Non-concluding edges
* within a complex loop condition are not matched by this predicate. * within a complex loop condition are not matched by this predicate.

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -136,18 +136,6 @@ 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,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -1137,7 +1137,7 @@ class BuiltInOperationIsArray extends BuiltInOperation, @isarray {
* A C++ `__array_rank` built-in operation (used by some implementations of the * A C++ `__array_rank` built-in operation (used by some implementations of the
* `<type_traits>` header). * `<type_traits>` header).
* *
* If known, returns the number of dimensions of an arrary type. * If known, returns the number of dimentsions of an arrary type.
* ``` * ```
* template<typename _Tp> * template<typename _Tp>
* struct rank * struct rank

View File

@@ -494,7 +494,7 @@ class VacuousDestructorCall extends Expr, @vacuous_destructor_call {
* An initialization of a base class or member variable performed as part * An initialization of a base class or member variable performed as part
* of a constructor's explicit initializer list or implicit actions. * of a constructor's explicit initializer list or implicit actions.
* *
* This is a QL root class for representing various types of constructor * This is a QL root class for reprenting various types of constructor
* initializations. * initializations.
*/ */
class ConstructorInit extends Expr, @ctorinit { class ConstructorInit extends Expr, @ctorinit {

View File

@@ -779,7 +779,7 @@ class AlignofExprOperator extends AlignofOperator {
/** /**
* A C++11 `alignof` expression whose operand is a type name. * A C++11 `alignof` expression whose operand is a type name.
* ``` * ```
* bool proper_alignment = (alignof(T) == alignof(T[0]); * bool proper_alignment = (alingof(T) == alignof(T[0]);
* ``` * ```
*/ */
class AlignofTypeOperator extends AlignofOperator { class AlignofTypeOperator extends AlignofOperator {

View File

@@ -451,7 +451,7 @@ class Expr extends StmtParent, @expr {
// For performance, we avoid a full transitive closure over `getConversion`. // For performance, we avoid a full transitive closure over `getConversion`.
// Since there can be several implicit conversions before and after an // Since there can be several implicit conversions before and after an
// explicit conversion, use `getImplicitlyConverted` to step over them // explicit conversion, use `getImplicitlyConverted` to step over them
// cheaply. Then, if there is an explicit conversion following the implicit // cheaply. Then, if there is an explicit conversion following the implict
// conversion sequence, recurse to handle multiple explicit conversions. // conversion sequence, recurse to handle multiple explicit conversions.
if this.getImplicitlyConverted().hasExplicitConversion() if this.getImplicitlyConverted().hasExplicitConversion()
then result = this.getImplicitlyConverted().getConversion().getExplicitlyConverted() then result = this.getImplicitlyConverted().getConversion().getExplicitlyConverted()

View File

@@ -5,6 +5,7 @@
*/ */
private import cpp private import cpp
import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.IR
/** /**
@@ -24,18 +25,18 @@ abstract class MustFlowConfiguration extends string {
/** /**
* Holds if `source` is a relevant data flow source. * Holds if `source` is a relevant data flow source.
*/ */
abstract predicate isSource(Instruction source); abstract predicate isSource(DataFlow::Node source);
/** /**
* Holds if `sink` is a relevant data flow sink. * Holds if `sink` is a relevant data flow sink.
*/ */
abstract predicate isSink(Operand sink); abstract predicate isSink(DataFlow::Node sink);
/** /**
* Holds if the additional flow step from `node1` to `node2` must be taken * Holds if the additional flow step from `node1` to `node2` must be taken
* into account in the analysis. * into account in the analysis.
*/ */
predicate isAdditionalFlowStep(Operand node1, Instruction node2) { none() } predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
/** Holds if this configuration allows flow from arguments to parameters. */ /** Holds if this configuration allows flow from arguments to parameters. */
predicate allowInterproceduralFlow() { any() } predicate allowInterproceduralFlow() { any() }
@@ -47,17 +48,17 @@ abstract class MustFlowConfiguration extends string {
* included in the module `PathGraph`. * included in the module `PathGraph`.
*/ */
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) { final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
this.isSource(source.getInstruction()) and this.isSource(source.getNode()) and
source.getASuccessor+() = sink source.getASuccessor+() = sink
} }
} }
/** Holds if `node` flows from a source. */ /** Holds if `node` flows from a source. */
pragma[nomagic] pragma[nomagic]
private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) { private predicate flowsFromSource(DataFlow::Node node, MustFlowConfiguration config) {
config.isSource(node) config.isSource(node)
or or
exists(Instruction mid | exists(DataFlow::Node mid |
step(mid, node, config) and step(mid, node, config) and
flowsFromSource(mid, pragma[only_bind_into](config)) flowsFromSource(mid, pragma[only_bind_into](config))
) )
@@ -65,12 +66,12 @@ private predicate flowsFromSource(Instruction node, MustFlowConfiguration config
/** Holds if `node` flows to a sink. */ /** Holds if `node` flows to a sink. */
pragma[nomagic] pragma[nomagic]
private predicate flowsToSink(Instruction node, MustFlowConfiguration config) { private predicate flowsToSink(DataFlow::Node node, MustFlowConfiguration config) {
flowsFromSource(node, pragma[only_bind_into](config)) and flowsFromSource(node, pragma[only_bind_into](config)) and
( (
config.isSink(node.getAUse()) config.isSink(node)
or or
exists(Instruction mid | exists(DataFlow::Node mid |
step(node, mid, config) and step(node, mid, config) and
flowsToSink(mid, pragma[only_bind_into](config)) flowsToSink(mid, pragma[only_bind_into](config))
) )
@@ -197,13 +198,12 @@ private module Cached {
} }
cached cached
predicate step(Instruction nodeFrom, Instruction nodeTo) { predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(Operand mid | instructionToOperandStep(nodeFrom.asInstruction(), nodeTo.asOperand())
instructionToOperandStep(nodeFrom, mid) and
operandToInstructionStep(mid, nodeTo)
)
or or
flowThroughCallable(nodeFrom, nodeTo) flowThroughCallable(nodeFrom.asInstruction(), nodeTo.asInstruction())
or
operandToInstructionStep(nodeFrom.asOperand(), nodeTo.asInstruction())
} }
} }
@@ -213,12 +213,12 @@ private module Cached {
* way around. * way around.
*/ */
pragma[inline] pragma[inline]
private IRFunction getEnclosingCallable(Instruction n) { private Declaration getEnclosingCallable(DataFlow::Node n) {
pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingIRFunction() pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingCallable()
} }
/** Holds if `nodeFrom` flows to `nodeTo`. */ /** Holds if `nodeFrom` flows to `nodeTo`. */
private predicate step(Instruction nodeFrom, Instruction nodeTo, MustFlowConfiguration config) { private predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, MustFlowConfiguration config) {
exists(config) and exists(config) and
Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and
( (
@@ -227,37 +227,37 @@ private predicate step(Instruction nodeFrom, Instruction nodeTo, MustFlowConfigu
getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo) getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo)
) )
or or
config.isAdditionalFlowStep(nodeFrom.getAUse(), nodeTo) config.isAdditionalFlowStep(nodeFrom, nodeTo)
} }
private newtype TLocalPathNode = private newtype TLocalPathNode =
MkLocalPathNode(Instruction n, MustFlowConfiguration config) { MkLocalPathNode(DataFlow::Node n, MustFlowConfiguration config) {
flowsToSink(n, config) and flowsToSink(n, config) and
( (
config.isSource(n) config.isSource(n)
or or
exists(MustFlowPathNode mid | step(mid.getInstruction(), n, config)) exists(MustFlowPathNode mid | step(mid.getNode(), n, config))
) )
} }
/** A `Node` that is in a path from a source to a sink. */ /** A `Node` that is in a path from a source to a sink. */
class MustFlowPathNode extends TLocalPathNode { class MustFlowPathNode extends TLocalPathNode {
Instruction n; DataFlow::Node n;
MustFlowPathNode() { this = MkLocalPathNode(n, _) } MustFlowPathNode() { this = MkLocalPathNode(n, _) }
/** Gets the underlying node. */ /** Gets the underlying node. */
Instruction getInstruction() { result = n } DataFlow::Node getNode() { result = n }
/** Gets a textual representation of this node. */ /** Gets a textual representation of this node. */
string toString() { result = n.getAst().toString() } string toString() { result = n.toString() }
/** Gets the location of this element. */ /** Gets the location of this element. */
Location getLocation() { result = n.getLocation() } Location getLocation() { result = n.getLocation() }
/** Gets a successor node, if any. */ /** Gets a successor node, if any. */
MustFlowPathNode getASuccessor() { MustFlowPathNode getASuccessor() {
step(this.getInstruction(), result.getInstruction(), this.getConfiguration()) step(this.getNode(), result.getNode(), this.getConfiguration())
} }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
@@ -265,7 +265,7 @@ class MustFlowPathNode extends TLocalPathNode {
} }
private class MustFlowPathSink extends MustFlowPathNode { private class MustFlowPathSink extends MustFlowPathNode {
MustFlowPathSink() { this.getConfiguration().isSink(this.getInstruction().getAUse()) } MustFlowPathSink() { this.getConfiguration().isSink(this.getNode()) }
} }
/** /**

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -147,12 +147,6 @@ 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.
*/ */
@@ -164,14 +158,12 @@ 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) { hasFlowPath(source, sink, this) } predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(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.
*/ */
predicate hasFlowTo(Node sink) { predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) }
sink = any(PathNodeSink n | this = n.getConfiguration()).getNodeEx().asNode()
}
/** /**
* 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.
@@ -566,16 +558,13 @@ private predicate expectsContentEx(NodeEx n, Content c) {
pragma[nomagic] pragma[nomagic]
private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) }
pragma[nomagic]
private predicate hasReadStep(Content c, Configuration config) { read(_, c, _, config) }
pragma[nomagic] pragma[nomagic]
private predicate store( private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) { ) {
store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
contentType) and contentType) and
hasReadStep(tc.getContent(), config) and read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config) stepFilter(node1, node2, config)
} }
@@ -609,9 +598,13 @@ private predicate hasSinkCallCtx(Configuration config) {
} }
private module Stage1 implements StageSig { private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit; class Ap = Unit;
private class Cc = boolean; class ApOption = Unit;
class Cc = boolean;
/* Begin: Stage 1 logic. */ /* Begin: Stage 1 logic. */
/** /**
@@ -620,7 +613,7 @@ private module Stage1 implements StageSig {
* The Boolean `cc` records whether the node is reached through an * The Boolean `cc` records whether the node is reached through an
* argument in a call. * argument in a call.
*/ */
private predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
sourceNode(node, _, config) and sourceNode(node, _, config) and
if hasSourceCallCtx(config) then cc = true else cc = false if hasSourceCallCtx(config) then cc = true else cc = false
or or
@@ -760,7 +753,7 @@ private module Stage1 implements StageSig {
* the enclosing callable in order to reach a sink. * the enclosing callable in order to reach a sink.
*/ */
pragma[nomagic] pragma[nomagic]
private predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { predicate revFlow(NodeEx node, boolean toReturn, Configuration config) {
revFlow0(node, toReturn, config) and revFlow0(node, toReturn, config) and
fwdFlow(node, config) fwdFlow(node, config)
} }
@@ -844,13 +837,13 @@ private module Stage1 implements StageSig {
* by `revFlow`. * by `revFlow`.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlowIsReadAndStored(Content c, Configuration conf) { predicate revFlowIsReadAndStored(Content c, Configuration conf) {
revFlowConsCand(c, conf) and revFlowConsCand(c, conf) and
revFlowStore(c, _, _, conf) revFlowStore(c, _, _, conf)
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableReturnPosOutNodeCandFwd1( predicate viableReturnPosOutNodeCandFwd1(
DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config
) { ) {
fwdFlowReturnPosition(pos, _, config) and fwdFlowReturnPosition(pos, _, config) and
@@ -866,7 +859,7 @@ private module Stage1 implements StageSig {
} }
pragma[nomagic] pragma[nomagic]
additional predicate viableParamArgNodeCandFwd1( predicate viableParamArgNodeCandFwd1(
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
) { ) {
viableParamArgEx(call, p, arg) and viableParamArgEx(call, p, arg) and
@@ -913,7 +906,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate revFlowState(FlowState state, Configuration config) { predicate revFlowState(FlowState state, Configuration config) {
exists(NodeEx node | exists(NodeEx node |
sinkNode(node, state, config) and sinkNode(node, state, config) and
revFlow(node, _, pragma[only_bind_into](config)) and revFlow(node, _, pragma[only_bind_into](config)) and
@@ -1005,7 +998,7 @@ private module Stage1 implements StageSig {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -1266,7 +1259,7 @@ private module MkStage<StageSig PrevStage> {
* argument. * argument.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate fwdFlow( predicate fwdFlow(
NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
) { ) {
fwdFlow0(node, state, cc, argAp, ap, config) and fwdFlow0(node, state, cc, argAp, ap, config) and
@@ -1490,7 +1483,7 @@ private module MkStage<StageSig PrevStage> {
* the access path of the returned value. * the access path of the returned value.
*/ */
pragma[nomagic] pragma[nomagic]
additional predicate revFlow( predicate revFlow(
NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
) { ) {
revFlow0(node, state, toReturn, returnAp, ap, config) and revFlow0(node, state, toReturn, returnAp, ap, config) and
@@ -1668,7 +1661,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate revFlow(NodeEx node, FlowState state, Configuration config) { predicate revFlow(NodeEx node, FlowState state, Configuration config) {
revFlow(node, state, _, _, _, config) revFlow(node, state, _, _, _, config)
} }
@@ -1681,13 +1674,11 @@ private module MkStage<StageSig PrevStage> {
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, Configuration config) { predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
revFlow(node, _, _, _, _, config)
}
// use an alias as a workaround for bad functionality-induced joins // use an alias as a workaround for bad functionality-induced joins
pragma[nomagic] pragma[nomagic]
additional predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
revFlow(node, state, ap, config) revFlow(node, state, ap, config)
} }
@@ -1708,7 +1699,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate consCand(TypedContent tc, Ap ap, Configuration config) { predicate consCand(TypedContent tc, Ap ap, Configuration config) {
revConsCand(tc, ap, config) and revConsCand(tc, ap, config) and
validAp(ap, config) validAp(ap, config)
} }
@@ -1750,7 +1741,7 @@ private module MkStage<StageSig PrevStage> {
) )
} }
additional predicate stats( predicate stats(
boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config
) { ) {
fwd = true and fwd = true and
@@ -2635,7 +2626,6 @@ 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
@@ -2654,7 +2644,6 @@ 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
@@ -2689,7 +2678,6 @@ 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
) { ) {
@@ -2718,18 +2706,6 @@ 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()
)
} }
/** /**
@@ -2799,7 +2775,6 @@ 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
@@ -2808,7 +2783,6 @@ 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) {
@@ -2897,16 +2871,54 @@ 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. */
abstract FlowState getState(); FlowState getState() { none() }
/** Gets the associated configuration. */ /** Gets the associated configuration. */
abstract Configuration getConfiguration(); Configuration getConfiguration() { none() }
/** 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. */
abstract predicate isSource(); predicate isSource() { none() }
}
abstract private class PathNodeImpl extends PathNode {
abstract PathNodeImpl getASuccessorImpl(); abstract PathNodeImpl getASuccessorImpl();
private PathNodeImpl getASuccessorIfHidden() { private PathNodeImpl getASuccessorIfHidden() {
@@ -2914,15 +2926,10 @@ abstract private class PathNodeImpl extends TPathNode {
result = this.getASuccessorImpl() result = this.getASuccessorImpl()
} }
pragma[nomagic]
private PathNodeImpl getANonHiddenSuccessor0() {
result = this.getASuccessorIfHidden*() and
not result.isHidden()
}
final PathNodeImpl getANonHiddenSuccessor() { final PathNodeImpl getANonHiddenSuccessor() {
result = this.getASuccessorImpl().getANonHiddenSuccessor0() and result = this.getASuccessorImpl().getASuccessorIfHidden*() and
not this.isHidden() not this.isHidden() and
not result.isHidden()
} }
abstract NodeEx getNodeEx(); abstract NodeEx getNodeEx();
@@ -2938,22 +2945,6 @@ abstract private class PathNodeImpl extends TPathNode {
) )
} }
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
@@ -2968,23 +2959,13 @@ abstract private class PathNodeImpl extends TPathNode {
result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" result = " <" + this.(PathNodeMid).getCallContext().toString() + ">"
} }
/** Gets a textual representation of this element. */ override string toString() { result = this.getNodeEx().toString() + this.ppAp() }
string toString() { result = this.getNodeEx().toString() + this.ppAp() }
/** override string toStringWithContext() {
* Gets a textual representation of this element, including a textual result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx()
* 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)
@@ -2993,71 +2974,18 @@ abstract private class PathNodeImpl extends TPathNode {
/** 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 n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor())
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(PathNodeImpl n) { directReach(n) or Subpaths::retReach(n) } private predicate reach(PathNode 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, PathNodeImpl n2) { private predicate pathSucc(PathNodeImpl n1, PathNode n2) {
n1.getANonHiddenSuccessor() = n2 and directReach(n2) n1.getANonHiddenSuccessor() = n2 and directReach(n2)
} }
private predicate pathSuccPlus(PathNodeImpl n1, PathNodeImpl n2) = fastTC(pathSucc/2)(n1, n2) private predicate pathSuccPlus(PathNode n1, PathNode 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.
@@ -3068,7 +2996,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) {
key = "semmle.label" and val = n.toString() reach(n) and key = "semmle.label" and val = n.toString()
} }
/** /**
@@ -3077,7 +3005,11 @@ 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) Subpaths::subpaths(arg, par, ret, out) and
reach(arg) and
reach(par) and
reach(ret) and
reach(out)
} }
} }
@@ -3178,66 +3110,9 @@ private class PathNodeSink extends PathNodeImpl, TPathNodeSink {
override Configuration getConfiguration() { result = config } override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result = TPathNodeSinkGroup(this.getSinkGroup(), config)
}
override predicate isSource() { sourceNode(node, state, config) }
string getSinkGroup() { config.sinkGrouping(node.asNode(), result) }
}
private class PathNodeSourceGroup extends PathNodeImpl, TPathNodeSourceGroup {
string sourceGroup;
Configuration config;
PathNodeSourceGroup() { this = TPathNodeSourceGroup(sourceGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() {
result.getSourceGroup() = sourceGroup and
result.getConfiguration() = config
}
override predicate isSource() { none() }
override string toString() { result = sourceGroup }
override predicate hasLocationInfo(
string filepath, int startline, int startcolumn, int endline, int endcolumn
) {
filepath = "" and startline = 0 and startcolumn = 0 and endline = 0 and endcolumn = 0
}
}
private class PathNodeSinkGroup extends PathNodeImpl, TPathNodeSinkGroup {
string sinkGroup;
Configuration config;
PathNodeSinkGroup() { this = TPathNodeSinkGroup(sinkGroup, config) }
override NodeEx getNodeEx() { none() }
override FlowState getState() { none() }
override Configuration getConfiguration() { result = config }
override PathNodeImpl getASuccessorImpl() { none() } override PathNodeImpl getASuccessorImpl() { none() }
override predicate isSource() { none() } override predicate isSource() { sourceNode(node, state, config) }
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(
@@ -3259,7 +3134,6 @@ 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
@@ -3517,7 +3391,7 @@ private module Subpaths {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate subpaths02( private predicate subpaths02(
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, PathNode 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
@@ -3525,14 +3399,14 @@ private module Subpaths {
} }
pragma[nomagic] pragma[nomagic]
private Configuration getPathNodeConf(PathNodeImpl n) { result = n.getConfiguration() } private Configuration getPathNodeConf(PathNode 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(
PathNodeImpl arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout PathNode 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
@@ -3562,7 +3436,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, PathNodeImpl out) { predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode 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
@@ -3578,7 +3452,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(PathNodeImpl out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out))
or or
exists(PathNodeImpl mid | exists(PathNodeImpl mid |
retReach(mid) and retReach(mid) and
@@ -3594,22 +3468,12 @@ 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(
PathNodeImpl flowsource, PathNodeSink flowsink, Node source, Node sink, PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration
Configuration configuration
) { ) {
flowsource.isSource() and flowsource.isSource() and
flowsource.getConfiguration() = configuration and flowsource.getConfiguration() = configuration and
flowsource.getNodeEx().asNode() = source and flowsource.(PathNodeImpl).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
} }
@@ -3632,14 +3496,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(PathNodeImpl pn) tuples = count(PathNode 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) tuples = count(PathNode pn | reach(pn))
} }
/** /**

View File

@@ -136,18 +136,6 @@ 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) {

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