Compare commits

..

4 Commits

Author SHA1 Message Date
Nick Rolfe
4aa4364200 Merge remote-tracking branch 'origin/dependabot/cargo/ruby/extractor/clap-4.0' into nickrolfe/clap4 2022-09-29 09:31:15 +01:00
Nick Rolfe
88fb187923 Merge remote-tracking branch 'origin/dependabot/cargo/ruby/generator/clap-4.0' into nickrolfe/clap4 2022-09-29 09:30:53 +01:00
dependabot[bot]
753042d883 Update clap requirement from 3.0 to 4.0 in /ruby/extractor
Updates the requirements on [clap](https://github.com/clap-rs/clap) to permit the latest version.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v3.0.0...v4.0.2)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-29 03:19:36 +00:00
dependabot[bot]
5299cd9c69 Update clap requirement from 3.0 to 4.0 in /ruby/generator
Updates the requirements on [clap](https://github.com/clap-rs/clap) to permit the latest version.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v3.0.0...v4.0.2)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-29 03:19:22 +00:00
2919 changed files with 72869 additions and 69534 deletions

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,56 +0,0 @@
name: ATM Check Queries Run
env:
DB_PATH: test_db
ATM_MODEL_PACK: javascript/ql/experimental/adaptivethreatmodeling/src
QUERY_SUITE: codeql-suites/javascript-atm-code-scanning.qls
on:
pull_request:
paths:
- ".github/workflows/atm-check-queries-run.yml"
- "javascript/ql/experimental/adaptivethreatmodeling/**"
workflow_dispatch:
jobs:
run-atm-queries:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install CodeQL CLI
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh extensions install github/gh-codeql
gh codeql download
- name: Install ATM model pack
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -exu
# Install ATM model pack
gh codeql pack install ${ATM_MODEL_PACK}
# Retrieve model checksum
model_checksum=$(gh codeql resolve extensions ${ATM_MODEL_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
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh codeql database create ${RUNNER_TEMP}/${DB_PATH} --source-root config/atm/ --language javascript
- name: Run ATM query suite
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh codeql database run-queries -vv -- ${RUNNER_TEMP}/${DB_PATH} ${ATM_MODEL_PACK}/${QUERY_SUITE}

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,57 +0,0 @@
name: "Compile all queries using the latest stable CodeQL CLI"
on:
push:
branches: [main] # makes sure the cache gets populated
pull_request:
branches:
- main
- "rc/*"
jobs:
compile-queries:
runs-on: ubuntu-latest-xl
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
# calculate the merge-base with main, in a way that works both on PRs and pushes to main.
- name: Calculate merge-base
if: ${{ github.event_name == 'pull_request' }}
env:
BASE_BRANCH: ${{ github.base_ref }}
run: |
MERGE_BASE=$(git merge-base --fork-point origin/$BASE_BRANCH)
echo "merge-base=$MERGE_BASE" >> $GITHUB_ENV
- name: Calculate merge-base - branch
if: ${{ github.event_name != 'pull_request' }}
# using github.sha instead, since we're directly on a branch, and not in a PR
run: |
MERGE_BASE=${{ github.sha }}
echo "merge-base=$MERGE_BASE" >> $GITHUB_ENV
- name: Cache CodeQL query compilation
uses: actions/cache@v3
with:
path: '*/ql/src/.cache'
# current GH HEAD first, merge-base second, generic third
key: codeql-stable-compile-${{ github.sha }}
restore-keys: |
codeql-stable-compile-${{ env.merge-base }}
codeql-stable-compile-
- name: Setup CodeQL
uses: ./.github/actions/fetch-codeql
with:
channel: 'release'
- name: check formatting
run: codeql query format */ql/{src,lib,test}/**/*.{qll,ql} --check-only
- name: compile queries - check-only
# run with --check-only if running in a PR (github.sha != main)
if : ${{ github.event_name == 'pull_request' }}
shell: bash
run: codeql query compile -j0 */ql/src --keep-going --warnings=error --check-only
- name: compile queries - full
# do full compile if running on main - this populates the cache
if : ${{ github.event_name != 'pull_request' }}
shell: bash
run: codeql query compile -j0 */ql/src --keep-going --warnings=error

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

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

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/**'
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
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
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: swift/generated-cpp-files/**
qlformat:
runs-on: ubuntu-latest
needs: changes
if: ${{ needs.changes.outputs.ql == 'true' }}
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- name: Check QL formatting
run: find swift/ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only

View File

@@ -20,9 +20,9 @@
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go /java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
# CodeQL tools and associated docs # CodeQL tools and associated docs
/docs/codeql/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

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"

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": [

View File

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

View File

@@ -1,17 +1,3 @@
## 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 ## 0.4.0
### Deprecated APIs ### Deprecated APIs

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,2 +1,2 @@
--- ---
lastReleaseVersion: 0.4.3 lastReleaseVersion: 0.4.0

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
@@ -90,61 +89,6 @@ module ProductFlow {
*/ */
predicate isBarrierOut2(DataFlow::Node node) { none() } 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 +103,54 @@ 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) { override predicate isBarrier(DataFlow::Node node, string state) {
exists(Configuration conf | conf.isBarrier1(node, state)) exists(Configuration conf | conf.isBarrier1(node, state))
} }
override predicate isBarrierOut(DataFlow::Node node) { override predicate isBarrierOut(DataFlow::Node node) {
exists(Configuration conf | conf.isBarrierOut1(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) { override predicate isBarrier(DataFlow::Node node, string state) {
exists(Configuration conf | conf.isBarrier2(node, state)) exists(Configuration conf | conf.isBarrier2(node, state))
} }
override predicate isBarrierOut(DataFlow::Node node) { override predicate isBarrierOut(DataFlow::Node node) {
exists(Configuration conf | conf.isBarrierOut2(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 +213,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -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,7 +411,7 @@ 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() }
@@ -474,6 +454,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 +478,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 +504,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 +536,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 +594,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 +615,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 +624,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 +645,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 +675,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 +859,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 +922,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 +1075,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

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

@@ -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.3 version: 0.4.1-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

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

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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

@@ -163,9 +163,7 @@ abstract class Configuration extends string {
/** /**
* 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.
@@ -838,13 +836,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
@@ -860,7 +858,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
@@ -907,7 +905,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
@@ -999,7 +997,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
@@ -1260,7 +1258,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
@@ -1484,7 +1482,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
@@ -1662,7 +1660,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)
} }
@@ -1675,13 +1673,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)
} }
@@ -1702,7 +1698,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)
} }
@@ -1744,7 +1740,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
@@ -2874,16 +2870,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() {
@@ -2891,15 +2925,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();
@@ -2929,23 +2958,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)
@@ -2958,59 +2977,14 @@ private predicate directReach(PathNodeImpl n) {
} }
/** 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() }
}
/** /**
* 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.
@@ -3021,7 +2995,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()
} }
/** /**
@@ -3030,7 +3004,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)
} }
} }
@@ -3412,7 +3390,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
@@ -3420,14 +3398,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
@@ -3457,7 +3435,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
@@ -3473,7 +3451,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
@@ -3490,12 +3468,11 @@ private module Subpaths {
* sinks. * sinks.
*/ */
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
} }
@@ -3518,14 +3495,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

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

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

@@ -742,7 +742,7 @@ class NoOpInstruction extends Instruction {
* The `ReturnInstruction` for a function will have a control-flow successor edge to a block * The `ReturnInstruction` for a function will have a control-flow successor edge to a block
* containing the `ExitFunction` instruction for that function. * containing the `ExitFunction` instruction for that function.
* *
* There are two different return instructions: `ReturnValueInstruction`, for returning a value from * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from
* a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a
* `void`-returning function. * `void`-returning function.
*/ */
@@ -1331,7 +1331,7 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction {
* *
* If the operand holds a null address, the result is a null address. * If the operand holds a null address, the result is a null address.
* *
* This instruction is used to represent `dynamic_cast<void*>` in C++, which returns the pointer to * This instruction is used to represent `dyanmic_cast<void*>` in C++, which returns the pointer to
* the most-derived object. * the most-derived object.
*/ */
class CompleteObjectAddressInstruction extends UnaryInstruction { class CompleteObjectAddressInstruction extends UnaryInstruction {

View File

@@ -64,7 +64,7 @@ private module Cached {
or or
instr = reusedPhiInstruction(_) and instr = reusedPhiInstruction(_) and
// Check that the phi instruction is *not* degenerate, but we can't use // Check that the phi instruction is *not* degenerate, but we can't use
// getDegeneratePhiOperand in the first stage with phi instructions // getDegeneratePhiOperand in the first stage with phi instyructions
not exists( not exists(
unique(OldIR::PhiInputOperand operand | unique(OldIR::PhiInputOperand operand |
operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and
@@ -718,7 +718,7 @@ module DefUse {
} }
/** /**
* Gets the rank index of a hypothetical use one instruction past the end of * Gets the rank index of a hyphothetical use one instruction past the end of
* the block. This index can be used to determine if a definition reaches the * the block. This index can be used to determine if a definition reaches the
* end of the block, even if the definition is the last instruction in the * end of the block, even if the definition is the last instruction in the
* block. * block.

View File

@@ -172,7 +172,7 @@ deprecated module UnaliasedSSAOperands = UnaliasedSsaOperands;
/** /**
* Provides wrappers for the constructors of each branch of `TOperand` that is used by the * Provides wrappers for the constructors of each branch of `TOperand` that is used by the
* aliased SSA stage. * asliased SSA stage.
* These wrappers are not parameterized because it is not possible to invoke an IPA constructor via * These wrappers are not parameterized because it is not possible to invoke an IPA constructor via
* a class alias. * a class alias.
*/ */

View File

@@ -742,7 +742,7 @@ class NoOpInstruction extends Instruction {
* The `ReturnInstruction` for a function will have a control-flow successor edge to a block * The `ReturnInstruction` for a function will have a control-flow successor edge to a block
* containing the `ExitFunction` instruction for that function. * containing the `ExitFunction` instruction for that function.
* *
* There are two different return instructions: `ReturnValueInstruction`, for returning a value from * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from
* a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a
* `void`-returning function. * `void`-returning function.
*/ */
@@ -1331,7 +1331,7 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction {
* *
* If the operand holds a null address, the result is a null address. * If the operand holds a null address, the result is a null address.
* *
* This instruction is used to represent `dynamic_cast<void*>` in C++, which returns the pointer to * This instruction is used to represent `dyanmic_cast<void*>` in C++, which returns the pointer to
* the most-derived object. * the most-derived object.
*/ */
class CompleteObjectAddressInstruction extends UnaryInstruction { class CompleteObjectAddressInstruction extends UnaryInstruction {

View File

@@ -542,7 +542,7 @@ class TranslatedArgumentExprSideEffect extends TranslatedArgumentSideEffect,
* The IR translation of an argument side effect for `*this` on a call, where there is no `Expr` * The IR translation of an argument side effect for `*this` on a call, where there is no `Expr`
* object that represents the `this` argument. * object that represents the `this` argument.
* *
* The applies only to constructor calls, as the AST has exploit qualifier `Expr`s for all other * The applies only to constructor calls, as the AST has explioit qualifier `Expr`s for all other
* calls to non-static member functions. * calls to non-static member functions.
*/ */
class TranslatedStructorQualifierSideEffect extends TranslatedArgumentSideEffect, class TranslatedStructorQualifierSideEffect extends TranslatedArgumentSideEffect,

View File

@@ -2177,7 +2177,7 @@ abstract class TranslatedConditionalExpr extends TranslatedNonConstantExpr {
/** /**
* The IR translation of the ternary conditional operator (`a ? b : c`). * The IR translation of the ternary conditional operator (`a ? b : c`).
* For this version, we expand the condition as a `TranslatedCondition`, rather than a * For this version, we expand the condition as a `TranslatedCondition`, rather than a
* `TranslatedExpr`, to simplify the control flow in the presence of short-circuit logical operators. * `TranslatedExpr`, to simplify the control flow in the presence of short-ciruit logical operators.
*/ */
class TranslatedTernaryConditionalExpr extends TranslatedConditionalExpr, ConditionContext { class TranslatedTernaryConditionalExpr extends TranslatedConditionalExpr, ConditionContext {
TranslatedTernaryConditionalExpr() { not expr.isTwoOperand() } TranslatedTernaryConditionalExpr() { not expr.isTwoOperand() }

View File

@@ -742,7 +742,7 @@ class NoOpInstruction extends Instruction {
* The `ReturnInstruction` for a function will have a control-flow successor edge to a block * The `ReturnInstruction` for a function will have a control-flow successor edge to a block
* containing the `ExitFunction` instruction for that function. * containing the `ExitFunction` instruction for that function.
* *
* There are two different return instructions: `ReturnValueInstruction`, for returning a value from * There are two differet return instructions: `ReturnValueInstruction`, for returning a value from
* a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a * a non-`void`-returning function, and `ReturnVoidInstruction`, for returning from a
* `void`-returning function. * `void`-returning function.
*/ */
@@ -1331,7 +1331,7 @@ class CheckedConvertOrThrowInstruction extends UnaryInstruction {
* *
* If the operand holds a null address, the result is a null address. * If the operand holds a null address, the result is a null address.
* *
* This instruction is used to represent `dynamic_cast<void*>` in C++, which returns the pointer to * This instruction is used to represent `dyanmic_cast<void*>` in C++, which returns the pointer to
* the most-derived object. * the most-derived object.
*/ */
class CompleteObjectAddressInstruction extends UnaryInstruction { class CompleteObjectAddressInstruction extends UnaryInstruction {

View File

@@ -64,7 +64,7 @@ private module Cached {
or or
instr = reusedPhiInstruction(_) and instr = reusedPhiInstruction(_) and
// Check that the phi instruction is *not* degenerate, but we can't use // Check that the phi instruction is *not* degenerate, but we can't use
// getDegeneratePhiOperand in the first stage with phi instructions // getDegeneratePhiOperand in the first stage with phi instyructions
not exists( not exists(
unique(OldIR::PhiInputOperand operand | unique(OldIR::PhiInputOperand operand |
operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and operand = instr.(OldIR::PhiInstruction).getAnInputOperand() and
@@ -718,7 +718,7 @@ module DefUse {
} }
/** /**
* Gets the rank index of a hypothetical use one instruction past the end of * Gets the rank index of a hyphothetical use one instruction past the end of
* the block. This index can be used to determine if a definition reaches the * the block. This index can be used to determine if a definition reaches the
* end of the block, even if the definition is the last instruction in the * end of the block, even if the definition is the last instruction in the
* block. * block.

View File

@@ -12,7 +12,7 @@ private Type getDecayedType(Type type) {
} }
/** /**
* Holds if the specified variable is a structured binding with a non-reference * Holds if the sepcified variable is a structured binding with a non-reference
* type. * type.
*/ */
predicate isNonReferenceStructuredBinding(Variable v) { predicate isNonReferenceStructuredBinding(Variable v) {

View File

@@ -209,7 +209,7 @@ private predicate aClassFile(Class c, File file) { c.getDefinitionLocation().get
pragma[noopt] pragma[noopt]
private predicate dependsOnFileSimple(MetricFile source, MetricFile dest) { private predicate dependsOnFileSimple(MetricFile source, MetricFile dest) {
// class derives from another class // class derives from classs
exists(Class fromClass, Class toClass | exists(Class fromClass, Class toClass |
aClassFile(fromClass, source) and aClassFile(fromClass, source) and
fromClass.derivesFrom(toClass) and fromClass.derivesFrom(toClass) and

View File

@@ -205,149 +205,57 @@ private predicate deconstructSizeExpr(Expr sizeExpr, Expr lengthExpr, int sizeof
sizeof = 1 sizeof = 1
} }
/** A `Function` that is a call target of an allocation. */
private signature class CallAllocationExprTarget extends Function;
/** /**
* This module abstracts over the type of allocation call-targets and provides a * An allocation expression that is a function call, such as call to `malloc`.
* class `CallAllocationExprImpl` which contains the implementation of the various
* predicates required by the `Allocation` class.
*
* This module is then instantiated for two types of allocation call-targets:
* - `AllocationFunction`: Functions that we've explicitly modeled as functions that
* perform allocations (i.e., `malloc`).
* - `HeuristicAllocationFunction`: Functions that we deduce as behaving like an allocation
* function using various heuristics.
*/ */
private module CallAllocationExprBase<CallAllocationExprTarget Target> { private class CallAllocationExpr extends AllocationExpr, FunctionCall {
/** A module that contains the collection of member-predicates required on `Target`. */ AllocationFunction target;
signature module Param {
/**
* Gets the index of the input pointer argument to be reallocated, if
* this is a `realloc` function.
*/
int getReallocPtrArg(Target target);
/** CallAllocationExpr() {
* Gets the index of the argument for the allocation size, if any. The actual target = this.getTarget() and
* allocation size is the value of this argument multiplied by the result of // realloc(ptr, 0) only frees the pointer
* `getSizeMult()`, in bytes. not (
*/ exists(target.getReallocPtrArg()) and
int getSizeArg(Target target); this.getArgument(target.getSizeArg()).getValue().toInt() = 0
) and
/** // these are modeled directly (and more accurately), avoid duplication
* Gets the index of an argument that multiplies the allocation size given not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
* by `getSizeArg`, if any.
*/
int getSizeMult(Target target);
/**
* Holds if this allocation requires a
* corresponding deallocation of some sort (most do, but `alloca` for example
* does not). If it is unclear, we default to no (for example a placement `new`
* allocation may or may not require a corresponding `delete`).
*/
predicate requiresDealloc(Target target);
} }
/** override Expr getSizeExpr() {
* A module that abstracts over a collection of predicates in exists(Expr sizeExpr | sizeExpr = this.getArgument(target.getSizeArg()) |
* the `Param` module). This should really be member-predicates if exists(target.getSizeMult())
* on `CallAllocationExprTarget`, but we cannot yet write this in QL. then result = sizeExpr
*/ else
module With<Param P> { exists(Expr lengthExpr |
private import P deconstructSizeExpr(sizeExpr, lengthExpr, _) and
result = lengthExpr
/**
* An allocation expression that is a function call, such as call to `malloc`.
*/
class CallAllocationExprImpl instanceof FunctionCall {
Target target;
CallAllocationExprImpl() {
target = this.getTarget() and
// realloc(ptr, 0) only frees the pointer
not (
exists(getReallocPtrArg(target)) and
this.getArgument(getSizeArg(target)).getValue().toInt() = 0
) and
// these are modeled directly (and more accurately), avoid duplication
not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
}
string toString() { result = super.toString() }
Expr getSizeExprImpl() {
exists(Expr sizeExpr | sizeExpr = super.getArgument(getSizeArg(target)) |
if exists(getSizeMult(target))
then result = sizeExpr
else
exists(Expr lengthExpr |
deconstructSizeExpr(sizeExpr, lengthExpr, _) and
result = lengthExpr
)
) )
} )
int getSizeMultImpl() {
// malloc with multiplier argument that is a constant
result = super.getArgument(getSizeMult(target)).getValue().toInt()
or
// malloc with no multiplier argument
not exists(getSizeMult(target)) and
deconstructSizeExpr(super.getArgument(getSizeArg(target)), _, result)
}
int getSizeBytesImpl() {
result = this.getSizeExprImpl().getValue().toInt() * this.getSizeMultImpl()
}
Expr getReallocPtrImpl() { result = super.getArgument(getReallocPtrArg(target)) }
Type getAllocatedElementTypeImpl() {
result =
super.getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() and
not result instanceof VoidType
}
predicate requiresDeallocImpl() { requiresDealloc(target) }
}
}
}
private module CallAllocationExpr {
private module Param implements CallAllocationExprBase<AllocationFunction>::Param {
int getReallocPtrArg(AllocationFunction f) { result = f.getReallocPtrArg() }
int getSizeArg(AllocationFunction f) { result = f.getSizeArg() }
int getSizeMult(AllocationFunction f) { result = f.getSizeMult() }
predicate requiresDealloc(AllocationFunction f) { f.requiresDealloc() }
} }
/** override int getSizeMult() {
* A class that provides the implementation of `AllocationExpr` for an allocation // malloc with multiplier argument that is a constant
* that calls an `AllocationFunction`. result = this.getArgument(target.getSizeMult()).getValue().toInt()
*/ or
private class Base = // malloc with no multiplier argument
CallAllocationExprBase<AllocationFunction>::With<Param>::CallAllocationExprImpl; not exists(target.getSizeMult()) and
deconstructSizeExpr(this.getArgument(target.getSizeArg()), _, result)
class CallAllocationExpr extends AllocationExpr, Base {
override Expr getSizeExpr() { result = super.getSizeExprImpl() }
override int getSizeMult() { result = super.getSizeMultImpl() }
override Type getAllocatedElementType() { result = super.getAllocatedElementTypeImpl() }
override predicate requiresDealloc() { super.requiresDeallocImpl() }
override int getSizeBytes() { result = super.getSizeBytesImpl() }
override Expr getReallocPtr() { result = super.getReallocPtrImpl() }
override string toString() { result = AllocationExpr.super.toString() }
} }
override int getSizeBytes() {
result = this.getSizeExpr().getValue().toInt() * this.getSizeMult()
}
override Expr getReallocPtr() { result = this.getArgument(target.getReallocPtrArg()) }
override Type getAllocatedElementType() {
result =
this.getFullyConverted().getType().stripTopLevelSpecifiers().(PointerType).getBaseType() and
not result instanceof VoidType
}
override predicate requiresDealloc() { target.requiresDealloc() }
} }
/** /**
@@ -386,99 +294,3 @@ private class NewArrayAllocationExpr extends AllocationExpr, NewArrayExpr {
override predicate requiresDealloc() { not exists(this.getPlacementPointer()) } override predicate requiresDealloc() { not exists(this.getPlacementPointer()) }
} }
private module HeuristicAllocation {
/** A class that maps an `AllocationExpr` to an `HeuristicAllocationExpr`. */
private class HeuristicAllocationModeled extends HeuristicAllocationExpr instanceof AllocationExpr {
override Expr getSizeExpr() { result = AllocationExpr.super.getSizeExpr() }
override int getSizeMult() { result = AllocationExpr.super.getSizeMult() }
override int getSizeBytes() { result = AllocationExpr.super.getSizeBytes() }
override Expr getReallocPtr() { result = AllocationExpr.super.getReallocPtr() }
override Type getAllocatedElementType() {
result = AllocationExpr.super.getAllocatedElementType()
}
override predicate requiresDealloc() { AllocationExpr.super.requiresDealloc() }
}
/** A class that maps an `AllocationFunction` to an `HeuristicAllocationFunction`. */
private class HeuristicAllocationFunctionModeled extends HeuristicAllocationFunction instanceof AllocationFunction {
override int getSizeArg() { result = AllocationFunction.super.getSizeArg() }
override int getSizeMult() { result = AllocationFunction.super.getSizeMult() }
override int getReallocPtrArg() { result = AllocationFunction.super.getReallocPtrArg() }
override predicate requiresDealloc() { AllocationFunction.super.requiresDealloc() }
}
private int getAnUnsignedParameter(Function f) {
f.getParameter(result).getUnspecifiedType().(IntegralType).isUnsigned()
}
private int getAPointerParameter(Function f) {
f.getParameter(result).getUnspecifiedType() instanceof PointerType
}
/**
* A class that uses heuristics to find additional allocation functions. The required are as follows:
* 1. The word `alloc` must appear in the function name
* 2. The function must return a pointer type
* 3. There must be a unique parameter of unsigned integral type.
*/
private class HeuristicAllocationFunctionByName extends HeuristicAllocationFunction instanceof Function {
int sizeArg;
HeuristicAllocationFunctionByName() {
Function.super.getName().matches("%alloc%") and
Function.super.getUnspecifiedType() instanceof PointerType and
sizeArg = unique( | | getAnUnsignedParameter(this))
}
override int getSizeArg() { result = sizeArg }
override int getReallocPtrArg() {
Function.super.getName().matches("%realloc%") and
result = unique( | | getAPointerParameter(this))
}
override predicate requiresDealloc() { none() }
}
private module Param implements CallAllocationExprBase<HeuristicAllocationFunction>::Param {
int getReallocPtrArg(HeuristicAllocationFunction f) { result = f.getReallocPtrArg() }
int getSizeArg(HeuristicAllocationFunction f) { result = f.getSizeArg() }
int getSizeMult(HeuristicAllocationFunction f) { result = f.getSizeMult() }
predicate requiresDealloc(HeuristicAllocationFunction f) { f.requiresDealloc() }
}
/**
* A class that provides the implementation of `AllocationExpr` for an allocation
* that calls an `HeuristicAllocationFunction`.
*/
private class Base =
CallAllocationExprBase<HeuristicAllocationFunction>::With<Param>::CallAllocationExprImpl;
private class CallAllocationExpr extends HeuristicAllocationExpr, Base {
override Expr getSizeExpr() { result = super.getSizeExprImpl() }
override int getSizeMult() { result = super.getSizeMultImpl() }
override Type getAllocatedElementType() { result = super.getAllocatedElementTypeImpl() }
override predicate requiresDealloc() { super.requiresDeallocImpl() }
override int getSizeBytes() { result = super.getSizeBytesImpl() }
override Expr getReallocPtr() { result = super.getReallocPtrImpl() }
override string toString() { result = HeuristicAllocationExpr.super.toString() }
}
}

View File

@@ -113,84 +113,3 @@ class OperatorNewAllocationFunction extends AllocationFunction {
result = 1 result = 1
} }
} }
/**
* An expression that _might_ allocate memory.
*
* Unlike `AllocationExpr`, this class uses heuristics (such as a call target's
* name and parameters) to include additional expressions.
*/
abstract class HeuristicAllocationExpr extends Expr {
/**
* Gets an expression for the allocation size, if any. The actual allocation
* size is the value of this expression multiplied by the result of
* `getSizeMult()`, in bytes.
*/
Expr getSizeExpr() { none() }
/**
* Gets a constant multiplier for the allocation size given by `getSizeExpr`,
* in bytes.
*/
int getSizeMult() { none() }
/**
* Gets the size of this allocation in bytes, if it is a fixed size and that
* size can be determined.
*/
int getSizeBytes() { none() }
/**
* Gets the expression for the input pointer argument to be reallocated, if
* this is a `realloc` function.
*/
Expr getReallocPtr() { none() }
/**
* Gets the type of the elements that are allocated, if it can be determined.
*/
Type getAllocatedElementType() { none() }
/**
* Whether or not this allocation requires a corresponding deallocation of
* some sort (most do, but `alloca` for example does not). If it is unclear,
* we default to no (for example a placement `new` allocation may or may not
* require a corresponding `delete`).
*/
predicate requiresDealloc() { any() }
}
/**
* An function that _might_ allocate memory.
*
* Unlike `AllocationFunction`, this class uses heuristics (such as the function's
* name and its parameters) to include additional functions.
*/
abstract class HeuristicAllocationFunction extends Function {
/**
* Gets the index of the argument for the allocation size, if any. The actual
* allocation size is the value of this argument multiplied by the result of
* `getSizeMult()`, in bytes.
*/
int getSizeArg() { none() }
/**
* Gets the index of an argument that multiplies the allocation size given by
* `getSizeArg`, if any.
*/
int getSizeMult() { none() }
/**
* Gets the index of the input pointer argument to be reallocated, if this
* is a `realloc` function.
*/
int getReallocPtrArg() { none() }
/**
* Whether or not this allocation requires a corresponding deallocation of
* some sort (most do, but `alloca` for example does not). If it is unclear,
* we default to no (for example a placement `new` allocation may or may not
* require a corresponding `delete`).
*/
predicate requiresDealloc() { any() }
}

View File

@@ -173,7 +173,7 @@ predicate eqOpWithSwapAndNegate(EqualityOperation cmp, Expr a, Expr b, boolean i
/** /**
* Holds if `cmp` is an unconverted conversion of `a` to a Boolean that * Holds if `cmp` is an unconverted conversion of `a` to a Boolean that
* evaluates to `isEQ` iff `a` is 0. * evalutes to `isEQ` iff `a` is 0.
* *
* Note that `a` can be `cmp` itself or a conversion thereof. * Note that `a` can be `cmp` itself or a conversion thereof.
*/ */

View File

@@ -51,14 +51,14 @@ string getInsecureAlgorithmRegex() {
/** /**
* Holds if `name` looks like it might be related to operations with an * Holds if `name` looks like it might be related to operations with an
* insecure encryption algorithm. * insecure encyption algorithm.
*/ */
bindingset[name] bindingset[name]
predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgorithmRegex()) } predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgorithmRegex()) }
/** /**
* Holds if there is additional evidence that `name` looks like it might be * Holds if there is additional evidence that `name` looks like it might be
* related to operations with an encryption algorithm, besides the name of a * related to operations with an encyption algorithm, besides the name of a
* specific algorithm. This can be used in conjunction with * specific algorithm. This can be used in conjunction with
* `isInsecureEncryption` to produce a stronger heuristic. * `isInsecureEncryption` to produce a stronger heuristic.
*/ */

View File

@@ -1,7 +1,7 @@
/** /**
* DEPRECATED: we now use `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`, * DEPRECATED: we now use `semmle.code.cpp.ir.dataflow.DefaultTaintTracking`,
* which is based on the IR but designed to behave similarly to this old * which is based on the IR but designed to behave similarly to this old
* library. * libarary.
* *
* Provides the implementation of `semmle.code.cpp.security.TaintTracking`. Do * Provides the implementation of `semmle.code.cpp.security.TaintTracking`. Do
* not import this file directly. * not import this file directly.

View File

@@ -104,7 +104,7 @@ private newtype HC_Alloc =
HC_HasAlloc(HashCons hc) { mk_HasAlloc(hc, _) } HC_HasAlloc(HashCons hc) { mk_HasAlloc(hc, _) }
/** /**
* Used to implement optional extent expression on `new[]` expressions * Used to implement optional extent expression on `new[]` exprtessions
*/ */
private newtype HC_Extent = private newtype HC_Extent =
HC_NoExtent() or HC_NoExtent() or
@@ -116,7 +116,7 @@ private newtype HC_Args =
HC_ArgCons(HashCons hc, int i, HC_Args list) { mk_ArgCons(hc, i, list, _) } HC_ArgCons(HashCons hc, int i, HC_Args list) { mk_ArgCons(hc, i, list, _) }
/** /**
* Used to implement hash-consing of struct initializers. * Used to implement hash-consing of struct initizializers.
*/ */
private newtype HC_Fields = private newtype HC_Fields =
HC_EmptyFields(Class c) { exists(ClassAggregateLiteral cal | c = cal.getUnspecifiedType()) } or HC_EmptyFields(Class c) { exists(ClassAggregateLiteral cal | c = cal.getUnspecifiedType()) } or

View File

@@ -35,4 +35,4 @@ from LocalVariableOrParameter lv, GlobalVariable gv
where where
lv.getName() = gv.getName() and lv.getName() = gv.getName() and
lv.getFile() = gv.getFile() lv.getFile() = gv.getFile()
select lv, lv.type() + gv.getName() + " hides a $@ with the same name.", gv, "global variable" select lv, lv.type() + gv.getName() + " hides $@ with the same name.", gv, "a global variable"

View File

@@ -1,32 +0,0 @@
/*
* In this example, the developer intended to use a semicolon but accidentally used a comma:
*/
enum privileges entitlements = NONE;
if (is_admin)
entitlements = FULL, // BAD
restrict_privileges(entitlements);
/*
* The use of a comma means that the first example is equivalent to this second example:
*/
enum privileges entitlements = NONE;
if (is_admin) {
entitlements = FULL;
restrict_privileges(entitlements);
}
/*
* The indentation of the first example suggests that the developer probably intended the following code:
*/
enum privileges entitlements = NONE;
if (is_admin)
entitlements = FULL; // GOOD
restrict_privileges(entitlements);

View File

@@ -1,39 +0,0 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
If the expression after the comma operator starts at an earlier column than the expression before the comma, then
this suspicious indentation possibly indicates a logic error, caused by a typo that may escape visual inspection.
</p>
<warning>
This query has medium precision because CodeQL currently does not distinguish between tabs and spaces in whitespace.
If a file contains mixed tabs and spaces, alerts may highlight code that is correctly indented for one value of tab size but not for other tab sizes.
</warning>
</overview>
<recommendation>
<p>
To ensure that your code is easy to read and review, use standard indentation around the comma operator. Always begin the right-hand-side operand at the same level of
indentation (column number) as the left-hand-side operand. This makes it easier for other developers to see the intended behavior of your code.
</p>
<p>
Use whitespace consistently to communicate your coding intentions. Where possible, avoid mixing tabs and spaces within a file. If you need to mix them, use them consistently.
</p>
</recommendation>
<example>
<p>
This example shows three different ways of writing the same code. The first example contains a comma instead of a semicolon which means that the final line is part of the <code>if</code> statement, even though the indentation suggests that it is intended to be separate. The second example looks different but is functionally the same as the first example. It is more likely that the developer intended to write the third example.
</p>
<sample src="CommaBeforeMisleadingIndentation.cpp" />
</example>
<references>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Comma_operator">Comma operator</a></li>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Indentation_style#Tabs,_spaces,_and_size_of_indentations">Indentation style &mdash; Tabs, spaces, and size of indentations</a></li>
</references>
</qhelp>

View File

@@ -1,74 +0,0 @@
/**
* @name Comma before misleading indentation
* @description If expressions before and after a comma operator use different indentation, it is easy to misread the purpose of the code.
* @kind problem
* @id cpp/comma-before-misleading-indentation
* @problem.severity warning
* @security-severity 7.8
* @precision medium
* @tags maintainability
* readability
* security
* external/cwe/cwe-1078
* external/cwe/cwe-670
*/
import cpp
import semmle.code.cpp.commons.Exclusions
/**
* Gets a child of `e`, including conversions but excluding call arguments.
*/
pragma[inline]
Expr getAChildWithConversions(Expr e) {
result.getParentWithConversions() = e and
not result = any(Call c).getAnArgument()
}
/**
* Gets the left-most column position of any transitive child of `e` (including
* conversions but excluding call arguments).
*/
int getCandidateColumn(Expr e) {
result = e.getLocation().getStartColumn() or
result = getCandidateColumn(getAChildWithConversions(e))
}
/**
* Gets the transitive child of `e` (including conversions but excluding call
* arguments) at the left-most column position, preferring less deeply nested
* expressions if there is a choice.
*/
Expr normalizeExpr(Expr e) {
e.getLocation().getStartColumn() = min(getCandidateColumn(e)) and
result = e
or
not e.getLocation().getStartColumn() = min(getCandidateColumn(e)) and
result = normalizeExpr(getAChildWithConversions(e)) and
result.getLocation().getStartColumn() = min(getCandidateColumn(e))
}
predicate isParenthesized(CommaExpr ce) {
ce.getParent*().(Expr).isParenthesised()
or
ce.isUnevaluated() // sizeof(), decltype(), alignof(), noexcept(), typeid()
or
ce.getParent*() = [any(IfStmt i).getCondition(), any(SwitchStmt s).getExpr()]
or
ce.getParent*() = [any(Loop l).getCondition(), any(ForStmt f).getUpdate()]
or
ce.getEnclosingStmt() = any(ForStmt f).getInitialization()
}
from CommaExpr ce, Expr left, Expr right, Location leftLoc, Location rightLoc
where
ce.fromSource() and
not isFromMacroDefinition(ce) and
left = normalizeExpr(ce.getLeftOperand().getFullyConverted()) and
right = normalizeExpr(ce.getRightOperand().getFullyConverted()) and
leftLoc = left.getLocation() and
rightLoc = right.getLocation() and
not isParenthesized(ce) and
leftLoc.getEndLine() < rightLoc.getStartLine() and
leftLoc.getStartColumn() > rightLoc.getStartColumn()
select right, "The indentation level may be misleading for some tab sizes."

View File

@@ -13,32 +13,16 @@
import cpp import cpp
pragma[noinline]
predicate possiblyIncompleteFile(File f) {
exists(Diagnostic d | d.getFile() = f and d.getSeverity() >= 3)
}
predicate immediatelyReachableFunction(Function f) { predicate immediatelyReachableFunction(Function f) {
not f.isStatic() not f.isStatic() or
or exists(BlockExpr be | be.getFunction() = f) or
exists(BlockExpr be | be.getFunction() = f) f instanceof MemberFunction or
or f instanceof TemplateFunction or
f instanceof MemberFunction f.getFile() instanceof HeaderFile or
or f.getAnAttribute().hasName("constructor") or
f instanceof TemplateFunction f.getAnAttribute().hasName("destructor") or
or f.getAnAttribute().hasName("used") or
f.getFile() instanceof HeaderFile
or
f.getAnAttribute().hasName("constructor")
or
f.getAnAttribute().hasName("destructor")
or
f.getAnAttribute().hasName("used")
or
f.getAnAttribute().hasName("unused") f.getAnAttribute().hasName("unused")
or
// a compiler error in the same file suggests we may be missing data
possiblyIncompleteFile(f.getFile())
} }
predicate immediatelyReachableVariable(Variable v) { predicate immediatelyReachableVariable(Variable v) {

View File

@@ -11,7 +11,7 @@ caused by an unhandled case.</p>
</overview> </overview>
<recommendation> <recommendation>
<p>Check that the unused static variable does not indicate a defect, for example, an unhandled case. If the static variable is genuinely not needed, <p>Check that the unused static variable does not indicate a defect, for example, an unhandled case. If the static variable is genuinuely not needed,
then removing it will make code more readable. If the static variable is needed then you should update the code to fix the defect.</p> then removing it will make code more readable. If the static variable is needed then you should update the code to fix the defect.</p>
</recommendation> </recommendation>

View File

@@ -1,26 +1,3 @@
## 0.4.3
### Minor Analysis Improvements
* Fixed a bug in `cpp/jsf/av-rule-76` that caused the query to miss results when an implicitly-defined copy constructor or copy assignment operator was generated.
## 0.4.2
### New Queries
* Added a new medium-precision query, `cpp/comma-before-misleading-indentation`, which detects instances of whitespace that have readability issues.
### Minor Analysis Improvements
* The "Unterminated variadic call" (`cpp/unterminated-variadic-call`) query has been tuned to produce fewer false positive results.
* Fixed false positives from the "Unused static function" (`cpp/unused-static-function`) query in files that had errors during compilation.
## 0.4.1
### Minor Analysis Improvements
* The alert message of many queries have been changed to better follow the style guide and make the message consistent with other languages.
## 0.4.0 ## 0.4.0
### New Queries ### New Queries
@@ -142,7 +119,7 @@
* The `security` tag has been added to the `cpp/return-stack-allocated-memory` query. As a result, its results will now appear by default. * The `security` tag has been added to the `cpp/return-stack-allocated-memory` query. As a result, its results will now appear by default.
* The "Uncontrolled data in arithmetic expression" (cpp/uncontrolled-arithmetic) query has been enhanced to reduce false positive results and its @precision increased to high. * The "Uncontrolled data in arithmetic expression" (cpp/uncontrolled-arithmetic) query has been enhanced to reduce false positive results and its @precision increased to high.
* A new `cpp/very-likely-overrunning-write` query has been added to the default query suite for C/C++. The query reports some results that were formerly flagged by `cpp/overrunning-write`. * A new `cpp/very-likely-overruning-write` query has been added to the default query suite for C/C++. The query reports some results that were formerly flagged by `cpp/overruning-write`.
### Minor Analysis Improvements ### Minor Analysis Improvements

View File

@@ -19,7 +19,7 @@ This can occur when an operation performed on the open descriptor fails, and the
<example> <example>
<p>In the example below, the <code>sockfd</code> socket may remain open if an error is triggered. <p>In the example below, the <code>sockfd</code> socket may remain open if an error is triggered.
The code should be updated to ensure that the socket is always closed when the function ends. The code should be updated to ensure that the socket is always closed when when the function ends.
</p> </p>
<sample src="DescriptorMayNotBeClosed.cpp" /> <sample src="DescriptorMayNotBeClosed.cpp" />
</example> </example>

View File

@@ -63,7 +63,7 @@ predicate verifiedRealloc(FunctionCall reallocCall, Variable v, ControlFlowNode
node.(AnalysedExpr).getNonNullSuccessor(newV) = verified and node.(AnalysedExpr).getNonNullSuccessor(newV) = verified and
// note: this case uses naive flow logic (getAnAssignedValue). // note: this case uses naive flow logic (getAnAssignedValue).
// special case: if the result of the 'realloc' is assigned to the // special case: if the result of the 'realloc' is assigned to the
// same variable, we don't discriminate properly between the old // same variable, we don't descriminate properly between the old
// and the new allocation; better to not consider this a free at // and the new allocation; better to not consider this a free at
// all in that case. // all in that case.
newV != v newV != v

View File

@@ -3,7 +3,6 @@
* @description Lists all files in the source code directory that were extracted without encountering a problem in the file. * @description Lists all files in the source code directory that were extracted without encountering a problem in the file.
* @kind diagnostic * @kind diagnostic
* @id cpp/diagnostics/successfully-extracted-files * @id cpp/diagnostics/successfully-extracted-files
* @tags successfully-extracted-files
*/ */
import cpp import cpp

View File

@@ -15,7 +15,7 @@ As an exception, because their purpose is usually obvious, it is not necessary t
</overview> </overview>
<recommendation> <recommendation>
<p> <p>
Add comments to document the purpose of the function. In particular, ensure that the public API of the function is carefully documented. This reduces the chance that a future change to the function will introduce a defect by changing the API and breaking the expectations of the calling functions. Add comments to document the purpose of the function. In particular, ensure that the public API of the function is carefully documented. This reduces the chance that a future change to the function will introduce a defect by changing the API and breaking the expections of the calling functions.
</p> </p>
</recommendation> </recommendation>

View File

@@ -6,7 +6,7 @@
<overview> <overview>
<p> <p>
This rule finds comparison expressions that use 2 or more comparison operators and are not completely parenthesized. This rule finds comparison expressions that use 2 or more comparison operators and are not completely paranthesized.
It is best to fully parenthesize complex comparison expressions to explicitly define the order of the comparison operators. It is best to fully parenthesize complex comparison expressions to explicitly define the order of the comparison operators.
</p> </p>

View File

@@ -23,7 +23,7 @@ DoStmt getAFalseLoop() {
/** /**
* Gets a `do` ... `while` loop surrounding a statement. This is blocked by a * Gets a `do` ... `while` loop surrounding a statement. This is blocked by a
* `switch` statement, since a `continue` inside a `switch` inside a loop may be * `switch` statement, since a `continue` inside a `switch` inside a loop may be
* justified (`continue` breaks out of the loop whereas `break` only escapes the * jusitifed (`continue` breaks out of the loop whereas `break` only escapes the
* `switch`). * `switch`).
*/ */
DoStmt enclosingLoop(Stmt s) { DoStmt enclosingLoop(Stmt s) {

View File

@@ -25,11 +25,8 @@ class CastToPointerArithFlow extends DataFlow::Configuration {
override predicate isSource(DataFlow::Node node) { override predicate isSource(DataFlow::Node node) {
not node.asExpr() instanceof Conversion and not node.asExpr() instanceof Conversion and
exists(Type baseType1, Type baseType2 | introducesNewField(node.asExpr().getType().(DerivedType).getBaseType(),
hasBaseType(node.asExpr(), baseType1) and node.asExpr().getConversion*().getType().(DerivedType).getBaseType())
hasBaseType(node.asExpr().getConversion*(), baseType2) and
introducesNewField(baseType1, baseType2)
)
} }
override predicate isSink(DataFlow::Node node) { override predicate isSink(DataFlow::Node node) {
@@ -38,17 +35,6 @@ class CastToPointerArithFlow extends DataFlow::Configuration {
} }
} }
/**
* Holds if the type of `e` is a `DerivedType` with `base` as its base type.
*
* This predicate ensures that joins go from `e` to `base` instead
* of the other way around.
*/
pragma[inline]
predicate hasBaseType(Expr e, Type base) {
pragma[only_bind_into](base) = e.getType().(DerivedType).getBaseType()
}
/** /**
* `derived` has a (possibly indirect) base class of `base`, and at least one new * `derived` has a (possibly indirect) base class of `base`, and at least one new
* field has been introduced in the inheritance chain after `base`. * field has been introduced in the inheritance chain after `base`.

View File

@@ -6,9 +6,9 @@
<overview> <overview>
<p>This rule finds logical-not operator usage as an operator for in a bit-wise operation.</p> <p>This rule finds logical-not operator usage as an operator for in a bit-wise operation.</p>
<p>Due to the nature of logical operation result value, only the lowest bit could possibly be set, and it is unlikely to be intent in bitwise operations. Violations are often indicative of a typo, using a logical-not (<code>!</code>) operator instead of the bit-wise not (<code>~</code>) operator. </p> <p>Due to the nature of logical operation result value, only the lowest bit could possibly be set, and it is unlikely to be intent in bitwise opeartions. Violations are often indicative of a typo, using a logical-not (<code>!</code>) opeartor instead of the bit-wise not (<code>~</code>) operator. </p>
<p>This rule is restricted to analyze bit-wise and (<code>&amp;</code>) and bit-wise or (<code>|</code>) operation in order to provide better precision.</p> <p>This rule is restricted to analyze bit-wise and (<code>&amp;</code>) and bit-wise or (<code>|</code>) operation in order to provide better precision.</p>
<p>This rule ignores instances where a double negation (<code>!!</code>) is explicitly used as the operator of the bitwise operation, as this is a commonly used as a mechanism to normalize an integer value to either 1 or 0.</p> <p>This rule ignores instances where a double negation (<code>!!</code>) is explicitly used as the opeartor of the bitwise operation, as this is a commonly used as a mechanism to normalize an integer value to either 1 or 0.</p>
<p>NOTE: It is not recommended to use this rule in kernel code or older C code as it will likely find several false positive instances.</p> <p>NOTE: It is not recommended to use this rule in kernel code or older C code as it will likely find several false positive instances.</p>
</overview> </overview>

View File

@@ -17,7 +17,7 @@ import cpp
/** /**
* It's common in some projects to use "a double negation" to normalize the boolean * It's common in some projects to use "a double negation" to normalize the boolean
* result to either 1 or 0. * result to either 1 or 0.
* This predicate is intended to filter explicit usage of a double negation as it typically * This predciate is intended to filter explicit usage of a double negation as it typically
* indicates the explicit purpose to normalize the result for bit-wise or arithmetic purposes. * indicates the explicit purpose to normalize the result for bit-wise or arithmetic purposes.
*/ */
predicate doubleNegationNormalization(NotExpr notexpr) { notexpr.getAnOperand() instanceof NotExpr } predicate doubleNegationNormalization(NotExpr notexpr) { notexpr.getAnOperand() instanceof NotExpr }

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