mirror of
https://github.com/github/codeql.git
synced 2026-05-26 09:01:22 +02:00
Compare commits
1 Commits
esbena/reb
...
post-relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3c6b06da5 |
@@ -4,18 +4,14 @@
|
||||
"*/ql/lib/qlpack.yml",
|
||||
"*/ql/test/qlpack.yml",
|
||||
"*/ql/examples/qlpack.yml",
|
||||
"*/ql/consistency-queries/qlpack.yml",
|
||||
"*/upgrades/qlpack.yml",
|
||||
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
|
||||
"javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml",
|
||||
"javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml",
|
||||
"javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml",
|
||||
"csharp/ql/campaigns/Solorigate/lib/qlpack.yml",
|
||||
"csharp/ql/campaigns/Solorigate/src/qlpack.yml",
|
||||
"csharp/ql/campaigns/Solorigate/test/qlpack.yml",
|
||||
"misc/legacy-support/*/qlpack.yml",
|
||||
"misc/suite-helpers/qlpack.yml",
|
||||
"ruby/extractor-pack/codeql-extractor.yml",
|
||||
"ql/extractor-pack/codeql-extractor.yml"
|
||||
"ruby/ql/consistency-queries/qlpack.yml"
|
||||
],
|
||||
"versionPolicies": {
|
||||
"default": {
|
||||
|
||||
7
.gitattributes
vendored
7
.gitattributes
vendored
@@ -50,9 +50,4 @@
|
||||
*.pdb -text
|
||||
|
||||
java/ql/test/stubs/**/*.java linguist-generated=true
|
||||
java/ql/test/experimental/stubs/**/*.java linguist-generated=true
|
||||
|
||||
# Generated test files - these are synced from the standard JavaScript libraries using
|
||||
# `javascript/ql/experimental/adaptivethreatmodeling/test/update_endpoint_test_files.py`.
|
||||
javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/**/*.js linguist-generated=true -merge
|
||||
javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/autogenerated/**/*.ts linguist-generated=true -merge
|
||||
java/ql/test/experimental/stubs/**/*.java linguist-generated=true
|
||||
3
.github/labeler.yml
vendored
3
.github/labeler.yml
vendored
@@ -26,6 +26,3 @@ documentation:
|
||||
- "**/*.qhelp"
|
||||
- "**/*.md"
|
||||
- docs/**/*
|
||||
|
||||
"QL-for-QL":
|
||||
- ql/**/*
|
||||
1
.github/workflows/check-change-note.yml
vendored
1
.github/workflows/check-change-note.yml
vendored
@@ -7,7 +7,6 @@ on:
|
||||
- "*/ql/src/**/*.ql"
|
||||
- "*/ql/src/**/*.qll"
|
||||
- "!**/experimental/**"
|
||||
- "!ql/**"
|
||||
|
||||
jobs:
|
||||
check-change-note:
|
||||
|
||||
7
.github/workflows/codeql-analysis.yml
vendored
7
.github/workflows/codeql-analysis.yml
vendored
@@ -27,11 +27,6 @@ jobs:
|
||||
pull-requests: read
|
||||
|
||||
steps:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: 6.0.101
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
@@ -56,7 +51,7 @@ jobs:
|
||||
# uses a compiled language
|
||||
|
||||
- run: |
|
||||
dotnet build csharp /p:UseSharedCompilation=false
|
||||
dotnet build csharp
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@main
|
||||
|
||||
43
.github/workflows/csv-coverage-metrics.yml
vendored
43
.github/workflows/csv-coverage-metrics.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: "Publish framework coverage as metrics"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '5 0 * * *'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- ".github/workflows/csv-coverage-metrics.yml"
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
- name: Create empty database
|
||||
run: |
|
||||
DATABASE="${{ runner.temp }}/java-database"
|
||||
PROJECT="${{ runner.temp }}/java-project"
|
||||
mkdir -p "$PROJECT/src/tmp/empty"
|
||||
echo "class Empty {}" >> "$PROJECT/src/tmp/empty/Empty.java"
|
||||
codeql database create "$DATABASE" --language=java --source-root="$PROJECT" --command 'javac src/tmp/empty/Empty.java'
|
||||
- name: Capture coverage information
|
||||
run: |
|
||||
DATABASE="${{ runner.temp }}/java-database"
|
||||
codeql database analyze --format=sarif-latest --output=metrics.sarif -- "$DATABASE" ./java/ql/src/Metrics/Summaries/FrameworkCoverage.ql
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: metrics.sarif
|
||||
path: metrics.sarif
|
||||
retention-days: 20
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: metrics.sarif
|
||||
67
.github/workflows/js-ml-tests.yml
vendored
67
.github/workflows/js-ml-tests.yml
vendored
@@ -1,67 +0,0 @@
|
||||
name: JS ML-powered queries tests
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/**"
|
||||
- .github/workflows/js-ml-tests.yml
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
pull_request:
|
||||
paths:
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/**"
|
||||
- .github/workflows/js-ml-tests.yml
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: javascript/ql/experimental/adaptivethreatmodeling
|
||||
|
||||
jobs:
|
||||
qlformat:
|
||||
name: Check QL formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Check QL formatting
|
||||
run: |
|
||||
find . "(" -name "*.ql" -or -name "*.qll" ")" -print0 | \
|
||||
xargs -0 codeql query format --check-only
|
||||
|
||||
qlcompile:
|
||||
name: Check QL compilation
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Check QL compilation
|
||||
run: |
|
||||
codeql query compile \
|
||||
--check-only \
|
||||
--ram 5120 \
|
||||
--additional-packs "${{ github.workspace }}" \
|
||||
--threads=0 \
|
||||
-- \
|
||||
lib modelbuilding src
|
||||
|
||||
qltest:
|
||||
name: Run QL tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
codeql test run \
|
||||
--threads=0 \
|
||||
--ram 5120 \
|
||||
--additional-packs "${{ github.workspace }}" \
|
||||
-- \
|
||||
test
|
||||
103
.github/workflows/mad_modelDiff.yml
vendored
103
.github/workflows/mad_modelDiff.yml
vendored
@@ -1,103 +0,0 @@
|
||||
name: Models as Data - Diff
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
projects:
|
||||
description: "The projects to generate models for"
|
||||
required: true
|
||||
default: '["netty/netty"]'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "java/ql/src/utils/model-generator/**/*.*"
|
||||
- ".github/workflows/mad_modelDiff.yml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
model-diff:
|
||||
name: Model Difference
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'github/codeql'
|
||||
strategy:
|
||||
matrix:
|
||||
slug: ${{fromJson(github.event.inputs.projects || '["apache/commons-codec", "apache/commons-io", "apache/commons-beanutils", "apache/commons-logging", "apache/commons-fileupload", "apache/commons-lang", "apache/commons-validator", "apache/commons-csv", "apache/dubbo"]' )}}
|
||||
steps:
|
||||
- name: Clone github/codeql from PR
|
||||
uses: actions/checkout@v2
|
||||
if: github.event.pull_request
|
||||
with:
|
||||
path: codeql-pr
|
||||
- name: Clone github/codeql from main
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: codeql-main
|
||||
ref: main
|
||||
- uses: ./codeql-main/.github/actions/fetch-codeql
|
||||
- name: Download database
|
||||
env:
|
||||
SLUG: ${{ matrix.slug }}
|
||||
run: |
|
||||
set -x
|
||||
mkdir lib-dbs
|
||||
SHORTNAME=${SLUG//[^a-zA-Z0-9_]/}
|
||||
projectId=`curl -s https://lgtm.com/api/v1.0/projects/g/${SLUG} | jq .id`
|
||||
curl -L "https://lgtm.com/api/v1.0/snapshots/$projectId/java" -o "$SHORTNAME.zip"
|
||||
unzip -q -d "${SHORTNAME}-db" "${SHORTNAME}.zip"
|
||||
mkdir "lib-dbs/$SHORTNAME/"
|
||||
mv "${SHORTNAME}-db/"$(ls -1 "${SHORTNAME}"-db)/* "lib-dbs/${SHORTNAME}/"
|
||||
- name: Generate Models (PR and main)
|
||||
run: |
|
||||
set -x
|
||||
mkdir tmp-models
|
||||
MODELS=`pwd`/tmp-models
|
||||
DATABASES=`pwd`/lib-dbs
|
||||
|
||||
analyzeDatabaseWithCheckout() {
|
||||
QL_VARIANT=$1
|
||||
DATABASE=$2
|
||||
cd codeql-$QL_VARIANT
|
||||
SHORTNAME=`basename $DATABASE`
|
||||
python java/ql/src/utils/model-generator/GenerateFlowModel.py $DATABASE $MODELS/${SHORTNAME}.qll
|
||||
mv $MODELS/${SHORTNAME}.qll $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.qll
|
||||
cd ..
|
||||
}
|
||||
|
||||
for d in $DATABASES/*/ ; do
|
||||
ls -1 "$d"
|
||||
|
||||
analyzeDatabaseWithCheckout "main" $d
|
||||
if [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]
|
||||
then
|
||||
analyzeDatabaseWithCheckout "pr" $d
|
||||
fi
|
||||
done
|
||||
- name: Install diff2html
|
||||
if: github.event.pull_request
|
||||
run: |
|
||||
npm install -g diff2html-cli
|
||||
- name: Generate Model Diff
|
||||
if: github.event.pull_request
|
||||
run: |
|
||||
set -x
|
||||
MODELS=`pwd`/tmp-models
|
||||
ls -1 tmp-models/
|
||||
for m in $MODELS/*_main.qll ; do
|
||||
t="${m/main/"pr"}"
|
||||
basename=`basename $m`
|
||||
name="diff_${basename/_main.qll/""}"
|
||||
(diff -w -u $m $t | diff2html -i stdin -F $MODELS/$name.html) || true
|
||||
done
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: models
|
||||
path: tmp-models/*.qll
|
||||
retention-days: 20
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: diffs
|
||||
path: tmp-models/*.html
|
||||
retention-days: 20
|
||||
62
.github/workflows/mad_regenerate-models.yml
vendored
62
.github/workflows/mad_regenerate-models.yml
vendored
@@ -1,62 +0,0 @@
|
||||
name: Regenerate framework models
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "30 2 * * *"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- ".github/workflows/mad_regenerate-models.yml"
|
||||
|
||||
jobs:
|
||||
regenerate-models:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
# placeholder required for each axis, excluded below, replaced by the actual combinations (see include)
|
||||
slug: ["placeholder"]
|
||||
ref: ["placeholder"]
|
||||
include:
|
||||
- slug: "apache/commons-io"
|
||||
ref: "8985de8fe74f6622a419b37a6eed0dbc484dc128"
|
||||
exclude:
|
||||
- slug: "placeholder"
|
||||
ref: "placeholder"
|
||||
steps:
|
||||
- name: Clone self (github/codeql)
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup CodeQL binaries
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
- name: Clone repositories
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: repos/${{ matrix.ref }}
|
||||
ref: ${{ matrix.ref }}
|
||||
repository: ${{ matrix.slug }}
|
||||
- name: Build database
|
||||
env:
|
||||
SLUG: ${{ matrix.slug }}
|
||||
REF: ${{ matrix.ref }}
|
||||
run: |
|
||||
mkdir dbs
|
||||
cd repos/${REF}
|
||||
SHORTNAME=${SLUG//[^a-zA-Z0-9_]/}
|
||||
codeql database create --language=java ../../dbs/${SHORTNAME}
|
||||
- name: Regenerate models in-place
|
||||
env:
|
||||
SLUG: ${{ matrix.slug }}
|
||||
run: |
|
||||
SHORTNAME=${SLUG//[^a-zA-Z0-9_]/}
|
||||
java/ql/src/utils/model-generator/RegenerateModels.py "${SLUG}" dbs/${SHORTNAME}
|
||||
- name: Stage changes
|
||||
run: |
|
||||
find java -name "*.qll" -print0 | xargs -0 git add
|
||||
git status
|
||||
git diff --cached > models.patch
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: patch
|
||||
path: models.patch
|
||||
retention-days: 7
|
||||
199
.github/workflows/ql-for-ql-build.yml
vendored
199
.github/workflows/ql-for-ql-build.yml
vendored
@@ -1,199 +0,0 @@
|
||||
name: Run QL for QL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
queries:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@erik-krogh/ql
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- name: Get CodeQL version
|
||||
id: get-codeql-version
|
||||
run: |
|
||||
echo "::set-output name=version::$("${CODEQL}" --version | head -n 1 | rev | cut -d " " -f 1 | rev)"
|
||||
shell: bash
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Cache queries
|
||||
id: cache-queries
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ runner.temp }}/query-pack.zip
|
||||
key: queries-${{ hashFiles('ql/**/*.ql*') }}-${{ hashFiles('ql/ql/src/ql.dbscheme*') }}-${{ steps.get-codeql-version.outputs.version }}
|
||||
- name: Build query pack
|
||||
if: steps.cache-queries.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd ql/ql/src
|
||||
"${CODEQL}" pack create
|
||||
cd .codeql/pack/codeql/ql-all/0.0.0
|
||||
zip "${PACKZIP}" -r .
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
PACKZIP: ${{ runner.temp }}/query-pack.zip
|
||||
- name: Upload query pack
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: query-pack-zip
|
||||
path: ${{ runner.temp }}/query-pack.zip
|
||||
|
||||
extractors:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache entire extractor
|
||||
id: cache-extractor
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
ql/target/release/ql-autobuilder
|
||||
ql/target/release/ql-autobuilder.exe
|
||||
ql/target/release/ql-extractor
|
||||
ql/target/release/ql-extractor.exe
|
||||
key: ${{ runner.os }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
|
||||
- name: Cache cargo
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-rust-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo fmt --all -- --check
|
||||
- name: Build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo build --verbose
|
||||
- name: Run tests
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo test --verbose
|
||||
- name: Release build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo build --release
|
||||
- name: Generate dbscheme
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: ql/target/release/ql-generator --dbscheme ql/ql/src/ql.dbscheme --library ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: extractor-ubuntu-latest
|
||||
path: |
|
||||
ql/target/release/ql-autobuilder
|
||||
ql/target/release/ql-autobuilder.exe
|
||||
ql/target/release/ql-extractor
|
||||
ql/target/release/ql-extractor.exe
|
||||
retention-days: 1
|
||||
package:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs:
|
||||
- extractors
|
||||
- queries
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: query-pack-zip
|
||||
path: query-pack-zip
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: extractor-ubuntu-latest
|
||||
path: linux64
|
||||
- run: |
|
||||
unzip query-pack-zip/*.zip -d pack
|
||||
cp -r ql/codeql-extractor.yml ql/tools ql/ql/src/ql.dbscheme.stats pack/
|
||||
mkdir -p pack/tools/linux64
|
||||
if [[ -f linux64/ql-autobuilder ]]; then
|
||||
cp linux64/ql-autobuilder pack/tools/linux64/autobuilder
|
||||
chmod +x pack/tools/linux64/autobuilder
|
||||
fi
|
||||
if [[ -f linux64/ql-extractor ]]; then
|
||||
cp linux64/ql-extractor pack/tools/linux64/extractor
|
||||
chmod +x pack/tools/linux64/extractor
|
||||
fi
|
||||
cd pack
|
||||
zip -rq ../codeql-ql.zip .
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: codeql-ql-pack
|
||||
path: codeql-ql.zip
|
||||
retention-days: 1
|
||||
analyze:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
folder: [cpp, csharp, java, javascript, python, ql, ruby]
|
||||
|
||||
needs:
|
||||
- package
|
||||
|
||||
steps:
|
||||
- name: Download pack
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: codeql-ql-pack
|
||||
path: ${{ runner.temp }}/codeql-ql-pack-artifact
|
||||
|
||||
- name: Prepare pack
|
||||
run: |
|
||||
unzip "${PACK_ARTIFACT}/*.zip" -d "${PACK}"
|
||||
env:
|
||||
PACK_ARTIFACT: ${{ runner.temp }}/codeql-ql-pack-artifact
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
- name: Hack codeql-action options
|
||||
run: |
|
||||
JSON=$(jq -nc --arg pack "${PACK}" '.resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .database.init=["--search-path", $pack]')
|
||||
echo "CODEQL_ACTION_EXTRA_OPTIONS=${JSON}" >> ${GITHUB_ENV}
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Create CodeQL config file
|
||||
run: |
|
||||
echo "paths:" > ${CONF}
|
||||
echo " - ${FOLDER}" >> ${CONF}
|
||||
echo "paths-ignore:" >> ${CONF}
|
||||
echo " - ql/ql/test" >> ${CONF}
|
||||
echo "Config file: "
|
||||
cat ${CONF}
|
||||
env:
|
||||
CONF: ./ql-for-ql-config.yml
|
||||
FOLDER: ${{ matrix.folder }}
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@erik-krogh/ql
|
||||
with:
|
||||
languages: ql
|
||||
db-location: ${{ runner.temp }}/db
|
||||
config-file: ./ql-for-ql-config.yml
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@erik-krogh/ql
|
||||
with:
|
||||
category: "ql-for-ql-${{ matrix.folder }}"
|
||||
- name: Copy sarif file to CWD
|
||||
run: cp ../results/ql.sarif ./${{ matrix.folder }}.sarif
|
||||
- name: Sarif as artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ${{ matrix.folder }}.sarif
|
||||
path: ${{ matrix.folder }}.sarif
|
||||
|
||||
84
.github/workflows/ql-for-ql-dataset_measure.yml
vendored
84
.github/workflows/ql-for-ql-dataset_measure.yml
vendored
@@ -1,84 +0,0 @@
|
||||
name: Collect database stats for QL for QL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ql/ql/src/ql.dbscheme
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ql/ql/src/ql.dbscheme
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
measure:
|
||||
env:
|
||||
CODEQL_THREADS: 4 # TODO: remove this once it's set by the CLI
|
||||
strategy:
|
||||
matrix:
|
||||
repo:
|
||||
- github/codeql
|
||||
- github/codeql-go
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@erik-krogh/ql
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
- name: Build Extractor
|
||||
run: cd ql; env "PATH=$PATH:`dirname ${CODEQL}`" ./create-extractor-pack.sh
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Checkout ${{ matrix.repo }}
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ${{ matrix.repo }}
|
||||
path: ${{ github.workspace }}/repo
|
||||
- name: Create database
|
||||
run: |
|
||||
"${CODEQL}" database create \
|
||||
--search-path "ql/extractor-pack" \
|
||||
--threads 4 \
|
||||
--language ql --source-root "${{ github.workspace }}/repo" \
|
||||
"${{ runner.temp }}/database"
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Measure database
|
||||
run: |
|
||||
mkdir -p "stats/${{ matrix.repo }}"
|
||||
"${CODEQL}" dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ql"
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: measurements
|
||||
path: stats
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs: measure
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: measurements
|
||||
path: stats
|
||||
- run: |
|
||||
python -m pip install --user lxml
|
||||
find stats -name 'stats.xml' -print0 | sort -z | xargs -0 python ql/scripts/merge_stats.py --output ql/ql/src/ql.dbscheme.stats --normalise ql_tokeninfo
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ql.dbscheme.stats
|
||||
path: ql/ql/src/ql.dbscheme.stats
|
||||
52
.github/workflows/ql-for-ql-tests.yml
vendored
52
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -1,52 +0,0 @@
|
||||
name: Run QL for QL Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "ql/**"
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "ql/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
qltest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@erik-krogh/ql
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
- name: Build extractor
|
||||
run: |
|
||||
cd ql;
|
||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
||||
env "PATH=$PATH:$codeqlpath" ./create-extractor-pack.sh
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries ql/ql/test
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Check QL formatting
|
||||
run: |
|
||||
find ql/ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 "${CODEQL}" query format --check-only
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Check QL compilation
|
||||
run: |
|
||||
"${CODEQL}" query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ql/extractor-pack" "ql/ql/src" "ql/ql/examples"
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
2
.github/workflows/ruby-build.yml
vendored
2
.github/workflows/ruby-build.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ruby/target
|
||||
key: ${{ runner.os }}-ruby-rust-cargo-${{ hashFiles('ruby/rust-toolchain.toml', 'ruby/**/Cargo.lock') }}
|
||||
key: ${{ runner.os }}-rust-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check
|
||||
- name: Build
|
||||
|
||||
54
.github/workflows/ruby-qltest.yml
vendored
54
.github/workflows/ruby-qltest.yml
vendored
@@ -24,53 +24,27 @@ defaults:
|
||||
working-directory: ruby
|
||||
|
||||
jobs:
|
||||
qlformat:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- name: Check QL formatting
|
||||
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
|
||||
qlcompile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- name: Check QL compilation
|
||||
run: |
|
||||
codeql query compile --check-only --threads=0 --ram 5000 --warnings=error "ql/src" "ql/examples"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
qlupgrade:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- name: Check DB upgrade scripts
|
||||
run: |
|
||||
echo >empty.trap
|
||||
codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap
|
||||
codeql dataset upgrade testdb --additional-packs ql/lib
|
||||
diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme
|
||||
- name: Check DB downgrade scripts
|
||||
run: |
|
||||
echo >empty.trap
|
||||
rm -rf testdb; codeql dataset import -S ql/lib/ruby.dbscheme testdb empty.trap
|
||||
codeql resolve upgrades --format=lines --allow-downgrades --additional-packs downgrades \
|
||||
--dbscheme=ql/lib/ruby.dbscheme --target-dbscheme=downgrades/initial/ruby.dbscheme |
|
||||
xargs codeql execute upgrades testdb
|
||||
diff -q testdb/ruby.dbscheme downgrades/initial/ruby.dbscheme
|
||||
qltest:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
slice: ["1/2", "2/2"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: ./ruby/actions/create-extractor-pack
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
codeql test run --threads=0 --ram 5000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test
|
||||
codeql test run --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
- name: Check QL formatting
|
||||
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
|
||||
- name: Check QL compilation
|
||||
run: |
|
||||
codeql query compile --check-only --threads=4 --warnings=error "ql/src" "ql/examples"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
- name: Check DB upgrade scripts
|
||||
run: |
|
||||
echo >empty.trap
|
||||
codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap
|
||||
codeql dataset upgrade testdb --additional-packs ql/lib
|
||||
diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme
|
||||
|
||||
@@ -13,9 +13,6 @@
|
||||
/python/**/experimental/**/* @github/codeql-python @xcorail
|
||||
/ruby/**/experimental/**/* @github/codeql-ruby @xcorail
|
||||
|
||||
# ML-powered queries
|
||||
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
|
||||
|
||||
# Notify members of codeql-go about PRs to the shared data-flow library files
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @github/codeql-java @github/codeql-go
|
||||
@@ -28,6 +25,3 @@
|
||||
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||
/docs/ql-language-reference/ @github/codeql-frontend-reviewers
|
||||
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
|
||||
|
||||
# QL for QL reviewers
|
||||
/ql/ @github/codeql-ql-for-ql-reviewers
|
||||
|
||||
@@ -4,9 +4,6 @@ We welcome contributions to our CodeQL libraries and queries. Got an idea for a
|
||||
|
||||
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
|
||||
|
||||
## Change notes
|
||||
|
||||
Any nontrivial user-visible change to a query pack or library pack should have a change note. For details on how to add a change note for your change, see [this guide](docs/change-notes.md).
|
||||
|
||||
## Submitting a new experimental query
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# CodeQL
|
||||
|
||||
This open source repository contains the standard CodeQL libraries and queries that power [GitHub Advanced Security](https://github.com/features/security/code) and the other application security products that [GitHub](https://github.com/features/security/) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
|
||||
This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.com) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
|
||||
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
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.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension to try out your queries on any open source project that's currently being analyzed.
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -13,7 +13,7 @@ We welcome contributions to our standard library and standard checks. Do you hav
|
||||
|
||||
## License
|
||||
|
||||
The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com). The use of CodeQL on open source code is licensed under specific [Terms & Conditions](https://securitylab.github.com/tools/codeql/license/) UNLESS you have a commercial license in place. If you'd like to use CodeQL with a commercial codebase, please [contact us](https://github.com/enterprise/contact) for further help.
|
||||
The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com).
|
||||
|
||||
## Visual Studio Code integration
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll",
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
||||
@@ -453,15 +452,9 @@
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll"
|
||||
],
|
||||
"CryptoAlgorithms Python/JS/Ruby": [
|
||||
"CryptoAlgorithms Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
|
||||
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/CryptoAlgorithms.qll"
|
||||
],
|
||||
"CryptoAlgorithmNames Python/JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/internal/CryptoAlgorithmNames.qll",
|
||||
"python/ql/lib/semmle/python/concepts/internal/CryptoAlgorithmNames.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/internal/CryptoAlgorithmNames.qll"
|
||||
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll"
|
||||
],
|
||||
"SensitiveDataHeuristics Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Autobuild.Cpp</AssemblyName>
|
||||
<RootNamespace>Semmle.Autobuild.Cpp</RootNamespace>
|
||||
<ApplicationIcon />
|
||||
@@ -17,7 +17,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.Build" Version="16.9.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
@name Badly bounded write (CWE-120)
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-120/OverrunWrite.ql: /CWE/CWE-120
|
||||
@name Potentially overrunning write (CWE-120)
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-120/VeryLikelyOverrunWrite.ql: /CWE/CWE-120
|
||||
@name Likely overrunning write
|
||||
+ semmlecode-cpp-queries/Security/CWE/CWE-120/OverrunWriteFloat.ql: /CWE/CWE-120
|
||||
@name Potentially overrunning write with float to string conversion (CWE-120)
|
||||
+ semmlecode-cpp-queries/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql: /CWE/CWE-120
|
||||
@name Array offset used before range check (CWE-120)
|
||||
+ semmlecode-cpp-queries/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql: /CWE/CWE-120
|
||||
@name Potentially unsafe use of strcat (CWE-120)
|
||||
@name Potentially unsafe use of strcat (CWE-120)
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The "Cleartext transmission of sensitive information" (`cpp/cleartext-transmission`) query has been improved, reducing the number of false positive results when encryption is present.
|
||||
@@ -1,6 +1,4 @@
|
||||
name: codeql/cpp-examples
|
||||
groups:
|
||||
- cpp
|
||||
- examples
|
||||
version: 0.0.2
|
||||
dependencies:
|
||||
codeql/cpp-all: "*"
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
## 0.0.8
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The `codeql/cpp-upgrades` CodeQL pack has been removed. All upgrades scripts have been merged into the `codeql/cpp-all` CodeQL pack.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* `FormatLiteral::getMaxConvertedLength` now uses range analysis to provide a
|
||||
more accurate length for integers formatted with `%x`
|
||||
|
||||
## 0.0.7
|
||||
|
||||
## 0.0.6
|
||||
|
||||
## 0.0.5
|
||||
|
||||
## 0.0.4
|
||||
|
||||
@@ -73,7 +73,7 @@ class Options extends string {
|
||||
* __assume(0);
|
||||
* ```
|
||||
* (note that in this case if the hint is wrong and the expression is reached at
|
||||
* runtime, the program's behavior is undefined)
|
||||
* runtime, the program's behaviour is undefined)
|
||||
*/
|
||||
predicate exprExits(Expr e) {
|
||||
e.(AssumeExpr).getChild(0).(CompileTimeConstantInt).getIntValue() = 0 or
|
||||
|
||||
@@ -50,7 +50,7 @@ class CustomOptions extends Options {
|
||||
* __assume(0);
|
||||
* ```
|
||||
* (note that in this case if the hint is wrong and the expression is reached at
|
||||
* runtime, the program's behavior is undefined)
|
||||
* runtime, the program's behaviour is undefined)
|
||||
*/
|
||||
override predicate exprExits(Expr e) { Options.super.exprExits(e) }
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `cpp/world-writable-file-creation` query now only detects `open` and `openat` calls with the `O_CREAT` or `O_TMPFILE` flag.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query, `cpp/open-call-with-mode-argument`, to detect when `open` or `openat` is called with the `O_CREAT` or `O_TMPFILE` flag but when the `mode` argument is omitted.
|
||||
@@ -1 +0,0 @@
|
||||
## 0.0.7
|
||||
@@ -1,10 +0,0 @@
|
||||
## 0.0.8
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* The `codeql/cpp-upgrades` CodeQL pack has been removed. All upgrades scripts have been merged into the `codeql/cpp-all` CodeQL pack.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* `FormatLiteral::getMaxConvertedLength` now uses range analysis to provide a
|
||||
more accurate length for integers formatted with `%x`
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.8
|
||||
lastReleaseVersion: 0.0.5
|
||||
|
||||
@@ -37,7 +37,7 @@ abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition {
|
||||
* dependencies. Without this information, range analysis might work for
|
||||
* simple cases but will go into infinite loops on complex code.
|
||||
*
|
||||
* For example, when modeling the definition by reference in a call to an
|
||||
* For example, when modelling the definition by reference in a call to an
|
||||
* overloaded `operator=`, written as `v = e`, the definition of `(this, v)`
|
||||
* depends on `e`.
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* `Instruction` level), and then using the array length analysis and the range
|
||||
* analysis together to prove that some of these pointer dereferences are safe.
|
||||
*
|
||||
* The analysis is soundy, i.e. it is sound if no undefined behavior is present
|
||||
* The analysis is soundy, i.e. it is sound if no undefined behaviour is present
|
||||
* in the program.
|
||||
* Furthermore, it crucially depends on the soundiness of the range analysis and
|
||||
* the array length analysis.
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.0.9-dev
|
||||
version: 0.0.6-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/cpp-upgrades: ^0.0.3
|
||||
|
||||
@@ -206,7 +206,9 @@ class Class extends UserType {
|
||||
* it is callable by a particular caller. For C++11, there's also a question
|
||||
* of whether to include members that are defaulted or deleted.
|
||||
*/
|
||||
deprecated predicate hasCopyConstructor() { this.getAMemberFunction() instanceof CopyConstructor }
|
||||
deprecated predicate hasCopyConstructor() {
|
||||
exists(CopyConstructor cc | cc = this.getAMemberFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this class has a copy assignment operator that is either
|
||||
@@ -222,7 +224,7 @@ class Class extends UserType {
|
||||
* or deleted.
|
||||
*/
|
||||
deprecated predicate hasCopyAssignmentOperator() {
|
||||
this.getAMemberFunction() instanceof CopyAssignmentOperator
|
||||
exists(CopyAssignmentOperator coa | coa = this.getAMemberFunction())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -885,7 +887,7 @@ class NestedClass extends Class {
|
||||
* pure virtual function.
|
||||
*/
|
||||
class AbstractClass extends Class {
|
||||
AbstractClass() { this.getAMemberFunction() instanceof PureVirtualFunction }
|
||||
AbstractClass() { exists(PureVirtualFunction f | this.getAMemberFunction() = f) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "AbstractClass" }
|
||||
}
|
||||
|
||||
@@ -286,13 +286,13 @@ class AttributeArgument extends Element, @attribute_arg {
|
||||
override Location getLocation() { attribute_args(underlyingElement(this), _, _, _, result) }
|
||||
|
||||
override string toString() {
|
||||
if underlyingElement(this) instanceof @attribute_arg_empty
|
||||
if exists(@attribute_arg_empty self | self = underlyingElement(this))
|
||||
then result = "empty argument"
|
||||
else
|
||||
exists(string prefix, string tail |
|
||||
(if exists(this.getName()) then prefix = this.getName() + "=" else prefix = "") and
|
||||
(
|
||||
if underlyingElement(this) instanceof @attribute_arg_type
|
||||
if exists(@attribute_arg_type self | self = underlyingElement(this))
|
||||
then tail = this.getValueType().getName()
|
||||
else tail = this.getValueText()
|
||||
) and
|
||||
|
||||
@@ -233,7 +233,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
|
||||
XMLAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
|
||||
|
||||
/** Holds if this XML element has an attribute with the specified `name`. */
|
||||
predicate hasAttribute(string name) { exists(this.getAttribute(name)) }
|
||||
predicate hasAttribute(string name) { exists(XMLAttribute a | a = this.getAttribute(name)) }
|
||||
|
||||
/** Gets the value of the attribute with the specified `name`, if any. */
|
||||
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||
|
||||
@@ -101,21 +101,6 @@ predicate functionArgumentMustBeNullTerminated(Function f, int i) {
|
||||
f instanceof StrcatFunction and i = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `arg` is a string format argument to a formatting function call
|
||||
* `ffc`.
|
||||
*/
|
||||
predicate formatArgumentMustBeNullTerminated(FormattingFunctionCall ffc, Expr arg) {
|
||||
// String argument to a formatting function (such as `printf`)
|
||||
exists(int n, FormatLiteral fl |
|
||||
ffc.getConversionArgument(n) = arg and
|
||||
fl = ffc.getFormat() and
|
||||
fl.getConversionType(n) instanceof PointerType and // `%s`, `%ws` etc
|
||||
not fl.getConversionType(n) instanceof VoidPointerType and // exclude: `%p`
|
||||
not fl.hasPrecision(n) // exclude: `%.*s`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `va` is a variable access where the contents must be null terminated.
|
||||
*/
|
||||
@@ -128,7 +113,13 @@ predicate variableMustBeNullTerminated(VariableAccess va) {
|
||||
)
|
||||
or
|
||||
// String argument to a formatting function (such as `printf`)
|
||||
formatArgumentMustBeNullTerminated(fc, va)
|
||||
exists(int n, FormatLiteral fl |
|
||||
fc.(FormattingFunctionCall).getConversionArgument(n) = va and
|
||||
fl = fc.(FormattingFunctionCall).getFormat() and
|
||||
fl.getConversionType(n) instanceof PointerType and // `%s`, `%ws` etc
|
||||
not fl.getConversionType(n) instanceof VoidPointerType and // exclude: `%p`
|
||||
not fl.hasPrecision(n) // exclude: `%.*s`
|
||||
)
|
||||
or
|
||||
// Call to a wrapper function that requires null termination
|
||||
// (not itself adding a null terminator)
|
||||
|
||||
@@ -9,106 +9,6 @@ import semmle.code.cpp.models.interfaces.FormattingFunction
|
||||
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
private newtype TBufferWriteEstimationReason =
|
||||
TUnspecifiedEstimateReason() or
|
||||
TTypeBoundsAnalysis() or
|
||||
TWidenedValueFlowAnalysis() or
|
||||
TValueFlowAnalysis()
|
||||
|
||||
private predicate gradeToReason(int grade, TBufferWriteEstimationReason reason) {
|
||||
// when combining reasons, lower grade takes precedence
|
||||
grade = 0 and reason = TUnspecifiedEstimateReason()
|
||||
or
|
||||
grade = 1 and reason = TTypeBoundsAnalysis()
|
||||
or
|
||||
grade = 2 and reason = TWidenedValueFlowAnalysis()
|
||||
or
|
||||
grade = 3 and reason = TValueFlowAnalysis()
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for a specific buffer write size estimate.
|
||||
*/
|
||||
abstract class BufferWriteEstimationReason extends TBufferWriteEstimationReason {
|
||||
/**
|
||||
* Returns the name of the concrete class.
|
||||
*/
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Returns a human readable representation of this reason.
|
||||
*/
|
||||
abstract string getDescription();
|
||||
|
||||
/**
|
||||
* Combine estimate reasons. Used to give a reason for the size of a format string
|
||||
* conversion given reasons coming from its individual specifiers.
|
||||
*/
|
||||
BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
|
||||
exists(int grade, int otherGrade |
|
||||
gradeToReason(grade, this) and gradeToReason(otherGrade, other)
|
||||
|
|
||||
if otherGrade < grade then result = other else result = this
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No particular reason given. This is currently used for backward compatibility so that
|
||||
* classes derived from BufferWrite and overriding `getMaxData/0` still work with the
|
||||
* queries as intended.
|
||||
*/
|
||||
class UnspecifiedEstimateReason extends BufferWriteEstimationReason, TUnspecifiedEstimateReason {
|
||||
override string toString() { result = "UnspecifiedEstimateReason" }
|
||||
|
||||
override string getDescription() { result = "no reason specified" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The estimation comes from rough bounds just based on the type (e.g.
|
||||
* `0 <= x < 2^32` for an unsigned 32 bit integer).
|
||||
*/
|
||||
class TypeBoundsAnalysis extends BufferWriteEstimationReason, TTypeBoundsAnalysis {
|
||||
override string toString() { result = "TypeBoundsAnalysis" }
|
||||
|
||||
override string getDescription() { result = "based on type bounds" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The estimation comes from non trivial bounds found via actual flow analysis,
|
||||
* but a widening aproximation might have been used for variables in loops.
|
||||
* For example
|
||||
* ```
|
||||
* for (int i = 0; i < 10; ++i) {
|
||||
* int j = i + i;
|
||||
* //... <- estimation done here based on j
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class WidenedValueFlowAnalysis extends BufferWriteEstimationReason, TWidenedValueFlowAnalysis {
|
||||
override string toString() { result = "WidenedValueFlowAnalysis" }
|
||||
|
||||
override string getDescription() {
|
||||
result = "based on flow analysis of value bounds with a widening approximation"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The estimation comes from non trivial bounds found via actual flow analysis.
|
||||
* For example
|
||||
* ```
|
||||
* unsigned u = x;
|
||||
* if (u < 1000) {
|
||||
* //... <- estimation done here based on u
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ValueFlowAnalysis extends BufferWriteEstimationReason, TValueFlowAnalysis {
|
||||
override string toString() { result = "ValueFlowAnalysis" }
|
||||
|
||||
override string getDescription() { result = "based on flow analysis of value bounds" }
|
||||
}
|
||||
|
||||
class PrintfFormatAttribute extends FormatAttribute {
|
||||
PrintfFormatAttribute() { this.getArchetype() = ["printf", "__printf__"] }
|
||||
}
|
||||
@@ -382,38 +282,6 @@ private int lengthInBase10(float f) {
|
||||
result = f.log10().floor() + 1
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isPointerTypeWithBase(Type base, PointerType pt) { base = pt.getBaseType() }
|
||||
|
||||
bindingset[expr]
|
||||
private BufferWriteEstimationReason getEstimationReasonForIntegralExpression(Expr expr) {
|
||||
// we consider the range analysis non trivial if it
|
||||
// * constrained non-trivially both sides of a signed value, or
|
||||
// * constrained non-trivially the positive side of an unsigned value
|
||||
// expr should already be given as getFullyConverted
|
||||
if
|
||||
upperBound(expr) < exprMaxVal(expr) and
|
||||
(exprMinVal(expr) >= 0 or lowerBound(expr) > exprMinVal(expr))
|
||||
then
|
||||
// next we check whether the estimate may have been widened
|
||||
if upperBoundMayBeWidened(expr)
|
||||
then result = TWidenedValueFlowAnalysis()
|
||||
else result = TValueFlowAnalysis()
|
||||
else result = TTypeBoundsAnalysis()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of hex digits required to represent the integer represented by `f`.
|
||||
*
|
||||
* `f` is assumed to be nonnegative.
|
||||
*/
|
||||
bindingset[f]
|
||||
private int lengthInBase16(float f) {
|
||||
f = 0 and result = 1
|
||||
or
|
||||
result = (f.log2() / 4.0).floor() + 1
|
||||
}
|
||||
|
||||
/**
|
||||
* A class to represent format strings that occur as arguments to invocations of formatting functions.
|
||||
*/
|
||||
@@ -965,19 +833,19 @@ class FormatLiteral extends Literal {
|
||||
(
|
||||
conv = ["s", "S"] and
|
||||
len = "h" and
|
||||
isPointerTypeWithBase(any(PlainCharType plainCharType), result)
|
||||
result.(PointerType).getBaseType() instanceof PlainCharType
|
||||
or
|
||||
conv = ["s", "S"] and
|
||||
len = ["l", "w"] and
|
||||
isPointerTypeWithBase(this.getWideCharType(), result)
|
||||
result.(PointerType).getBaseType() = this.getWideCharType()
|
||||
or
|
||||
conv = "s" and
|
||||
(len != "l" and len != "w" and len != "h") and
|
||||
isPointerTypeWithBase(this.getDefaultCharType(), result)
|
||||
result.(PointerType).getBaseType() = this.getDefaultCharType()
|
||||
or
|
||||
conv = "S" and
|
||||
(len != "l" and len != "w" and len != "h") and
|
||||
isPointerTypeWithBase(this.getNonDefaultCharType(), result)
|
||||
result.(PointerType).getBaseType() = this.getNonDefaultCharType()
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1122,14 +990,7 @@ class FormatLiteral extends Literal {
|
||||
* conversion specifier of this format string; has no result if this cannot
|
||||
* be determined.
|
||||
*/
|
||||
int getMaxConvertedLength(int n) { result = max(this.getMaxConvertedLength(n, _)) }
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by the nth
|
||||
* conversion specifier of this format string, specifying the estimation reason;
|
||||
* has no result if this cannot be determined.
|
||||
*/
|
||||
int getMaxConvertedLength(int n, BufferWriteEstimationReason reason) {
|
||||
int getMaxConvertedLength(int n) {
|
||||
exists(int len |
|
||||
(
|
||||
(
|
||||
@@ -1141,12 +1002,10 @@ class FormatLiteral extends Literal {
|
||||
) and
|
||||
(
|
||||
this.getConversionChar(n) = "%" and
|
||||
len = 1 and
|
||||
reason = TValueFlowAnalysis()
|
||||
len = 1
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "c" and
|
||||
len = 1 and
|
||||
reason = TValueFlowAnalysis() // e.g. 'a'
|
||||
len = 1 // e.g. 'a'
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "f" and
|
||||
exists(int dot, int afterdot |
|
||||
@@ -1160,8 +1019,7 @@ class FormatLiteral extends Literal {
|
||||
afterdot = 6
|
||||
) and
|
||||
len = 1 + 309 + dot + afterdot
|
||||
) and
|
||||
reason = TTypeBoundsAnalysis() // e.g. -1e308="-100000"...
|
||||
) // e.g. -1e308="-100000"...
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "e" and
|
||||
exists(int dot, int afterdot |
|
||||
@@ -1175,8 +1033,7 @@ class FormatLiteral extends Literal {
|
||||
afterdot = 6
|
||||
) and
|
||||
len = 1 + 1 + dot + afterdot + 1 + 1 + 3
|
||||
) and
|
||||
reason = TTypeBoundsAnalysis() // -1e308="-1.000000e+308"
|
||||
) // -1e308="-1.000000e+308"
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "g" and
|
||||
exists(int dot, int afterdot |
|
||||
@@ -1199,110 +1056,85 @@ class FormatLiteral extends Literal {
|
||||
// (e.g. 123456, 0.000123456 are just OK)
|
||||
// so case %f can be at most P characters + 4 zeroes, sign, dot = P + 6
|
||||
len = (afterdot.maximum(1) + 6).maximum(1 + 1 + dot + afterdot + 1 + 1 + 3)
|
||||
) and
|
||||
reason = TTypeBoundsAnalysis() // (e.g. "-1.59203e-319")
|
||||
) // (e.g. "-1.59203e-319")
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = ["d", "i"] and
|
||||
// e.g. -2^31 = "-2147483648"
|
||||
exists(float typeBasedBound, float valueBasedBound |
|
||||
// The first case handles length sub-specifiers
|
||||
// Subtract one in the exponent because one bit is for the sign.
|
||||
// Add 1 to account for the possible sign in the output.
|
||||
typeBasedBound =
|
||||
1 + lengthInBase10(2.pow(this.getIntegralDisplayType(n).getSize() * 8 - 1)) and
|
||||
// The second case uses range analysis to deduce a length that's shorter than the length
|
||||
// of the number -2^31.
|
||||
exists(Expr arg, float lower, float upper |
|
||||
arg = this.getUse().getConversionArgument(n) and
|
||||
lower = lowerBound(arg.getFullyConverted()) and
|
||||
upper = upperBound(arg.getFullyConverted())
|
||||
|
|
||||
valueBasedBound =
|
||||
max(int cand |
|
||||
// Include the sign bit in the length if it can be negative
|
||||
(
|
||||
if lower < 0
|
||||
then cand = 1 + lengthInBase10(lower.abs())
|
||||
else cand = lengthInBase10(lower)
|
||||
len =
|
||||
min(float cand |
|
||||
// The first case handles length sub-specifiers
|
||||
// Subtract one in the exponent because one bit is for the sign.
|
||||
// Add 1 to account for the possible sign in the output.
|
||||
cand = 1 + lengthInBase10(2.pow(this.getIntegralDisplayType(n).getSize() * 8 - 1))
|
||||
or
|
||||
// The second case uses range analysis to deduce a length that's shorter than the length
|
||||
// of the number -2^31.
|
||||
exists(Expr arg, float lower, float upper |
|
||||
arg = this.getUse().getConversionArgument(n) and
|
||||
lower = lowerBound(arg.getFullyConverted()) and
|
||||
upper = upperBound(arg.getFullyConverted())
|
||||
|
|
||||
cand =
|
||||
max(int cand0 |
|
||||
// Include the sign bit in the length if it can be negative
|
||||
(
|
||||
if lower < 0
|
||||
then cand0 = 1 + lengthInBase10(lower.abs())
|
||||
else cand0 = lengthInBase10(lower)
|
||||
)
|
||||
or
|
||||
(
|
||||
if upper < 0
|
||||
then cand0 = 1 + lengthInBase10(upper.abs())
|
||||
else cand0 = lengthInBase10(upper)
|
||||
)
|
||||
)
|
||||
or
|
||||
(
|
||||
if upper < 0
|
||||
then cand = 1 + lengthInBase10(upper.abs())
|
||||
else cand = lengthInBase10(upper)
|
||||
)
|
||||
) and
|
||||
// we don't want to call this on `arg.getFullyConverted()` as we want
|
||||
// to detect non-trivial range analysis without taking into account up-casting
|
||||
reason = getEstimationReasonForIntegralExpression(arg)
|
||||
) and
|
||||
len = valueBasedBound.minimum(typeBasedBound)
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "u" and
|
||||
// e.g. 2^32 - 1 = "4294967295"
|
||||
exists(float typeBasedBound, float valueBasedBound |
|
||||
// The first case handles length sub-specifiers
|
||||
typeBasedBound = lengthInBase10(2.pow(this.getIntegralDisplayType(n).getSize() * 8) - 1) and
|
||||
// The second case uses range analysis to deduce a length that's shorter than
|
||||
// the length of the number 2^31 - 1.
|
||||
exists(Expr arg, float lower, float upper |
|
||||
arg = this.getUse().getConversionArgument(n) and
|
||||
lower = lowerBound(arg.getFullyConverted()) and
|
||||
upper = upperBound(arg.getFullyConverted())
|
||||
|
|
||||
valueBasedBound =
|
||||
lengthInBase10(max(float cand |
|
||||
len =
|
||||
min(float cand |
|
||||
// The first case handles length sub-specifiers
|
||||
cand = 2.pow(this.getIntegralDisplayType(n).getSize() * 8)
|
||||
or
|
||||
// The second case uses range analysis to deduce a length that's shorter than
|
||||
// the length of the number 2^31 - 1.
|
||||
exists(Expr arg, float lower |
|
||||
arg = this.getUse().getConversionArgument(n) and
|
||||
lower = lowerBound(arg.getFullyConverted())
|
||||
|
|
||||
cand =
|
||||
max(float cand0 |
|
||||
// If lower can be negative we use `(unsigned)-1` as the candidate value.
|
||||
lower < 0 and
|
||||
cand = 2.pow(any(IntType t | t.isUnsigned()).getSize() * 8)
|
||||
cand0 = 2.pow(any(IntType t | t.isUnsigned()).getSize() * 8)
|
||||
or
|
||||
cand = upper
|
||||
)) and
|
||||
// we don't want to call this on `arg.getFullyConverted()` as we want
|
||||
// to detect non-trivial range analysis without taking into account up-casting
|
||||
reason = getEstimationReasonForIntegralExpression(arg)
|
||||
) and
|
||||
len = valueBasedBound.minimum(typeBasedBound)
|
||||
)
|
||||
cand0 = upperBound(arg.getFullyConverted())
|
||||
)
|
||||
)
|
||||
|
|
||||
lengthInBase10(cand)
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "x" and
|
||||
// e.g. "12345678"
|
||||
exists(int baseLen, int typeBasedBound, int valueBasedBound |
|
||||
typeBasedBound =
|
||||
min(int digits |
|
||||
digits = 2 * this.getIntegralDisplayType(n).getSize()
|
||||
exists(int sizeBytes, int baseLen |
|
||||
sizeBytes =
|
||||
min(int bytes |
|
||||
bytes = this.getIntegralDisplayType(n).getSize()
|
||||
or
|
||||
exists(IntegralType t |
|
||||
t = this.getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||
|
|
||||
t.isUnsigned() and
|
||||
digits = 2 * t.getSize()
|
||||
t.isUnsigned() and bytes = t.getSize()
|
||||
)
|
||||
) and
|
||||
exists(Expr arg, float lower, float upper, float typeLower, float typeUpper |
|
||||
arg = this.getUse().getConversionArgument(n) and
|
||||
lower = lowerBound(arg.getFullyConverted()) and
|
||||
upper = upperBound(arg.getFullyConverted()) and
|
||||
typeLower = exprMinVal(arg.getFullyConverted()) and
|
||||
typeUpper = exprMaxVal(arg.getFullyConverted())
|
||||
|
|
||||
valueBasedBound =
|
||||
lengthInBase16(max(float cand |
|
||||
// If lower can be negative we use `(unsigned)-1` as the candidate value.
|
||||
lower < 0 and
|
||||
cand = 2.pow(any(IntType t | t.isUnsigned()).getSize() * 8)
|
||||
or
|
||||
cand = upper
|
||||
)) and
|
||||
(
|
||||
if lower > typeLower or upper < typeUpper
|
||||
then reason = TValueFlowAnalysis()
|
||||
else reason = TTypeBoundsAnalysis()
|
||||
)
|
||||
) and
|
||||
baseLen = valueBasedBound.minimum(typeBasedBound) and
|
||||
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||
baseLen = sizeBytes * 2 and
|
||||
(
|
||||
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||
)
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "p" and
|
||||
@@ -1312,8 +1144,7 @@ class FormatLiteral extends Literal {
|
||||
(
|
||||
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||
)
|
||||
) and
|
||||
reason = TValueFlowAnalysis()
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "o" and
|
||||
// e.g. 2^32 - 1 = "37777777777"
|
||||
@@ -1332,16 +1163,14 @@ class FormatLiteral extends Literal {
|
||||
(
|
||||
if this.hasAlternateFlag(n) then len = 1 + baseLen else len = baseLen // "0"
|
||||
)
|
||||
) and
|
||||
reason = TTypeBoundsAnalysis()
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "s" and
|
||||
len =
|
||||
min(int v |
|
||||
v = this.getPrecision(n) or
|
||||
v = this.getUse().getFormatArgument(n).(AnalysedString).getMaxLength() - 1 // (don't count null terminator)
|
||||
) and
|
||||
reason = TValueFlowAnalysis()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1353,19 +1182,10 @@ class FormatLiteral extends Literal {
|
||||
* determining whether a buffer overflow is caused by long float to string
|
||||
* conversions.
|
||||
*/
|
||||
int getMaxConvertedLengthLimited(int n) { result = max(this.getMaxConvertedLengthLimited(n, _)) }
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by the nth
|
||||
* conversion specifier of this format string, specifying the reason for the
|
||||
* estimation, except that float to string conversions are assumed to be 8
|
||||
* characters. This is helpful for determining whether a buffer overflow is
|
||||
* caused by long float to string conversions.
|
||||
*/
|
||||
int getMaxConvertedLengthLimited(int n, BufferWriteEstimationReason reason) {
|
||||
int getMaxConvertedLengthLimited(int n) {
|
||||
if this.getConversionChar(n).toLowerCase() = "f"
|
||||
then result = this.getMaxConvertedLength(n, reason).minimum(8)
|
||||
else result = this.getMaxConvertedLength(n, reason)
|
||||
then result = this.getMaxConvertedLength(n).minimum(8)
|
||||
else result = this.getMaxConvertedLength(n)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1405,35 +1225,29 @@ class FormatLiteral extends Literal {
|
||||
)
|
||||
}
|
||||
|
||||
private int getMaxConvertedLengthAfter(int n, BufferWriteEstimationReason reason) {
|
||||
private int getMaxConvertedLengthAfter(int n) {
|
||||
if n = this.getNumConvSpec()
|
||||
then result = this.getConstantSuffix().length() + 1 and reason = TValueFlowAnalysis()
|
||||
then result = this.getConstantSuffix().length() + 1
|
||||
else
|
||||
exists(BufferWriteEstimationReason headReason, BufferWriteEstimationReason tailReason |
|
||||
result =
|
||||
this.getConstantPart(n).length() + this.getMaxConvertedLength(n, headReason) +
|
||||
this.getMaxConvertedLengthAfter(n + 1, tailReason) and
|
||||
reason = headReason.combineWith(tailReason)
|
||||
)
|
||||
result =
|
||||
this.getConstantPart(n).length() + this.getMaxConvertedLength(n) +
|
||||
this.getMaxConvertedLengthAfter(n + 1)
|
||||
}
|
||||
|
||||
private int getMaxConvertedLengthAfterLimited(int n, BufferWriteEstimationReason reason) {
|
||||
private int getMaxConvertedLengthAfterLimited(int n) {
|
||||
if n = this.getNumConvSpec()
|
||||
then result = this.getConstantSuffix().length() + 1 and reason = TValueFlowAnalysis()
|
||||
then result = this.getConstantSuffix().length() + 1
|
||||
else
|
||||
exists(BufferWriteEstimationReason headReason, BufferWriteEstimationReason tailReason |
|
||||
result =
|
||||
this.getConstantPart(n).length() + this.getMaxConvertedLengthLimited(n, headReason) +
|
||||
this.getMaxConvertedLengthAfterLimited(n + 1, tailReason) and
|
||||
reason = headReason.combineWith(tailReason)
|
||||
)
|
||||
result =
|
||||
this.getConstantPart(n).length() + this.getMaxConvertedLengthLimited(n) +
|
||||
this.getMaxConvertedLengthAfterLimited(n + 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by this format
|
||||
* string. Has no result if this cannot be determined.
|
||||
*/
|
||||
int getMaxConvertedLength() { result = this.getMaxConvertedLengthAfter(0, _) }
|
||||
int getMaxConvertedLength() { result = this.getMaxConvertedLengthAfter(0) }
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by this format
|
||||
@@ -1441,24 +1255,5 @@ class FormatLiteral extends Literal {
|
||||
* characters. This is helpful for determining whether a buffer overflow
|
||||
* is caused by long float to string conversions.
|
||||
*/
|
||||
int getMaxConvertedLengthLimited() { result = this.getMaxConvertedLengthAfterLimited(0, _) }
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by this format
|
||||
* string, specifying the reason for the estimate. Has no result if no estimate
|
||||
* can be found.
|
||||
*/
|
||||
int getMaxConvertedLengthWithReason(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxConvertedLengthAfter(0, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by this format
|
||||
* string, specifying the reason for the estimate, except that float to string
|
||||
* conversions are assumed to be 8 characters. This is helpful for determining
|
||||
* whether a buffer overflow is caused by long float to string conversions.
|
||||
*/
|
||||
int getMaxConvertedLengthLimitedWithReason(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxConvertedLengthAfterLimited(0, reason)
|
||||
}
|
||||
int getMaxConvertedLengthLimited() { result = this.getMaxConvertedLengthAfterLimited(0) }
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ import cpp
|
||||
*/
|
||||
bindingset[input]
|
||||
int parseOctal(string input) {
|
||||
input.regexpMatch("0[0-7]+") and
|
||||
input.charAt(0) = "0" and
|
||||
result =
|
||||
strictsum(int ix |
|
||||
ix in [1 .. input.length()]
|
||||
ix in [0 .. input.length()]
|
||||
|
|
||||
8.pow(input.length() - (ix + 1)) * input.charAt(ix).toInt()
|
||||
)
|
||||
|
||||
@@ -29,7 +29,7 @@ class GuardCondition extends Expr {
|
||||
exists(IRGuardCondition ir | this = ir.getUnconvertedResultExpression())
|
||||
or
|
||||
// no binary operators in the IR
|
||||
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
||||
exists(GuardCondition gc | this.(BinaryLogicalOperation).getAnOperand() = gc)
|
||||
or
|
||||
// the IR short-circuits if(!x)
|
||||
// don't produce a guard condition for `y = !x` and other non-short-circuited cases
|
||||
@@ -98,7 +98,7 @@ class GuardCondition extends Expr {
|
||||
*/
|
||||
private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||
GuardConditionFromBinaryLogicalOperator() {
|
||||
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
||||
exists(GuardCondition gc | this.(BinaryLogicalOperation).getAnOperand() = gc)
|
||||
}
|
||||
|
||||
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
|
||||
|
||||
@@ -153,11 +153,9 @@ library class SSAHelper extends int {
|
||||
* Modern Compiler Implementation by Andrew Appel.
|
||||
*/
|
||||
private predicate frontier_phi_node(StackVariable v, BasicBlock b) {
|
||||
exists(BasicBlock x |
|
||||
dominanceFrontier(x, b) and ssa_defn_rec(pragma[only_bind_into](v), pragma[only_bind_into](x))
|
||||
) and
|
||||
exists(BasicBlock x | dominanceFrontier(x, b) and ssa_defn_rec(v, x)) and
|
||||
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
|
||||
live_at_start_of_bb(pragma[only_bind_into](v), b)
|
||||
live_at_start_of_bb(v, b)
|
||||
}
|
||||
|
||||
private predicate ssa_defn_rec(StackVariable v, BasicBlock b) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,17 +3,6 @@ private import DataFlowImplSpecific::Public
|
||||
import Cached
|
||||
|
||||
module DataFlowImplCommonPublic {
|
||||
/** A state value to track during data flow. */
|
||||
class FlowState = string;
|
||||
|
||||
/**
|
||||
* The default state, which is used when the state is unspecified for a source
|
||||
* or a sink.
|
||||
*/
|
||||
class FlowStateEmpty extends FlowState {
|
||||
FlowStateEmpty() { this = "" }
|
||||
}
|
||||
|
||||
private newtype TFlowFeature =
|
||||
TFeatureHasSourceCallContext() or
|
||||
TFeatureHasSinkCallContext() or
|
||||
@@ -1290,7 +1279,7 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
/** Content tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
|
||||
@@ -15,23 +15,11 @@ module Consistency {
|
||||
class ConsistencyConfiguration extends TConsistencyConfiguration {
|
||||
string toString() { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
|
||||
predicate uniqueEnclosingCallableExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
|
||||
predicate uniqueNodeLocationExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `missingLocation`. */
|
||||
predicate missingLocationExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
|
||||
predicate postWithInFlowExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
|
||||
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
|
||||
predicate reverseReadExclude(Node n) { none() }
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -58,7 +46,6 @@ module Consistency {
|
||||
n instanceof RelevantNode and
|
||||
c = count(nodeGetEnclosingCallable(n)) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
}
|
||||
@@ -79,7 +66,6 @@ module Consistency {
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
|
||||
msg = "Node should have one location but has " + c + "."
|
||||
)
|
||||
}
|
||||
@@ -90,8 +76,7 @@ module Consistency {
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
||||
)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
)
|
||||
@@ -187,7 +172,6 @@ module Consistency {
|
||||
|
||||
query predicate reverseRead(Node n, string msg) {
|
||||
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
|
||||
not any(ConsistencyConfiguration conf).reverseReadExclude(n) and
|
||||
msg = "Origin of readStep is missing a PostUpdateNode."
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,7 +48,7 @@ private class Argument extends Expr {
|
||||
*/
|
||||
class ArgumentNode extends Node {
|
||||
ArgumentNode() {
|
||||
this.asExpr() instanceof Argument or
|
||||
exists(Argument arg | this.asExpr() = arg) or
|
||||
this = getInstanceArgument(_)
|
||||
}
|
||||
|
||||
|
||||
@@ -592,14 +592,12 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
|
||||
|
||||
/**
|
||||
|
||||
@@ -435,7 +435,7 @@ module FlowVar_internal {
|
||||
parameterIsNonConstReference(p) and
|
||||
p = v and
|
||||
// This definition reaches the exit node of the function CFG
|
||||
getAReachedBlockVarSBB(this).getEnd() = p.getFunction()
|
||||
getAReachedBlockVarSBB(this).getANode() = p.getFunction()
|
||||
}
|
||||
|
||||
override predicate definedByInitialValue(StackVariable lsv) {
|
||||
|
||||
@@ -47,12 +47,6 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { n
|
||||
*/
|
||||
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `guard` should be a sanitizer guard in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
*/
|
||||
predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
|
||||
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
|
||||
@@ -124,14 +118,12 @@ predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeT
|
||||
* Holds if taint may propagate from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localExprTaint(Expr e1, Expr e2) {
|
||||
localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
@@ -69,7 +69,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
@@ -93,7 +93,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,7 +61,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
@@ -69,7 +69,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
@@ -93,7 +93,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -84,8 +84,8 @@ class VariableAccess extends Access, @varaccess {
|
||||
exists(Assignment a | a.getLValue() = this) or
|
||||
exists(CrementOperation c | c.getOperand() = this) or
|
||||
exists(AddressOfExpr addof | addof.getOperand() = this) or
|
||||
this.getConversion() instanceof ReferenceToExpr or
|
||||
this.getConversion() instanceof ArrayToPointerConversion
|
||||
exists(ReferenceToExpr rte | this.getConversion() = rte) or
|
||||
exists(ArrayToPointerConversion atpc | this.getConversion() = atpc)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -104,8 +104,8 @@ class VariableAccess extends Access, @varaccess {
|
||||
predicate isRValue() {
|
||||
not exists(AssignExpr ae | ae.getLValue() = this) and
|
||||
not exists(AddressOfExpr addof | addof.getOperand() = this) and
|
||||
not this.getConversion() instanceof ReferenceToExpr and
|
||||
not this.getConversion() instanceof ArrayToPointerConversion
|
||||
not exists(ReferenceToExpr rte | this.getConversion() = rte) and
|
||||
not exists(ArrayToPointerConversion atpc | this.getConversion() = atpc)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +218,9 @@ class PointerFieldAccess extends FieldAccess {
|
||||
class DotFieldAccess extends FieldAccess {
|
||||
override string getAPrimaryQlClass() { result = "DotFieldAccess" }
|
||||
|
||||
DotFieldAccess() { this.getQualifier().getFullyConverted().getUnspecifiedType() instanceof Class }
|
||||
DotFieldAccess() {
|
||||
exists(Class c | c = this.getQualifier().getFullyConverted().getUnspecifiedType())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,7 @@ class Call extends Expr, NameQualifiableElement, TCall {
|
||||
*
|
||||
* For example, `ptr->f()` has a qualifier, whereas plain `f()` does not.
|
||||
*/
|
||||
predicate hasQualifier() { exists(this.getChild(-1)) }
|
||||
predicate hasQualifier() { exists(Expr e | this.getChild(-1) = e) }
|
||||
|
||||
/**
|
||||
* Gets the expression to the left of the function name or function pointer variable name.
|
||||
|
||||
@@ -724,7 +724,7 @@ class SizeofOperator extends Expr, @runtime_sizeof {
|
||||
* ```
|
||||
*/
|
||||
class SizeofExprOperator extends SizeofOperator {
|
||||
SizeofExprOperator() { exists(this.getChild(0)) }
|
||||
SizeofExprOperator() { exists(Expr e | this.getChild(0) = e) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SizeofExprOperator" }
|
||||
|
||||
@@ -787,7 +787,7 @@ class AlignofOperator extends Expr, @runtime_alignof {
|
||||
* ```
|
||||
*/
|
||||
class AlignofExprOperator extends AlignofOperator {
|
||||
AlignofExprOperator() { exists(this.getChild(0)) }
|
||||
AlignofExprOperator() { exists(Expr e | this.getChild(0) = e) }
|
||||
|
||||
/**
|
||||
* Gets the contained expression.
|
||||
|
||||
@@ -118,7 +118,7 @@ class LambdaCapture extends Locatable, @lambdacapture {
|
||||
* An identifier is captured by reference if:
|
||||
* - It is explicitly captured by reference.
|
||||
* - It is implicitly captured, and the lambda's default capture mode is by-reference.
|
||||
* - The identifier is "this". [Said behavior is dictated by the C++11 standard, but it
|
||||
* - The identifier is "this". [Said behaviour is dictated by the C++11 standard, but it
|
||||
* is actually "*this" being captured rather than "this".]
|
||||
*/
|
||||
predicate isCapturedByReference() { lambda_capture(this, _, _, _, true, _, _) }
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
/**
|
||||
* An IR taint tracking library that uses an IR DataFlow configuration to track
|
||||
* taint from user inputs as defined by `semmle.code.cpp.security.Security`.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
|
||||
@@ -63,7 +63,7 @@ private module VirtualDispatch {
|
||||
this.flowsFrom(other, allowOtherFromArg)
|
||||
|
|
||||
// Call argument
|
||||
exists(DataFlowCall call, Position i |
|
||||
exists(DataFlowCall call, int i |
|
||||
other
|
||||
.(DataFlow::ParameterNode)
|
||||
.isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and
|
||||
@@ -268,6 +268,16 @@ Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) {
|
||||
)
|
||||
}
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition extends int {
|
||||
ParameterPosition() { any(ParameterNode p).isParameterOf(_, this) }
|
||||
}
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition extends int {
|
||||
ArgumentPosition() { any(ArgumentNode a).argumentOf(_, this) }
|
||||
}
|
||||
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,17 +3,6 @@ private import DataFlowImplSpecific::Public
|
||||
import Cached
|
||||
|
||||
module DataFlowImplCommonPublic {
|
||||
/** A state value to track during data flow. */
|
||||
class FlowState = string;
|
||||
|
||||
/**
|
||||
* The default state, which is used when the state is unspecified for a source
|
||||
* or a sink.
|
||||
*/
|
||||
class FlowStateEmpty extends FlowState {
|
||||
FlowStateEmpty() { this = "" }
|
||||
}
|
||||
|
||||
private newtype TFlowFeature =
|
||||
TFeatureHasSourceCallContext() or
|
||||
TFeatureHasSinkCallContext() or
|
||||
@@ -1290,7 +1279,7 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
/** Content tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
|
||||
@@ -15,23 +15,11 @@ module Consistency {
|
||||
class ConsistencyConfiguration extends TConsistencyConfiguration {
|
||||
string toString() { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
|
||||
predicate uniqueEnclosingCallableExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
|
||||
predicate uniqueNodeLocationExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `missingLocation`. */
|
||||
predicate missingLocationExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
|
||||
predicate postWithInFlowExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
|
||||
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
|
||||
predicate reverseReadExclude(Node n) { none() }
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -58,7 +46,6 @@ module Consistency {
|
||||
n instanceof RelevantNode and
|
||||
c = count(nodeGetEnclosingCallable(n)) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
}
|
||||
@@ -79,7 +66,6 @@ module Consistency {
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
|
||||
msg = "Node should have one location but has " + c + "."
|
||||
)
|
||||
}
|
||||
@@ -90,8 +76,7 @@ module Consistency {
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
||||
)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
)
|
||||
@@ -187,7 +172,6 @@ module Consistency {
|
||||
|
||||
query predicate reverseRead(Node n, string msg) {
|
||||
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
|
||||
not any(ConsistencyConfiguration conf).reverseReadExclude(n) and
|
||||
msg = "Origin of readStep is missing a PostUpdateNode."
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ abstract class ArgumentNode extends OperandNode {
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
|
||||
abstract predicate argumentOf(DataFlowCall call, int pos);
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
@@ -42,9 +42,7 @@ private class PrimaryArgumentNode extends ArgumentNode {
|
||||
|
||||
PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
op = call.getArgumentOperand(pos.(DirectPosition).getIndex())
|
||||
}
|
||||
override predicate argumentOf(DataFlowCall call, int pos) { op = call.getArgumentOperand(pos) }
|
||||
|
||||
override string toString() {
|
||||
exists(Expr unconverted |
|
||||
@@ -73,9 +71,9 @@ private class SideEffectArgumentNode extends ArgumentNode {
|
||||
|
||||
SideEffectArgumentNode() { op = read.getSideEffectOperand() }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
override predicate argumentOf(DataFlowCall call, int pos) {
|
||||
read.getPrimaryInstruction() = call and
|
||||
pos.(IndirectionPosition).getIndex() = read.getIndex()
|
||||
pos = getArgumentPosOfSideEffect(read.getIndex())
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
@@ -92,54 +90,6 @@ private class SideEffectArgumentNode extends ArgumentNode {
|
||||
}
|
||||
}
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition = Position;
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition = Position;
|
||||
|
||||
class Position extends TPosition {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
class DirectPosition extends TDirectPosition {
|
||||
int index;
|
||||
|
||||
DirectPosition() { this = TDirectPosition(index) }
|
||||
|
||||
string toString() {
|
||||
index = -1 and
|
||||
result = "this"
|
||||
or
|
||||
index != -1 and
|
||||
result = index.toString()
|
||||
}
|
||||
|
||||
int getIndex() { result = index }
|
||||
}
|
||||
|
||||
class IndirectionPosition extends TIndirectionPosition {
|
||||
int index;
|
||||
|
||||
IndirectionPosition() { this = TIndirectionPosition(index) }
|
||||
|
||||
string toString() {
|
||||
index = -1 and
|
||||
result = "this"
|
||||
or
|
||||
index != -1 and
|
||||
result = index.toString()
|
||||
}
|
||||
|
||||
int getIndex() { result = index }
|
||||
}
|
||||
|
||||
newtype TPosition =
|
||||
TDirectPosition(int index) { exists(any(CallInstruction c).getArgument(index)) } or
|
||||
TIndirectionPosition(int index) {
|
||||
exists(ReadSideEffectInstruction instr | instr.getIndex() = index)
|
||||
}
|
||||
|
||||
private newtype TReturnKind =
|
||||
TNormalReturnKind() or
|
||||
TIndirectReturnKind(ParameterIndex index)
|
||||
|
||||
@@ -490,6 +490,19 @@ class ExprNode extends InstructionNode {
|
||||
override string toString() { result = this.asConvertedExpr().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use. Translates a parameter/argument index into a negative
|
||||
* number that denotes the index of its side effect (pointer indirection).
|
||||
*/
|
||||
bindingset[index]
|
||||
int getArgumentPosOfSideEffect(int index) {
|
||||
// -1 -> -2
|
||||
// 0 -> -3
|
||||
// 1 -> -4
|
||||
// ...
|
||||
result = -3 - index
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph. This includes both explicit parameters such as `x` in `f(x)`
|
||||
@@ -512,7 +525,7 @@ class ParameterNode extends InstructionNode {
|
||||
* implicit `this` parameter is considered to have position `-1`, and
|
||||
* pointer-indirection parameters are at further negative positions.
|
||||
*/
|
||||
predicate isParameterOf(Function f, ParameterPosition pos) { none() } // overridden by subclasses
|
||||
predicate isParameterOf(Function f, int pos) { none() } // overridden by subclasses
|
||||
}
|
||||
|
||||
/** An explicit positional parameter, not including `this` or `...`. */
|
||||
@@ -521,8 +534,8 @@ private class ExplicitParameterNode extends ParameterNode {
|
||||
|
||||
ExplicitParameterNode() { exists(instr.getParameter()) }
|
||||
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
f.getParameter(pos.(DirectPosition).getIndex()) = instr.getParameter()
|
||||
override predicate isParameterOf(Function f, int pos) {
|
||||
f.getParameter(pos) = instr.getParameter()
|
||||
}
|
||||
|
||||
/** Gets the `Parameter` associated with this node. */
|
||||
@@ -537,8 +550,8 @@ class ThisParameterNode extends ParameterNode {
|
||||
|
||||
ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable }
|
||||
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
pos.(DirectPosition).getIndex() = -1 and instr.getEnclosingFunction() = f
|
||||
override predicate isParameterOf(Function f, int pos) {
|
||||
pos = -1 and instr.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
override string toString() { result = "this" }
|
||||
@@ -548,12 +561,12 @@ class ThisParameterNode extends ParameterNode {
|
||||
class ParameterIndirectionNode extends ParameterNode {
|
||||
override InitializeIndirectionInstruction instr;
|
||||
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
override predicate isParameterOf(Function f, int pos) {
|
||||
exists(int index |
|
||||
instr.getEnclosingFunction() = f and
|
||||
instr.hasIndex(index)
|
||||
|
|
||||
pos.(IndirectionPosition).getIndex() = index
|
||||
pos = getArgumentPosOfSideEffect(index)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1032,14 +1045,12 @@ SideEffectInstruction getSideEffectFor(CallInstruction call, int argument) {
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `i1` to `i2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localInstructionFlow(Instruction e1, Instruction e2) {
|
||||
localFlow(instructionNode(e1), instructionNode(e2))
|
||||
}
|
||||
@@ -1048,7 +1059,6 @@ predicate localInstructionFlow(Instruction e1, Instruction e2) {
|
||||
* Holds if data can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) }
|
||||
|
||||
private newtype TContent =
|
||||
|
||||
@@ -659,15 +659,4 @@ module Consistency {
|
||||
not phiHasInputFromBlock(_, def, _) and
|
||||
not uncertainWriteDefinitionInput(_, def)
|
||||
}
|
||||
|
||||
query predicate notDominatedByDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef) |
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
(bb != bbDef or i < iDef)
|
||||
or
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
not ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,16 @@ private newtype TDefOrUse =
|
||||
TExplicitUse(Operand op) { isExplicitUse(op) } or
|
||||
TReturnParamIndirection(Operand op) { returnParameterIndirection(op, _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private int getRank(DefOrUse defOrUse, IRBlock block) {
|
||||
defOrUse =
|
||||
rank[result](int i, DefOrUse cand |
|
||||
block.getInstruction(i) = toInstruction(cand)
|
||||
|
|
||||
cand order by i
|
||||
)
|
||||
}
|
||||
|
||||
private class DefOrUse extends TDefOrUse {
|
||||
/** Gets the instruction associated with this definition, if any. */
|
||||
Instruction asDef() { none() }
|
||||
@@ -64,10 +74,9 @@ private class DefOrUse extends TDefOrUse {
|
||||
/** Gets the block of this definition or use. */
|
||||
abstract IRBlock getBlock();
|
||||
|
||||
/** Holds if this definition or use has index `index` in block `block`. */
|
||||
final predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
block.getInstruction(index) = toInstruction(this)
|
||||
}
|
||||
/** Holds if this definition or use has rank `rank` in block `block`. */
|
||||
cached
|
||||
final predicate hasRankInBlock(IRBlock block, int rnk) { rnk = getRank(this, block) }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
abstract Cpp::Location getLocation();
|
||||
@@ -170,16 +179,10 @@ private class ReturnParameterIndirection extends Use, TReturnParamIndirection {
|
||||
}
|
||||
|
||||
private predicate isExplicitUse(Operand op) {
|
||||
exists(VariableAddressInstruction vai | vai = op.getDef() |
|
||||
// Don't include this operand as a use if it only exists to initialize the
|
||||
// indirection of a parameter.
|
||||
not exists(LoadInstruction load |
|
||||
load.getSourceAddressOperand() = op and
|
||||
load.getAUse().getUse() instanceof InitializeIndirectionInstruction
|
||||
) and
|
||||
// Don't include this operand as a use if the only use of the address is for a write
|
||||
// that definitely overrides a variable.
|
||||
not (explicitWrite(true, _, vai) and exists(unique( | | vai.getAUse())))
|
||||
op.getDef() instanceof VariableAddressInstruction and
|
||||
not exists(LoadInstruction load |
|
||||
load.getSourceAddressOperand() = op and
|
||||
load.getAUse().getUse() instanceof InitializeIndirectionInstruction
|
||||
)
|
||||
}
|
||||
|
||||
@@ -310,8 +313,8 @@ cached
|
||||
private module Cached {
|
||||
private predicate defUseFlow(Node nodeFrom, Node nodeTo) {
|
||||
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, DefOrUse defOrUse, Use use |
|
||||
defOrUse.hasIndexInBlock(bb1, i1) and
|
||||
use.hasIndexInBlock(bb2, i2) and
|
||||
defOrUse.hasRankInBlock(bb1, i1) and
|
||||
use.hasRankInBlock(bb2, i2) and
|
||||
adjacentDefRead(_, bb1, i1, bb2, i2) and
|
||||
nodeFrom.asInstruction() = toInstruction(defOrUse) and
|
||||
flowOutOfAddressStep(use.getOperand(), nodeTo)
|
||||
@@ -323,9 +326,9 @@ private module Cached {
|
||||
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, Def def, Use use |
|
||||
nodeFrom.isTerminal() and
|
||||
def.getInstruction() = nodeFrom.getStoreInstruction() and
|
||||
def.hasIndexInBlock(bb1, i1) and
|
||||
def.hasRankInBlock(bb1, i1) and
|
||||
adjacentDefRead(_, bb1, i1, bb2, i2) and
|
||||
use.hasIndexInBlock(bb2, i2) and
|
||||
use.hasRankInBlock(bb2, i2) and
|
||||
flowOutOfAddressStep(use.getOperand(), nodeTo)
|
||||
)
|
||||
or
|
||||
@@ -356,8 +359,8 @@ private module Cached {
|
||||
|
||||
private predicate fromReadNode(ReadNode nodeFrom, Node nodeTo) {
|
||||
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, Use use1, Use use2 |
|
||||
use1.hasIndexInBlock(bb1, i1) and
|
||||
use2.hasIndexInBlock(bb2, i2) and
|
||||
use1.hasRankInBlock(bb1, i1) and
|
||||
use2.hasRankInBlock(bb2, i2) and
|
||||
use1.getOperand().getDef() = nodeFrom.getInstruction() and
|
||||
adjacentDefRead(_, bb1, i1, bb2, i2) and
|
||||
flowOutOfAddressStep(use2.getOperand(), nodeTo)
|
||||
@@ -368,7 +371,7 @@ private module Cached {
|
||||
exists(PhiNode phi, Use use, IRBlock block, int rnk |
|
||||
phi = nodeFrom.getPhiNode() and
|
||||
adjacentDefRead(phi, _, _, block, rnk) and
|
||||
use.hasIndexInBlock(block, rnk) and
|
||||
use.hasRankInBlock(block, rnk) and
|
||||
flowOutOfAddressStep(use.getOperand(), nodeTo)
|
||||
)
|
||||
}
|
||||
@@ -376,7 +379,7 @@ private module Cached {
|
||||
private predicate toPhiNode(Node nodeFrom, SsaPhiNode nodeTo) {
|
||||
// Flow to phi nodes
|
||||
exists(Def def, IRBlock block, int rnk |
|
||||
def.hasIndexInBlock(block, rnk) and
|
||||
def.hasRankInBlock(block, rnk) and
|
||||
nodeTo.hasInputAtRankInBlock(block, rnk)
|
||||
|
|
||||
exists(StoreNodeInstr storeNode |
|
||||
@@ -509,8 +512,8 @@ private module Cached {
|
||||
|
|
||||
store = def.getInstruction() and
|
||||
store.getSourceValueOperand() = operand and
|
||||
def.hasIndexInBlock(block1, rnk1) and
|
||||
use.hasIndexInBlock(block2, rnk2) and
|
||||
def.hasRankInBlock(block1, rnk1) and
|
||||
use.hasRankInBlock(block2, rnk2) and
|
||||
adjacentDefRead(_, block1, rnk1, block2, rnk2)
|
||||
|
|
||||
// The shared SSA library has determined that `use` is the next use of the operand
|
||||
@@ -540,12 +543,12 @@ private module Cached {
|
||||
not operand = getSourceAddressOperand(_) and
|
||||
exists(Use use1, Use use2, IRBlock block1, int rnk1, IRBlock block2, int rnk2 |
|
||||
use1.getOperand() = operand and
|
||||
use1.hasIndexInBlock(block1, rnk1) and
|
||||
use1.hasRankInBlock(block1, rnk1) and
|
||||
// Don't flow to the next use if this use is part of a store operation that totally
|
||||
// overrides a variable.
|
||||
not explicitWrite(true, _, use1.getOperand().getDef()) and
|
||||
adjacentDefRead(_, block1, rnk1, block2, rnk2) and
|
||||
use2.hasIndexInBlock(block2, rnk2) and
|
||||
use2.hasRankInBlock(block2, rnk2) and
|
||||
flowOutOfAddressStep(use2.getOperand(), nodeTo)
|
||||
)
|
||||
or
|
||||
@@ -617,7 +620,7 @@ import Cached
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
exists(Def def |
|
||||
def.hasIndexInBlock(bb, i) and
|
||||
def.hasRankInBlock(bb, i) and
|
||||
v = def.getSourceVariable() and
|
||||
(if def.isCertain() then certain = true else certain = false)
|
||||
)
|
||||
@@ -629,7 +632,7 @@ predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
*/
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(Use use |
|
||||
use.hasIndexInBlock(bb, i) and
|
||||
use.hasRankInBlock(bb, i) and
|
||||
v = use.getSourceVariable() and
|
||||
certain = true
|
||||
)
|
||||
|
||||
@@ -121,14 +121,12 @@ private predicate operandToInstructionTaintStep(Operand opFrom, Instruction inst
|
||||
* Holds if taint may propagate from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `i1` to `i2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localInstructionTaint(Instruction i1, Instruction i2) {
|
||||
localTaint(DataFlow::instructionNode(i1), DataFlow::instructionNode(i2))
|
||||
}
|
||||
@@ -137,7 +135,6 @@ predicate localInstructionTaint(Instruction i1, Instruction i2) {
|
||||
* Holds if taint can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localExprTaint(Expr e1, Expr e2) {
|
||||
localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
|
||||
}
|
||||
@@ -163,12 +160,6 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { n
|
||||
*/
|
||||
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `guard` should be a sanitizer guard in all global taint flow configurations
|
||||
* but not in local taint.
|
||||
*/
|
||||
predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `instrIn` to `instrOut` through a call to a
|
||||
* modeled function.
|
||||
|
||||
@@ -61,7 +61,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
@@ -69,7 +69,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
@@ -93,7 +93,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,7 +61,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
@@ -69,7 +69,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
@@ -93,7 +93,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -61,7 +61,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSource(DataFlow::Node source) { none() }
|
||||
abstract override predicate isSource(DataFlow::Node source);
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant taint sink.
|
||||
@@ -69,7 +69,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
* The smaller this predicate is, the faster `hasFlow()` will converge.
|
||||
*/
|
||||
// overridden to provide taint-tracking specific qldoc
|
||||
override predicate isSink(DataFlow::Node sink) { none() }
|
||||
abstract override predicate isSink(DataFlow::Node sink);
|
||||
|
||||
/** Holds if the node `node` is a taint sanitizer. */
|
||||
predicate isSanitizer(DataFlow::Node node) { none() }
|
||||
@@ -93,7 +93,7 @@ abstract class Configuration extends DataFlow::Configuration {
|
||||
predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
|
||||
|
||||
final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
|
||||
this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
|
||||
this.isSanitizerGuard(guard)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -266,20 +266,6 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
||||
bitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate initializeParameterInstructionHasVariable(
|
||||
IRVariable var, InitializeParameterInstruction init
|
||||
) {
|
||||
init.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate instructionInitializesThisInFunction(
|
||||
Language::Function f, InitializeParameterInstruction init
|
||||
) {
|
||||
initializeParameterInstructionHasVariable(any(IRThisVariable var), pragma[only_bind_into](init)) and
|
||||
init.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
private predicate isArgumentForParameter(
|
||||
CallInstruction ci, Operand operand, InitializeParameterInstruction init
|
||||
) {
|
||||
@@ -289,7 +275,8 @@ private predicate isArgumentForParameter(
|
||||
(
|
||||
init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
|
||||
or
|
||||
instructionInitializesThisInFunction(f, init) and
|
||||
init.getIRVariable() instanceof IRThisVariable and
|
||||
unique( | | init.getEnclosingFunction()) = f and
|
||||
operand instanceof ThisArgumentOperand
|
||||
) and
|
||||
not Language::isFunctionVirtual(f) and
|
||||
|
||||
@@ -111,45 +111,6 @@ private predicate hasDefaultSideEffect(Call call, ParameterIndex i, boolean buff
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Call` or `NewOrNewArrayExpr`.
|
||||
*
|
||||
* Both kinds of expression invoke a function as part of their evaluation. This class provides a
|
||||
* way to treat both kinds of function similarly, and to get the invoked `Function`.
|
||||
*/
|
||||
class CallOrAllocationExpr extends Expr {
|
||||
CallOrAllocationExpr() {
|
||||
this instanceof Call
|
||||
or
|
||||
this instanceof NewOrNewArrayExpr
|
||||
}
|
||||
|
||||
/** Gets the `Function` invoked by this expression, if known. */
|
||||
final Function getTarget() {
|
||||
result = this.(Call).getTarget()
|
||||
or
|
||||
result = this.(NewOrNewArrayExpr).getAllocator()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the side effect opcode, if any, that represents any side effects not specifically modeled
|
||||
* by an argument side effect.
|
||||
*/
|
||||
Opcode getCallSideEffectOpcode(CallOrAllocationExpr expr) {
|
||||
not exists(expr.getTarget().(SideEffectFunction)) and result instanceof Opcode::CallSideEffect
|
||||
or
|
||||
exists(SideEffectFunction sideEffectFunction |
|
||||
sideEffectFunction = expr.getTarget() and
|
||||
if not sideEffectFunction.hasOnlySpecificWriteSideEffects()
|
||||
then result instanceof Opcode::CallSideEffect
|
||||
else (
|
||||
not sideEffectFunction.hasOnlySpecificReadSideEffects() and
|
||||
result instanceof Opcode::CallReadSideEffect
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a side effect opcode for parameter index `i` of the specified call.
|
||||
*
|
||||
|
||||
@@ -49,6 +49,19 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
tag = CallTag() and
|
||||
opcode instanceof Opcode::Call and
|
||||
resultType = getTypeForPRValue(getCallResultType())
|
||||
or
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
(
|
||||
if hasWriteSideEffect()
|
||||
then (
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
resultType = getUnknownType()
|
||||
) else (
|
||||
opcode instanceof Opcode::CallReadSideEffect and
|
||||
resultType = getVoidType()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
@@ -71,8 +84,25 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
tag = CallTag() and
|
||||
result = getSideEffects().getFirstInstruction()
|
||||
(
|
||||
(
|
||||
tag = CallTag() and
|
||||
if hasSideEffect()
|
||||
then result = getInstruction(CallSideEffectTag())
|
||||
else
|
||||
if hasPreciseSideEffect()
|
||||
then result = getSideEffects().getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
or
|
||||
(
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
if hasPreciseSideEffect()
|
||||
then result = getSideEffects().getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
@@ -91,6 +121,15 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
)
|
||||
}
|
||||
|
||||
final override CppType getInstructionMemoryOperandType(
|
||||
InstructionTag tag, TypedOperandTag operandTag
|
||||
) {
|
||||
tag = CallSideEffectTag() and
|
||||
hasSideEffect() and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getUnknownType()
|
||||
}
|
||||
|
||||
final override Instruction getResult() { result = getInstruction(CallTag()) }
|
||||
|
||||
/**
|
||||
@@ -161,31 +200,40 @@ abstract class TranslatedCall extends TranslatedExpr {
|
||||
*/
|
||||
abstract predicate hasArguments();
|
||||
|
||||
predicate hasReadSideEffect() { any() }
|
||||
|
||||
predicate hasWriteSideEffect() { any() }
|
||||
|
||||
private predicate hasSideEffect() { hasReadSideEffect() or hasWriteSideEffect() }
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
hasSideEffect() and
|
||||
tag = CallSideEffectTag() and
|
||||
result = getResult()
|
||||
}
|
||||
|
||||
predicate hasPreciseSideEffect() { exists(getSideEffects()) }
|
||||
|
||||
final TranslatedSideEffects getSideEffects() { result.getExpr() = expr }
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the side effects of the parent `TranslatedElement`.
|
||||
*
|
||||
* This object does not itself generate the side effect instructions. Instead, its children provide
|
||||
* the actual side effects, with this object acting as a placeholder so the parent only needs to
|
||||
* insert this one element at the point where all the side effects are supposed to occur.
|
||||
*/
|
||||
abstract class TranslatedSideEffects extends TranslatedElement {
|
||||
/** Gets the expression whose side effects are being modeled. */
|
||||
abstract Expr getExpr();
|
||||
|
||||
final override Locatable getAST() { result = getExpr() }
|
||||
|
||||
final override Function getFunction() { result = getExpr().getEnclosingFunction() }
|
||||
|
||||
final override TranslatedElement getChild(int i) {
|
||||
override TranslatedElement getChild(int i) {
|
||||
result =
|
||||
rank[i + 1](TranslatedSideEffect tse, int group, int indexInGroup |
|
||||
tse.getPrimaryExpr() = getExpr() and
|
||||
tse.sortOrder(group, indexInGroup)
|
||||
rank[i + 1](TranslatedSideEffect tse, int isWrite, int index |
|
||||
(
|
||||
tse.getCall() = getExpr() and
|
||||
tse.getArgumentIndex() = index and
|
||||
if tse.isWrite() then isWrite = 1 else isWrite = 0
|
||||
)
|
||||
|
|
||||
tse order by group, indexInGroup
|
||||
tse order by isWrite, index
|
||||
)
|
||||
}
|
||||
|
||||
@@ -198,21 +246,12 @@ abstract class TranslatedSideEffects extends TranslatedElement {
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
||||
none()
|
||||
/**
|
||||
* Gets the `TranslatedFunction` containing this expression.
|
||||
*/
|
||||
final TranslatedFunction getEnclosingFunction() {
|
||||
result = getTranslatedFunction(getExpr().getEnclosingFunction())
|
||||
}
|
||||
|
||||
final override Instruction getFirstInstruction() {
|
||||
result = getChild(0).getFirstInstruction()
|
||||
or
|
||||
// Some functions, like `std::move()`, have no side effects whatsoever.
|
||||
not exists(getChild(0)) and result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
/** Gets the primary instruction to be associated with each side effect instruction. */
|
||||
abstract Instruction getPrimaryInstruction();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,6 +325,14 @@ class TranslatedFunctionCall extends TranslatedCallExpr, TranslatedDirectCall {
|
||||
tag = CallTargetTag() and result = expr.getTarget()
|
||||
}
|
||||
|
||||
override predicate hasReadSideEffect() {
|
||||
not expr.getTarget().(SideEffectFunction).hasOnlySpecificReadSideEffects()
|
||||
}
|
||||
|
||||
override predicate hasWriteSideEffect() {
|
||||
not expr.getTarget().(SideEffectFunction).hasOnlySpecificWriteSideEffects()
|
||||
}
|
||||
|
||||
override Instruction getQualifierResult() {
|
||||
hasQualifier() and
|
||||
result = getQualifier().getResult()
|
||||
@@ -316,116 +363,209 @@ class TranslatedStructorCall extends TranslatedFunctionCall {
|
||||
override predicate hasQualifier() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the side effects of a function call, including the implicit allocator
|
||||
* call in a `new` or `new[]` expression.
|
||||
*/
|
||||
class TranslatedAllocationSideEffects extends TranslatedSideEffects,
|
||||
TTranslatedAllocationSideEffects {
|
||||
AllocationExpr expr;
|
||||
|
||||
TranslatedAllocationSideEffects() { this = TTranslatedAllocationSideEffects(expr) }
|
||||
|
||||
final override AllocationExpr getExpr() { result = expr }
|
||||
|
||||
override string toString() { result = "(allocation side effects for " + expr.toString() + ")" }
|
||||
|
||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
||||
opcode instanceof Opcode::InitializeDynamicAllocation and
|
||||
tag = OnlyInstructionTag() and
|
||||
type = getUnknownType()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
tag = OnlyInstructionTag() and
|
||||
kind = EdgeKind::gotoEdge() and
|
||||
if exists(getChild(0))
|
||||
then result = getChild(0).getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag = addressOperand() and
|
||||
result = getPrimaryInstructionForSideEffect(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
if expr instanceof NewOrNewArrayExpr
|
||||
then result = getTranslatedAllocatorCall(expr).getInstruction(CallTag())
|
||||
else result = getTranslatedCallInstruction(expr)
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedCallSideEffects extends TranslatedSideEffects, TTranslatedCallSideEffects {
|
||||
Expr expr;
|
||||
Call expr;
|
||||
|
||||
TranslatedCallSideEffects() { this = TTranslatedCallSideEffects(expr) }
|
||||
|
||||
final override string toString() { result = "(side effects for " + expr.toString() + ")" }
|
||||
override string toString() { result = "(side effects for " + expr.toString() + ")" }
|
||||
|
||||
final override Expr getExpr() { result = expr }
|
||||
override Call getExpr() { result = expr }
|
||||
|
||||
final override Instruction getPrimaryInstruction() {
|
||||
expr instanceof Call and result = getTranslatedCallInstruction(expr)
|
||||
or
|
||||
expr instanceof NewOrNewArrayExpr and
|
||||
result = getTranslatedAllocatorCall(expr).getInstruction(CallTag())
|
||||
}
|
||||
}
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) { none() }
|
||||
|
||||
/** Returns the sort group index for argument read side effects. */
|
||||
private int argumentReadGroup() { result = 1 }
|
||||
override Instruction getFirstInstruction() { result = getChild(0).getFirstInstruction() }
|
||||
|
||||
/** Returns the sort group index for conservative call side effects. */
|
||||
private int callSideEffectGroup() {
|
||||
result = 0 // Make this group first for now to preserve the existing ordering
|
||||
}
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
/** Returns the sort group index for argument write side effects. */
|
||||
private int argumentWriteGroup() { result = 2 }
|
||||
|
||||
/** Returns the sort group index for dynamic allocation side effects. */
|
||||
private int initializeAllocationGroup() { result = 3 }
|
||||
|
||||
/**
|
||||
* The IR translation of a single side effect of a call.
|
||||
*/
|
||||
abstract class TranslatedSideEffect extends TranslatedElement {
|
||||
final override TranslatedElement getChild(int n) { none() }
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
|
||||
final override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
sideEffectInstruction(opcode, type)
|
||||
result = getTranslatedCallInstruction(expr)
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedStructorCallSideEffects extends TranslatedCallSideEffects {
|
||||
TranslatedStructorCallSideEffects() {
|
||||
getParent().(TranslatedStructorCall).hasQualifier() and
|
||||
getASideEffectOpcode(expr, -1) instanceof WriteSideEffectOpcode
|
||||
}
|
||||
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType t) {
|
||||
tag instanceof OnlyInstructionTag and
|
||||
t = getTypeForPRValue(expr.getTarget().getDeclaringType()) and
|
||||
opcode = getASideEffectOpcode(expr, -1).(WriteSideEffectOpcode)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
(
|
||||
if exists(getChild(0))
|
||||
then result = getChild(0).getFirstInstruction()
|
||||
else result = getParent().getChildSuccessor(this)
|
||||
) and
|
||||
tag = OnlyInstructionTag() and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getParent().(TranslatedStructorCall).getQualifierResult()
|
||||
}
|
||||
|
||||
final override int getInstructionIndex(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = -1
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedSideEffect extends TranslatedElement, TTranslatedArgumentSideEffect {
|
||||
Call call;
|
||||
Expr arg;
|
||||
int index;
|
||||
SideEffectOpcode sideEffectOpcode;
|
||||
|
||||
TranslatedSideEffect() {
|
||||
this = TTranslatedArgumentSideEffect(call, arg, index, sideEffectOpcode)
|
||||
}
|
||||
|
||||
override Locatable getAST() { result = arg }
|
||||
|
||||
Expr getExpr() { result = arg }
|
||||
|
||||
Call getCall() { result = call }
|
||||
|
||||
int getArgumentIndex() { result = index }
|
||||
|
||||
predicate isWrite() { sideEffectOpcode instanceof WriteSideEffectOpcode }
|
||||
|
||||
override string toString() {
|
||||
isWrite() and
|
||||
result = "(write side effect for " + arg.toString() + ")"
|
||||
or
|
||||
not isWrite() and
|
||||
result = "(read side effect for " + arg.toString() + ")"
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int n) { none() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType type) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode = sideEffectOpcode and
|
||||
(
|
||||
isWrite() and
|
||||
(
|
||||
opcode instanceof BufferAccessOpcode and
|
||||
type = getUnknownType()
|
||||
or
|
||||
not opcode instanceof BufferAccessOpcode and
|
||||
exists(Type baseType | baseType = arg.getUnspecifiedType().(DerivedType).getBaseType() |
|
||||
if baseType instanceof VoidType
|
||||
then type = getUnknownType()
|
||||
else type = getTypeForPRValueOrUnknown(baseType)
|
||||
)
|
||||
or
|
||||
index = -1 and
|
||||
not arg.getUnspecifiedType() instanceof DerivedType and
|
||||
type = getTypeForPRValueOrUnknown(arg.getUnspecifiedType())
|
||||
)
|
||||
or
|
||||
not isWrite() and
|
||||
type = getVoidType()
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
result = getParent().getChildSuccessor(this) and
|
||||
tag = OnlyInstructionTag() and
|
||||
kind instanceof GotoEdge
|
||||
}
|
||||
|
||||
final override Function getFunction() { result = getParent().getFunction() }
|
||||
|
||||
final override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getParent().(TranslatedSideEffects).getPrimaryInstruction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that caused this side effect.
|
||||
*
|
||||
* All side effects with the same `getPrimaryExpr()` will appear in the same contiguous sequence
|
||||
* in the IR.
|
||||
*/
|
||||
abstract Expr getPrimaryExpr();
|
||||
|
||||
/**
|
||||
* Gets the order in which this side effect should be sorted with respect to other side effects
|
||||
* for the same expression.
|
||||
*
|
||||
* Side effects are sorted first by `group`, and then by `indexInGroup`.
|
||||
*/
|
||||
abstract predicate sortOrder(int group, int indexInGroup);
|
||||
|
||||
/**
|
||||
* Gets the opcode and result type for the side effect instruction.
|
||||
*/
|
||||
abstract predicate sideEffectInstruction(Opcode opcode, CppType type);
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of a single argument side effect for a call.
|
||||
*/
|
||||
abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect {
|
||||
Call call;
|
||||
int index;
|
||||
SideEffectOpcode sideEffectOpcode;
|
||||
|
||||
// All subclass charpreds must bind the `index` field.
|
||||
bindingset[index]
|
||||
TranslatedArgumentSideEffect() { any() }
|
||||
|
||||
override string toString() {
|
||||
isWrite() and
|
||||
result = "(write side effect for " + getArgString() + ")"
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getTranslatedExpr(arg).getResult()
|
||||
or
|
||||
not isWrite() and
|
||||
result = "(read side effect for " + getArgString() + ")"
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof BufferSizeOperandTag and
|
||||
result =
|
||||
getTranslatedExpr(call.getArgument(call.getTarget()
|
||||
.(SideEffectFunction)
|
||||
.getParameterSizeIndex(index)).getFullyConverted()).getResult()
|
||||
}
|
||||
|
||||
override Call getPrimaryExpr() { result = call }
|
||||
override CppType getInstructionMemoryOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
not isWrite() and
|
||||
if sideEffectOpcode instanceof BufferAccessOpcode
|
||||
then
|
||||
result = getUnknownType() and
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
else
|
||||
exists(Type operandType |
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandType = arg.getType().getUnspecifiedType().(DerivedType).getBaseType() and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
or
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandType = arg.getType().getUnspecifiedType() and
|
||||
not operandType instanceof DerivedType and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
|
|
||||
// If the type we select is an incomplete type (e.g. a forward-declared `struct`), there will
|
||||
// not be a `CppType` that represents that type. In that case, fall back to `UnknownCppType`.
|
||||
result = getTypeForPRValueOrUnknown(operandType)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate sortOrder(int group, int indexInGroup) {
|
||||
indexInGroup = index and
|
||||
if isWrite() then group = argumentWriteGroup() else group = argumentReadGroup()
|
||||
override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
result = getTranslatedCallInstruction(call)
|
||||
}
|
||||
|
||||
final override int getInstructionIndex(InstructionTag tag) {
|
||||
@@ -437,199 +577,11 @@ abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect {
|
||||
* Gets the `TranslatedFunction` containing this expression.
|
||||
*/
|
||||
final TranslatedFunction getEnclosingFunction() {
|
||||
result = getTranslatedFunction(call.getEnclosingFunction())
|
||||
result = getTranslatedFunction(arg.getEnclosingFunction())
|
||||
}
|
||||
|
||||
final override predicate sideEffectInstruction(Opcode opcode, CppType type) {
|
||||
opcode = sideEffectOpcode and
|
||||
(
|
||||
isWrite() and
|
||||
(
|
||||
opcode instanceof BufferAccessOpcode and
|
||||
type = getUnknownType()
|
||||
or
|
||||
not opcode instanceof BufferAccessOpcode and
|
||||
exists(Type indirectionType | indirectionType = getIndirectionType() |
|
||||
if indirectionType instanceof VoidType
|
||||
then type = getUnknownType()
|
||||
else type = getTypeForPRValueOrUnknown(indirectionType)
|
||||
)
|
||||
)
|
||||
or
|
||||
not isWrite() and
|
||||
type = getVoidType()
|
||||
)
|
||||
}
|
||||
|
||||
final override CppType getInstructionMemoryOperandType(
|
||||
InstructionTag tag, TypedOperandTag operandTag
|
||||
) {
|
||||
not isWrite() and
|
||||
if sideEffectOpcode instanceof BufferAccessOpcode
|
||||
then
|
||||
result = getUnknownType() and
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
else
|
||||
exists(Type operandType |
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandType = getIndirectionType() and
|
||||
operandTag instanceof SideEffectOperandTag
|
||||
|
|
||||
// If the type we select is an incomplete type (e.g. a forward-declared `struct`), there will
|
||||
// not be a `CppType` that represents that type. In that case, fall back to `UnknownCppType`.
|
||||
result = getTypeForPRValueOrUnknown(operandType)
|
||||
)
|
||||
}
|
||||
|
||||
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof AddressOperandTag and
|
||||
result = getArgInstruction()
|
||||
or
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof BufferSizeOperandTag and
|
||||
result =
|
||||
getTranslatedExpr(call.getArgument(call.getTarget()
|
||||
.(SideEffectFunction)
|
||||
.getParameterSizeIndex(index)).getFullyConverted()).getResult()
|
||||
}
|
||||
|
||||
/** Holds if this side effect is a write side effect, rather than a read side effect. */
|
||||
final predicate isWrite() { sideEffectOpcode instanceof WriteSideEffectOpcode }
|
||||
|
||||
/** Gets a text representation of the argument. */
|
||||
abstract string getArgString();
|
||||
|
||||
/** Gets the `Instruction` whose result is the value of the argument. */
|
||||
abstract Instruction getArgInstruction();
|
||||
|
||||
/** Gets the type pointed to by the argument. */
|
||||
abstract Type getIndirectionType();
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of an argument side effect where the argument has an `Expr` object in the AST.
|
||||
*
|
||||
* This generally applies to all positional arguments, as well as qualifier (`this`) arguments for
|
||||
* calls other than constructor calls.
|
||||
*/
|
||||
class TranslatedArgumentExprSideEffect extends TranslatedArgumentSideEffect,
|
||||
TTranslatedArgumentExprSideEffect {
|
||||
Expr arg;
|
||||
|
||||
TranslatedArgumentExprSideEffect() {
|
||||
this = TTranslatedArgumentExprSideEffect(call, arg, index, sideEffectOpcode)
|
||||
}
|
||||
|
||||
final override Locatable getAST() { result = arg }
|
||||
|
||||
final override Type getIndirectionType() {
|
||||
result = arg.getUnspecifiedType().(DerivedType).getBaseType()
|
||||
or
|
||||
// Sometimes the qualifier type gets the type of the class itself, rather than a pointer to the
|
||||
// class.
|
||||
index = -1 and
|
||||
not arg.getUnspecifiedType() instanceof DerivedType and
|
||||
result = arg.getUnspecifiedType()
|
||||
}
|
||||
|
||||
final override string getArgString() { result = arg.toString() }
|
||||
|
||||
final override Instruction getArgInstruction() { result = getTranslatedExpr(arg).getResult() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of an argument side effect for `*this` on a call, where there is no `Expr`
|
||||
* object that represents the `this` argument.
|
||||
*
|
||||
* The applies only to constructor calls, as the AST has explioit qualifier `Expr`s for all other
|
||||
* calls to non-static member functions.
|
||||
*/
|
||||
class TranslatedStructorQualifierSideEffect extends TranslatedArgumentSideEffect,
|
||||
TTranslatedStructorQualifierSideEffect {
|
||||
TranslatedStructorQualifierSideEffect() {
|
||||
this = TTranslatedStructorQualifierSideEffect(call, sideEffectOpcode) and
|
||||
index = -1
|
||||
}
|
||||
|
||||
final override Locatable getAST() { result = call }
|
||||
|
||||
final override Type getIndirectionType() { result = call.getTarget().getDeclaringType() }
|
||||
|
||||
final override string getArgString() { result = "this" }
|
||||
|
||||
final override Instruction getArgInstruction() {
|
||||
exists(TranslatedStructorCall structorCall |
|
||||
structorCall.getExpr() = call and
|
||||
result = structorCall.getQualifierResult()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** The IR translation of the non-argument-specific side effect of a call. */
|
||||
class TranslatedCallSideEffect extends TranslatedSideEffect, TTranslatedCallSideEffect {
|
||||
Expr expr;
|
||||
SideEffectOpcode sideEffectOpcode;
|
||||
|
||||
TranslatedCallSideEffect() { this = TTranslatedCallSideEffect(expr, sideEffectOpcode) }
|
||||
|
||||
override Locatable getAST() { result = expr }
|
||||
|
||||
override Expr getPrimaryExpr() { result = expr }
|
||||
|
||||
override predicate sortOrder(int group, int indexInGroup) {
|
||||
group = callSideEffectGroup() and indexInGroup = 0
|
||||
}
|
||||
|
||||
override string toString() { result = "(call side effect for '" + expr.toString() + "')" }
|
||||
|
||||
override predicate sideEffectInstruction(Opcode opcode, CppType type) {
|
||||
opcode = sideEffectOpcode and
|
||||
(
|
||||
opcode instanceof Opcode::CallSideEffect and
|
||||
type = getUnknownType()
|
||||
or
|
||||
opcode instanceof Opcode::CallReadSideEffect and
|
||||
type = getVoidType()
|
||||
)
|
||||
}
|
||||
|
||||
override CppType getInstructionMemoryOperandType(InstructionTag tag, TypedOperandTag operandTag) {
|
||||
tag instanceof OnlyInstructionTag and
|
||||
operandTag instanceof SideEffectOperandTag and
|
||||
result = getUnknownType()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The IR translation of the allocation side effect of a call to a memory allocation function.
|
||||
*
|
||||
* This side effect provides a definition for the newly-allocated memory.
|
||||
*/
|
||||
class TranslatedAllocationSideEffect extends TranslatedSideEffect, TTranslatedAllocationSideEffect {
|
||||
AllocationExpr expr;
|
||||
|
||||
TranslatedAllocationSideEffect() { this = TTranslatedAllocationSideEffect(expr) }
|
||||
|
||||
override Locatable getAST() { result = expr }
|
||||
|
||||
override Expr getPrimaryExpr() { result = expr }
|
||||
|
||||
override predicate sortOrder(int group, int indexInGroup) {
|
||||
group = initializeAllocationGroup() and indexInGroup = 0
|
||||
}
|
||||
|
||||
override string toString() { result = "(allocation side effect for '" + expr.toString() + "')" }
|
||||
|
||||
override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
|
||||
tag = OnlyInstructionTag() and
|
||||
operandTag = addressOperand() and
|
||||
result = getPrimaryInstructionForSideEffect(OnlyInstructionTag())
|
||||
}
|
||||
|
||||
override predicate sideEffectInstruction(Opcode opcode, CppType type) {
|
||||
opcode instanceof Opcode::InitializeDynamicAllocation and
|
||||
type = getUnknownType()
|
||||
}
|
||||
/**
|
||||
* Gets the `Function` containing this expression.
|
||||
*/
|
||||
override Function getFunction() { result = arg.getEnclosingFunction() }
|
||||
}
|
||||
|
||||
@@ -135,20 +135,6 @@ private predicate ignoreExpr(Expr expr) {
|
||||
ignoreExprAndDescendants(expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the side effects of `expr` should be ignoredf for the purposes of IR generation.
|
||||
*
|
||||
* In cases involving `constexpr`, a call can wind up as a constant expression. `ignoreExpr()` will
|
||||
* not hold for such a call, since we do need to translate the call (as a constant), but we need to
|
||||
* ignore all of the side effects of that call, since we will not actually be generating a `Call`
|
||||
* instruction.
|
||||
*/
|
||||
private predicate ignoreSideEffects(Expr expr) {
|
||||
ignoreExpr(expr)
|
||||
or
|
||||
isIRConstant(expr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `func` contains an AST that cannot be translated into IR. This is mostly used to work
|
||||
* around extractor bugs. Once the relevant extractor bugs are fixed, this predicate can be removed.
|
||||
@@ -567,13 +553,6 @@ newtype TTranslatedElement =
|
||||
} or
|
||||
// The initialization of a base class from within a constructor.
|
||||
TTranslatedConstructorBaseInit(ConstructorBaseInit init) { not ignoreExpr(init) } or
|
||||
// Workaround for a case where no base constructor is generated but a targetless base
|
||||
// constructor call is present.
|
||||
TTranslatedConstructorBareInit(ConstructorInit init) {
|
||||
not ignoreExpr(init) and
|
||||
not init instanceof ConstructorBaseInit and
|
||||
not init instanceof ConstructorFieldInit
|
||||
} or
|
||||
// The destruction of a base class from within a destructor.
|
||||
TTranslatedDestructorBaseDestruction(DestructorBaseDestruction destruction) {
|
||||
not ignoreExpr(destruction)
|
||||
@@ -642,34 +621,32 @@ newtype TTranslatedElement =
|
||||
// The declaration/initialization part of a `ConditionDeclExpr`
|
||||
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
|
||||
// The side effects of a `Call`
|
||||
TTranslatedCallSideEffects(CallOrAllocationExpr expr) { not ignoreSideEffects(expr) } or
|
||||
// The non-argument-specific side effect of a `Call`
|
||||
TTranslatedCallSideEffect(Expr expr, SideEffectOpcode opcode) {
|
||||
not ignoreSideEffects(expr) and
|
||||
opcode = getCallSideEffectOpcode(expr)
|
||||
TTranslatedCallSideEffects(Call expr) {
|
||||
// Exclude allocations such as `malloc` (which happen to also be function calls).
|
||||
// Both `TranslatedCallSideEffects` and `TranslatedAllocationSideEffects` generate
|
||||
// the same side effects for its children as they both extend the `TranslatedSideEffects`
|
||||
// class.
|
||||
// Note: We can separate allocation side effects and call side effects into two
|
||||
// translated elements as no call can be both a `ConstructorCall` and an `AllocationExpr`.
|
||||
not expr instanceof AllocationExpr and
|
||||
(
|
||||
exists(TTranslatedArgumentSideEffect(expr, _, _, _)) or
|
||||
expr instanceof ConstructorCall
|
||||
)
|
||||
} or
|
||||
// The side effects of an allocation, i.e. `new`, `new[]` or `malloc`
|
||||
TTranslatedAllocationSideEffects(AllocationExpr expr) { not ignoreExpr(expr) } or
|
||||
// A precise side effect of an argument to a `Call`
|
||||
TTranslatedArgumentExprSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) {
|
||||
TTranslatedArgumentSideEffect(Call call, Expr expr, int n, SideEffectOpcode opcode) {
|
||||
not ignoreExpr(expr) and
|
||||
not ignoreSideEffects(call) and
|
||||
not ignoreExpr(call) and
|
||||
(
|
||||
n >= 0 and expr = call.getArgument(n).getFullyConverted()
|
||||
or
|
||||
n = -1 and expr = call.getQualifier().getFullyConverted()
|
||||
) and
|
||||
opcode = getASideEffectOpcode(call, n)
|
||||
} or
|
||||
// Constructor calls lack a qualifier (`this`) expression, so we need to handle the side effects
|
||||
// on `*this` without an `Expr`.
|
||||
TTranslatedStructorQualifierSideEffect(Call call, SideEffectOpcode opcode) {
|
||||
not ignoreSideEffects(call) and
|
||||
// Don't bother with destructor calls for now, since we won't see very many of them in the IR
|
||||
// until we start injecting implicit destructor calls.
|
||||
call instanceof ConstructorCall and
|
||||
opcode = getASideEffectOpcode(call, -1)
|
||||
} or
|
||||
// The side effect that initializes newly-allocated memory.
|
||||
TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the first explicitly initialized element in `initList`
|
||||
|
||||
@@ -573,11 +573,6 @@ class TranslatedConstructorInitList extends TranslatedElement, InitializationCon
|
||||
baseInit = func.(Constructor).getInitializer(id) and
|
||||
result = getTranslatedConstructorBaseInit(baseInit)
|
||||
)
|
||||
or
|
||||
exists(ConstructorInit bareInit |
|
||||
bareInit = func.(Constructor).getInitializer(id) and
|
||||
result = getTranslatedConstructorBareInit(bareInit)
|
||||
)
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction() {
|
||||
|
||||
@@ -917,36 +917,3 @@ class TranslatedDestructorBaseDestruction extends TranslatedBaseStructorCall,
|
||||
|
||||
final override string toString() { result = "destroy base: " + call.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A constructor base init call where no base constructor has been generated.
|
||||
*
|
||||
* Workaround for an extractor issue.
|
||||
*/
|
||||
class TranslatedConstructorBareInit extends TranslatedElement, TTranslatedConstructorBareInit {
|
||||
ConstructorInit init;
|
||||
|
||||
TranslatedConstructorBareInit() { this = TTranslatedConstructorBareInit(init) }
|
||||
|
||||
override Locatable getAST() { result = init }
|
||||
|
||||
final override string toString() { result = "construct base (no constructor)" }
|
||||
|
||||
override Instruction getFirstInstruction() { result = getParent().getChildSuccessor(this) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
override Function getFunction() { result = getParent().getFunction() }
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
}
|
||||
|
||||
TranslatedConstructorBareInit getTranslatedConstructorBareInit(ConstructorInit init) {
|
||||
result.getAST() = init
|
||||
}
|
||||
|
||||
@@ -266,20 +266,6 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
||||
bitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate initializeParameterInstructionHasVariable(
|
||||
IRVariable var, InitializeParameterInstruction init
|
||||
) {
|
||||
init.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate instructionInitializesThisInFunction(
|
||||
Language::Function f, InitializeParameterInstruction init
|
||||
) {
|
||||
initializeParameterInstructionHasVariable(any(IRThisVariable var), pragma[only_bind_into](init)) and
|
||||
init.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
private predicate isArgumentForParameter(
|
||||
CallInstruction ci, Operand operand, InitializeParameterInstruction init
|
||||
) {
|
||||
@@ -289,7 +275,8 @@ private predicate isArgumentForParameter(
|
||||
(
|
||||
init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
|
||||
or
|
||||
instructionInitializesThisInFunction(f, init) and
|
||||
init.getIRVariable() instanceof IRThisVariable and
|
||||
unique( | | init.getEnclosingFunction()) = f and
|
||||
operand instanceof ThisArgumentOperand
|
||||
) and
|
||||
not Language::isFunctionVirtual(f) and
|
||||
|
||||
@@ -308,45 +308,45 @@ class MetricClass extends Class {
|
||||
}
|
||||
|
||||
private string getAUsedHalsteadN1Operator() {
|
||||
this.getAnEnclosedExpression() instanceof CommaExpr and result = "comma"
|
||||
exists(CommaExpr e | e = this.getAnEnclosedExpression()) and result = "comma"
|
||||
or
|
||||
this.getAnEnclosedExpression() instanceof ReferenceToExpr and result = "refTo"
|
||||
exists(ReferenceToExpr e | e = this.getAnEnclosedExpression()) and result = "refTo"
|
||||
or
|
||||
this.getAnEnclosedExpression() instanceof PointerDereferenceExpr and result = "dereference"
|
||||
exists(PointerDereferenceExpr e | e = this.getAnEnclosedExpression()) and result = "dereference"
|
||||
or
|
||||
this.getAnEnclosedExpression() instanceof CStyleCast and result = "cCast"
|
||||
exists(CStyleCast e | e = this.getAnEnclosedExpression()) and result = "cCast"
|
||||
or
|
||||
this.getAnEnclosedExpression() instanceof StaticCast and result = "staticCast"
|
||||
exists(StaticCast e | e = this.getAnEnclosedExpression()) and result = "staticCast"
|
||||
or
|
||||
this.getAnEnclosedExpression() instanceof ConstCast and result = "constCast"
|
||||
exists(ConstCast e | e = this.getAnEnclosedExpression()) and result = "constCast"
|
||||
or
|
||||
this.getAnEnclosedExpression() instanceof ReinterpretCast and result = "reinterpretCast"
|
||||
exists(ReinterpretCast e | e = this.getAnEnclosedExpression()) and result = "reinterpretCast"
|
||||
or
|
||||
this.getAnEnclosedExpression() instanceof DynamicCast and result = "dynamicCast"
|
||||
exists(DynamicCast e | e = this.getAnEnclosedExpression()) and result = "dynamicCast"
|
||||
or
|
||||
this.getAnEnclosedExpression() instanceof SizeofExprOperator and result = "sizeofExpr"
|
||||
exists(SizeofExprOperator e | e = this.getAnEnclosedExpression()) and result = "sizeofExpr"
|
||||
or
|
||||
this.getAnEnclosedExpression() instanceof SizeofTypeOperator and result = "sizeofType"
|
||||
exists(SizeofTypeOperator e | e = this.getAnEnclosedExpression()) and result = "sizeofType"
|
||||
or
|
||||
this.getAnEnclosedStmt() instanceof IfStmt and result = "ifVal"
|
||||
exists(IfStmt e | e = this.getAnEnclosedStmt()) and result = "ifVal"
|
||||
or
|
||||
this.getAnEnclosedStmt() instanceof SwitchStmt and result = "switchVal"
|
||||
exists(SwitchStmt e | e = this.getAnEnclosedStmt()) and result = "switchVal"
|
||||
or
|
||||
this.getAnEnclosedStmt() instanceof ForStmt and result = "forVal"
|
||||
exists(ForStmt e | e = this.getAnEnclosedStmt()) and result = "forVal"
|
||||
or
|
||||
this.getAnEnclosedStmt() instanceof DoStmt and result = "doVal"
|
||||
exists(DoStmt e | e = this.getAnEnclosedStmt()) and result = "doVal"
|
||||
or
|
||||
this.getAnEnclosedStmt() instanceof WhileStmt and result = "whileVal"
|
||||
exists(WhileStmt e | e = this.getAnEnclosedStmt()) and result = "whileVal"
|
||||
or
|
||||
this.getAnEnclosedStmt() instanceof GotoStmt and result = "gotoVal"
|
||||
exists(GotoStmt e | e = this.getAnEnclosedStmt()) and result = "gotoVal"
|
||||
or
|
||||
this.getAnEnclosedStmt() instanceof ContinueStmt and result = "continueVal"
|
||||
exists(ContinueStmt e | e = this.getAnEnclosedStmt()) and result = "continueVal"
|
||||
or
|
||||
this.getAnEnclosedStmt() instanceof BreakStmt and result = "breakVal"
|
||||
exists(BreakStmt e | e = this.getAnEnclosedStmt()) and result = "breakVal"
|
||||
or
|
||||
this.getAnEnclosedStmt() instanceof ReturnStmt and result = "returnVal"
|
||||
exists(ReturnStmt e | e = this.getAnEnclosedStmt()) and result = "returnVal"
|
||||
or
|
||||
this.getAnEnclosedStmt() instanceof SwitchCase and result = "caseVal"
|
||||
exists(SwitchCase e | e = this.getAnEnclosedStmt()) and result = "caseVal"
|
||||
or
|
||||
exists(IfStmt s | s = this.getAnEnclosedStmt() and s.hasElse()) and
|
||||
result = "elseVal"
|
||||
|
||||
@@ -11,14 +11,15 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
|
||||
/**
|
||||
* The standard functions `fgets` and `fgetws`.
|
||||
* The standard functions `gets` and `fgets`.
|
||||
*/
|
||||
private class FgetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
|
||||
private class GetsFunction extends DataFlowFunction, TaintFunction, ArrayFunction, AliasFunction,
|
||||
SideEffectFunction, RemoteFlowSourceFunction {
|
||||
FgetsFunction() {
|
||||
GetsFunction() {
|
||||
// gets(str)
|
||||
// fgets(str, num, stream)
|
||||
// fgetws(wstr, num, stream)
|
||||
this.hasGlobalOrStdOrBslName(["fgets", "fgetws"])
|
||||
this.hasGlobalOrStdOrBslName(["gets", "fgets", "fgetws"])
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
@@ -50,61 +51,18 @@ private class FgetsFunction extends DataFlowFunction, TaintFunction, ArrayFuncti
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(0) and
|
||||
description = "String read by " + this.getName()
|
||||
or
|
||||
output.isReturnValue() and
|
||||
description = "String read by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasArrayWithVariableSize(int bufParam, int countParam) {
|
||||
not this.hasName("gets") and
|
||||
bufParam = 0 and
|
||||
countParam = 1
|
||||
}
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasSocketInput(FunctionInput input) { input.isParameterDeref(2) }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard functions `gets`.
|
||||
*/
|
||||
private class GetsFunction extends DataFlowFunction, ArrayFunction, AliasFunction,
|
||||
SideEffectFunction, LocalFlowSourceFunction {
|
||||
GetsFunction() {
|
||||
// gets(str)
|
||||
this.hasGlobalOrStdOrBslName("gets")
|
||||
override predicate hasArrayWithUnknownSize(int bufParam) {
|
||||
this.hasName("gets") and
|
||||
bufParam = 0
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate parameterNeverEscapes(int index) { none() }
|
||||
|
||||
override predicate parameterEscapesOnlyViaReturn(int index) { index = 0 }
|
||||
|
||||
override predicate parameterIsAlwaysReturned(int index) { index = 0 }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and
|
||||
buffer = true and
|
||||
mustWrite = true
|
||||
}
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(0) and
|
||||
description = "String read by " + this.getName()
|
||||
or
|
||||
output.isReturnValue() and
|
||||
description = "String read by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasArrayWithUnknownSize(int bufParam) { bufParam = 0 }
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
}
|
||||
|
||||
@@ -20,9 +20,8 @@ abstract class RemoteFlowSourceFunction extends Function {
|
||||
abstract predicate hasRemoteFlowSource(FunctionOutput output, string description);
|
||||
|
||||
/**
|
||||
* Holds if remote data from this source comes from a socket or stream
|
||||
* described by `input`. There is no result if none is specified by a
|
||||
* parameter.
|
||||
* Holds if remote data from this source comes from a socket described by
|
||||
* `input`. There is no result if a socket is not specified.
|
||||
*/
|
||||
predicate hasSocketInput(FunctionInput input) { none() }
|
||||
}
|
||||
@@ -60,9 +59,8 @@ abstract class RemoteFlowSinkFunction extends Function {
|
||||
abstract predicate hasRemoteFlowSink(FunctionInput input, string description);
|
||||
|
||||
/**
|
||||
* Holds if data put into this sink is transmitted through a socket or stream
|
||||
* described by `input`. There is no result if none is specified by a
|
||||
* parameter.
|
||||
* Holds if data put into this sink is transmitted through a socket described
|
||||
* by `input`. There is no result if a socket is not specified.
|
||||
*/
|
||||
predicate hasSocketInput(FunctionInput input) { none() }
|
||||
}
|
||||
|
||||
@@ -397,7 +397,7 @@ class PaddedType extends Class {
|
||||
// Support only single inheritance for now. If multiple inheritance is
|
||||
// supported, be sure to fix up the calls to getABaseClass*() to correctly
|
||||
// handle the presence of multiple base class subojects with the same type.
|
||||
not exists(this.getDerivation(1))
|
||||
not exists(ClassDerivation cd | cd = this.getDerivation(1))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -72,7 +72,7 @@ predicate lvalue(Element e) {
|
||||
or
|
||||
exists(Cast c | lvalue(c) and e.(Expr).getConversion() = c)
|
||||
or
|
||||
e.(Expr).getConversion() instanceof ReferenceToExpr
|
||||
exists(ReferenceToExpr toref | e.(Expr).getConversion() = toref)
|
||||
or
|
||||
// If f is a function-pointer, then the following two
|
||||
// calls are equivalent: f() and (*f)()
|
||||
|
||||
@@ -71,14 +71,6 @@ abstract class BufferWrite extends Expr {
|
||||
*/
|
||||
int getMaxData() { none() }
|
||||
|
||||
/**
|
||||
* Gets an upper bound to the amount of data that's being written (if one
|
||||
* can be found), specifying the reason for the estimation.
|
||||
*/
|
||||
int getMaxData(BufferWriteEstimationReason reason) {
|
||||
reason instanceof UnspecifiedEstimateReason and result = this.getMaxData()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound to the amount of data that's being written (if one
|
||||
* can be found), except that float to string conversions are assumed to be
|
||||
@@ -87,15 +79,6 @@ abstract class BufferWrite extends Expr {
|
||||
*/
|
||||
int getMaxDataLimited() { result = this.getMaxData() }
|
||||
|
||||
/**
|
||||
* Gets an upper bound to the amount of data that's being written (if one
|
||||
* can be found), specifying the reason for the estimation, except that
|
||||
* float to string conversions are assumed to be much smaller (8 bytes)
|
||||
* than their true maximum length. This can be helpful in determining the
|
||||
* cause of a buffer overflow issue.
|
||||
*/
|
||||
int getMaxDataLimited(BufferWriteEstimationReason reason) { result = this.getMaxData(reason) }
|
||||
|
||||
/**
|
||||
* Gets the size of a single character of the type this
|
||||
* operation works with, in bytes.
|
||||
@@ -152,18 +135,10 @@ class StrCopyBW extends BufferWriteCall {
|
||||
result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize()
|
||||
}
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
// when result exists, it is an exact flow analysis
|
||||
reason instanceof ValueFlowAnalysis and
|
||||
override int getMaxData() {
|
||||
result =
|
||||
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize()
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxDataImpl(reason)
|
||||
}
|
||||
|
||||
override int getMaxData() { result = max(this.getMaxDataImpl(_)) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,18 +173,10 @@ class StrCatBW extends BufferWriteCall {
|
||||
result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize()
|
||||
}
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
// when result exists, it is an exact flow analysis
|
||||
reason instanceof ValueFlowAnalysis and
|
||||
override int getMaxData() {
|
||||
result =
|
||||
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize()
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxDataImpl(reason)
|
||||
}
|
||||
|
||||
override int getMaxData() { result = max(this.getMaxDataImpl(_)) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,31 +233,19 @@ class SprintfBW extends BufferWriteCall {
|
||||
|
||||
override Expr getDest() { result = this.getArgument(f.getOutputParameterIndex(false)) }
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
override int getMaxData() {
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.(FormattingFunctionCall).getFormat() and
|
||||
result = fl.getMaxConvertedLengthWithReason(reason) * this.getCharSize()
|
||||
result = fl.getMaxConvertedLength() * this.getCharSize()
|
||||
)
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxDataImpl(reason)
|
||||
}
|
||||
|
||||
override int getMaxData() { result = max(this.getMaxDataImpl(_)) }
|
||||
|
||||
private int getMaxDataLimitedImpl(BufferWriteEstimationReason reason) {
|
||||
override int getMaxDataLimited() {
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.(FormattingFunctionCall).getFormat() and
|
||||
result = fl.getMaxConvertedLengthLimitedWithReason(reason) * this.getCharSize()
|
||||
result = fl.getMaxConvertedLengthLimited() * this.getCharSize()
|
||||
)
|
||||
}
|
||||
|
||||
override int getMaxDataLimited(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxDataLimitedImpl(reason)
|
||||
}
|
||||
|
||||
override int getMaxDataLimited() { result = max(this.getMaxDataLimitedImpl(_)) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,31 +336,19 @@ class SnprintfBW extends BufferWriteCall {
|
||||
result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize()
|
||||
}
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
override int getMaxData() {
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.(FormattingFunctionCall).getFormat() and
|
||||
result = fl.getMaxConvertedLengthWithReason(reason) * this.getCharSize()
|
||||
result = fl.getMaxConvertedLength() * this.getCharSize()
|
||||
)
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxDataImpl(reason)
|
||||
}
|
||||
|
||||
override int getMaxData() { result = max(this.getMaxDataImpl(_)) }
|
||||
|
||||
private int getMaxDataLimitedImpl(BufferWriteEstimationReason reason) {
|
||||
override int getMaxDataLimited() {
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.(FormattingFunctionCall).getFormat() and
|
||||
result = fl.getMaxConvertedLengthLimitedWithReason(reason) * this.getCharSize()
|
||||
result = fl.getMaxConvertedLengthLimited() * this.getCharSize()
|
||||
)
|
||||
}
|
||||
|
||||
override int getMaxDataLimited(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxDataLimitedImpl(reason)
|
||||
}
|
||||
|
||||
override int getMaxDataLimited() { result = max(this.getMaxDataLimitedImpl(_)) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,9 +436,7 @@ class ScanfBW extends BufferWrite {
|
||||
|
||||
override Expr getDest() { result = this }
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
// when this returns, it is based on exact flow analysis
|
||||
reason instanceof ValueFlowAnalysis and
|
||||
override int getMaxData() {
|
||||
exists(ScanfFunctionCall fc, ScanfFormatLiteral fl, int arg |
|
||||
this = fc.getArgument(arg) and
|
||||
fl = fc.getFormat() and
|
||||
@@ -503,12 +444,6 @@ class ScanfBW extends BufferWrite {
|
||||
)
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxDataImpl(reason)
|
||||
}
|
||||
|
||||
override int getMaxData() { result = max(this.getMaxDataImpl(_)) }
|
||||
|
||||
override string getBWDesc() {
|
||||
exists(FunctionCall fc |
|
||||
this = fc.getArgument(_) and
|
||||
@@ -539,16 +474,8 @@ class RealpathBW extends BufferWriteCall {
|
||||
|
||||
override Expr getASource() { result = this.getArgument(0) }
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
// although there may be some unknown invariants guaranteeing that a real path is shorter than PATH_MAX, we can consider providing less than PATH_MAX a problem with high precision
|
||||
reason instanceof ValueFlowAnalysis and
|
||||
override int getMaxData() {
|
||||
result = path_max() and
|
||||
this = this // Suppress a compiler warning
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxDataImpl(reason)
|
||||
}
|
||||
|
||||
override int getMaxData() { result = max(this.getMaxDataImpl(_)) }
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import semmle.code.cpp.controlflow.Dominance
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* Holds if the value of `use` is guarded using `abs`.
|
||||
@@ -17,16 +16,53 @@ import semmle.code.cpp.controlflow.Guards
|
||||
predicate guardedAbs(Operation e, Expr use) {
|
||||
exists(FunctionCall fc | fc.getTarget().getName() = ["abs", "labs", "llabs", "imaxabs"] |
|
||||
fc.getArgument(0).getAChild*() = use and
|
||||
exists(GuardCondition c | c.ensuresLt(fc, _, _, e.getBasicBlock(), true))
|
||||
guardedLesser(e, fc)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of `stmt` in basic block `block` (this is a thin layer
|
||||
* over `BasicBlock.getNode`, intended to improve performance).
|
||||
*/
|
||||
pragma[noinline]
|
||||
private int getStmtIndexInBlock(BasicBlock block, Stmt stmt) { block.getNode(result) = stmt }
|
||||
|
||||
pragma[inline]
|
||||
private predicate stmtDominates(Stmt dominator, Stmt dominated) {
|
||||
// In same block
|
||||
exists(BasicBlock block, int dominatorIndex, int dominatedIndex |
|
||||
dominatorIndex = getStmtIndexInBlock(block, dominator) and
|
||||
dominatedIndex = getStmtIndexInBlock(block, dominated) and
|
||||
dominatedIndex >= dominatorIndex
|
||||
)
|
||||
or
|
||||
// In (possibly) different blocks
|
||||
bbStrictlyDominates(dominator.getBasicBlock(), dominated.getBasicBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value of `use` is guarded to be less than something, and `e`
|
||||
* is in code controlled by that guard (where the guard condition held).
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate guardedLesser(Operation e, Expr use) {
|
||||
exists(GuardCondition c | c.ensuresLt(use, _, _, e.getBasicBlock(), true))
|
||||
exists(IfStmt c, RelationalOperation guard |
|
||||
use = guard.getLesserOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
stmtDominates(c.getThen(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(Loop c, RelationalOperation guard |
|
||||
use = guard.getLesserOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
stmtDominates(c.getStmt(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(ConditionalExpr c, RelationalOperation guard |
|
||||
use = guard.getLesserOperand().getAChild*() and
|
||||
guard = c.getCondition().getAChild*() and
|
||||
c.getThen().getAChild*() = e
|
||||
)
|
||||
or
|
||||
guardedAbs(e, use)
|
||||
}
|
||||
@@ -35,8 +71,25 @@ predicate guardedLesser(Operation e, Expr use) {
|
||||
* Holds if the value of `use` is guarded to be greater than something, and `e`
|
||||
* is in code controlled by that guard (where the guard condition held).
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate guardedGreater(Operation e, Expr use) {
|
||||
exists(GuardCondition c | c.ensuresLt(use, _, _, e.getBasicBlock(), false))
|
||||
exists(IfStmt c, RelationalOperation guard |
|
||||
use = guard.getGreaterOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
stmtDominates(c.getThen(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(Loop c, RelationalOperation guard |
|
||||
use = guard.getGreaterOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
stmtDominates(c.getStmt(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(ConditionalExpr c, RelationalOperation guard |
|
||||
use = guard.getGreaterOperand().getAChild*() and
|
||||
guard = c.getCondition().getAChild*() and
|
||||
c.getThen().getAChild*() = e
|
||||
)
|
||||
or
|
||||
guardedAbs(e, use)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
* Support for tracking tainted data through the program. This is an alias for
|
||||
* `semmle.code.cpp.ir.dataflow.DefaultTaintTracking` provided for backwards
|
||||
* compatibility.
|
||||
* Support for tracking tainted data through the program.
|
||||
*
|
||||
* Prefer to use `semmle.code.cpp.dataflow.TaintTracking` or
|
||||
* `semmle.code.cpp.ir.dataflow.TaintTracking` when designing new queries.
|
||||
* Prefer to use `semmle.code.cpp.dataflow.TaintTracking` when designing new queries.
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.DefaultTaintTracking
|
||||
|
||||
@@ -258,7 +258,7 @@ private predicate insideFunctionValueMoveTo(Element src, Element dest) {
|
||||
format.getConversionChar(sourceArg - ffc.getTarget().getNumberOfParameters()) = ["s", "S"]
|
||||
)
|
||||
or
|
||||
not c.(FormattingFunctionCall).getFormat() instanceof FormatLiteral
|
||||
not exists(FormatLiteral fl | fl = c.(FormattingFunctionCall).getFormat())
|
||||
or
|
||||
not c instanceof FormattingFunctionCall
|
||||
) and
|
||||
|
||||
@@ -271,7 +271,7 @@ class IfStmt extends ConditionalStmt, @stmt_if {
|
||||
* if (b) { x = 1; }
|
||||
* ```
|
||||
*/
|
||||
predicate hasElse() { exists(this.getElse()) }
|
||||
predicate hasElse() { exists(Stmt s | this.getElse() = s) }
|
||||
|
||||
override string toString() { result = "if (...) ... " }
|
||||
|
||||
@@ -357,7 +357,7 @@ class ConstexprIfStmt extends ConditionalStmt, @stmt_constexpr_if {
|
||||
* if constexpr (b) { x = 1; }
|
||||
* ```
|
||||
*/
|
||||
predicate hasElse() { exists(this.getElse()) }
|
||||
predicate hasElse() { exists(Stmt s | this.getElse() = s) }
|
||||
|
||||
override string toString() { result = "if constexpr (...) ... " }
|
||||
|
||||
|
||||
@@ -6,22 +6,122 @@
|
||||
*/
|
||||
class Person extends string {
|
||||
Person() {
|
||||
this =
|
||||
[
|
||||
"Ronil", "Dina", "Ravi", "Bruce", "Jo", "Aida", "Esme", "Charlie", "Fred", "Meera", "Maya",
|
||||
"Chad", "Tiana", "Laura", "George", "Will", "Mary", "Almira", "Susannah", "Rhoda",
|
||||
"Cynthia", "Eunice", "Olive", "Virginia", "Angeline", "Helen", "Cornelia", "Harriet",
|
||||
"Mahala", "Abby", "Margaret", "Deb", "Minerva", "Severus", "Lavina", "Adeline", "Cath",
|
||||
"Elisa", "Lucretia", "Anne", "Eleanor", "Joanna", "Adam", "Agnes", "Rosanna", "Clara",
|
||||
"Melissa", "Amy", "Isabel", "Jemima", "Cordelia", "Melinda", "Delila", "Jeremiah", "Elijah",
|
||||
"Hester", "Walter", "Oliver", "Hugh", "Aaron", "Reuben", "Eli", "Amos", "Augustus",
|
||||
"Theodore", "Ira", "Timothy", "Cyrus", "Horace", "Simon", "Asa", "Frank", "Nelson",
|
||||
"Leonard", "Harrison", "Anthony", "Louis", "Milton", "Noah", "Cornelius", "Abdul", "Warren",
|
||||
"Harvey", "Dennis", "Wesley", "Sylvester", "Gilbert", "Sullivan", "Edmund", "Wilson",
|
||||
"Perry", "Matthew", "Simba", "Nala", "Rafiki", "Shenzi", "Ernest", "Gertrude", "Oscar",
|
||||
"Lilian", "Raymond", "Elgar", "Elmer", "Herbert", "Maude", "Mae", "Otto", "Edwin",
|
||||
"Ophelia", "Parsley", "Sage", "Rosemary", "Thyme", "Garfunkel", "King Basil", "Stephen"
|
||||
]
|
||||
this = "Ronil" or
|
||||
this = "Dina" or
|
||||
this = "Ravi" or
|
||||
this = "Bruce" or
|
||||
this = "Jo" or
|
||||
this = "Aida" or
|
||||
this = "Esme" or
|
||||
this = "Charlie" or
|
||||
this = "Fred" or
|
||||
this = "Meera" or
|
||||
this = "Maya" or
|
||||
this = "Chad" or
|
||||
this = "Tiana" or
|
||||
this = "Laura" or
|
||||
this = "George" or
|
||||
this = "Will" or
|
||||
this = "Mary" or
|
||||
this = "Almira" or
|
||||
this = "Susannah" or
|
||||
this = "Rhoda" or
|
||||
this = "Cynthia" or
|
||||
this = "Eunice" or
|
||||
this = "Olive" or
|
||||
this = "Virginia" or
|
||||
this = "Angeline" or
|
||||
this = "Helen" or
|
||||
this = "Cornelia" or
|
||||
this = "Harriet" or
|
||||
this = "Mahala" or
|
||||
this = "Abby" or
|
||||
this = "Margaret" or
|
||||
this = "Deb" or
|
||||
this = "Minerva" or
|
||||
this = "Severus" or
|
||||
this = "Lavina" or
|
||||
this = "Adeline" or
|
||||
this = "Cath" or
|
||||
this = "Elisa" or
|
||||
this = "Lucretia" or
|
||||
this = "Anne" or
|
||||
this = "Eleanor" or
|
||||
this = "Joanna" or
|
||||
this = "Adam" or
|
||||
this = "Agnes" or
|
||||
this = "Rosanna" or
|
||||
this = "Clara" or
|
||||
this = "Melissa" or
|
||||
this = "Amy" or
|
||||
this = "Isabel" or
|
||||
this = "Jemima" or
|
||||
this = "Cordelia" or
|
||||
this = "Melinda" or
|
||||
this = "Delila" or
|
||||
this = "Jeremiah" or
|
||||
this = "Elijah" or
|
||||
this = "Hester" or
|
||||
this = "Walter" or
|
||||
this = "Oliver" or
|
||||
this = "Hugh" or
|
||||
this = "Aaron" or
|
||||
this = "Reuben" or
|
||||
this = "Eli" or
|
||||
this = "Amos" or
|
||||
this = "Augustus" or
|
||||
this = "Theodore" or
|
||||
this = "Ira" or
|
||||
this = "Timothy" or
|
||||
this = "Cyrus" or
|
||||
this = "Horace" or
|
||||
this = "Simon" or
|
||||
this = "Asa" or
|
||||
this = "Frank" or
|
||||
this = "Nelson" or
|
||||
this = "Leonard" or
|
||||
this = "Harrison" or
|
||||
this = "Anthony" or
|
||||
this = "Louis" or
|
||||
this = "Milton" or
|
||||
this = "Noah" or
|
||||
this = "Cornelius" or
|
||||
this = "Abdul" or
|
||||
this = "Warren" or
|
||||
this = "Harvey" or
|
||||
this = "Dennis" or
|
||||
this = "Wesley" or
|
||||
this = "Sylvester" or
|
||||
this = "Gilbert" or
|
||||
this = "Sullivan" or
|
||||
this = "Edmund" or
|
||||
this = "Wilson" or
|
||||
this = "Perry" or
|
||||
this = "Matthew" or
|
||||
this = "Simba" or
|
||||
this = "Nala" or
|
||||
this = "Rafiki" or
|
||||
this = "Shenzi" or
|
||||
this = "Ernest" or
|
||||
this = "Gertrude" or
|
||||
this = "Oscar" or
|
||||
this = "Lilian" or
|
||||
this = "Raymond" or
|
||||
this = "Elgar" or
|
||||
this = "Elmer" or
|
||||
this = "Herbert" or
|
||||
this = "Maude" or
|
||||
this = "Mae" or
|
||||
this = "Otto" or
|
||||
this = "Edwin" or
|
||||
this = "Ophelia" or
|
||||
this = "Parsley" or
|
||||
this = "Sage" or
|
||||
this = "Rosemary" or
|
||||
this = "Thyme" or
|
||||
this = "Garfunkel" or
|
||||
this = "King Basil" or
|
||||
this = "Stephen"
|
||||
}
|
||||
|
||||
/** Gets the hair color of the person. If the person is bald, there is no result. */
|
||||
@@ -836,12 +936,25 @@ class Person extends string {
|
||||
|
||||
/** Holds if the person is deceased. */
|
||||
predicate isDeceased() {
|
||||
this =
|
||||
[
|
||||
"Ernest", "Gertrude", "Oscar", "Lilian", "Edwin", "Raymond", "Elgar", "Elmer", "Herbert",
|
||||
"Maude", "Mae", "Otto", "Ophelia", "Parsley", "Sage", "Rosemary", "Thyme", "Garfunkel",
|
||||
"King Basil"
|
||||
]
|
||||
this = "Ernest" or
|
||||
this = "Gertrude" or
|
||||
this = "Oscar" or
|
||||
this = "Lilian" or
|
||||
this = "Edwin" or
|
||||
this = "Raymond" or
|
||||
this = "Elgar" or
|
||||
this = "Elmer" or
|
||||
this = "Herbert" or
|
||||
this = "Maude" or
|
||||
this = "Mae" or
|
||||
this = "Otto" or
|
||||
this = "Ophelia" or
|
||||
this = "Parsley" or
|
||||
this = "Sage" or
|
||||
this = "Rosemary" or
|
||||
this = "Thyme" or
|
||||
this = "Garfunkel" or
|
||||
this = "King Basil"
|
||||
}
|
||||
|
||||
/** Gets a parent of the person (alive or deceased). */
|
||||
@@ -1082,7 +1195,12 @@ class Person extends string {
|
||||
}
|
||||
|
||||
/** Holds if the person is allowed in the region. Initially, all villagers are allowed in every region. */
|
||||
predicate isAllowedIn(string region) { region = ["north", "south", "east", "west"] }
|
||||
predicate isAllowedIn(string region) {
|
||||
region = "north" or
|
||||
region = "south" or
|
||||
region = "east" or
|
||||
region = "west"
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns a parent of the person. */
|
||||
|
||||
@@ -18,7 +18,6 @@ where
|
||||
not lv1.isCompilerGenerated() and
|
||||
not lv2.isCompilerGenerated() and
|
||||
not lv1.getParentScope().(BlockStmt).isInMacroExpansion() and
|
||||
not lv2.getParentScope().(BlockStmt).isInMacroExpansion() and
|
||||
not lv1.getName() = "(unnamed local variable)"
|
||||
not lv2.getParentScope().(BlockStmt).isInMacroExpansion()
|
||||
select lv1, "Variable " + lv1.getName() + " hides another variable of the same name (on $@).", lv2,
|
||||
"line " + lv2.getLocation().getStartLine().toString()
|
||||
|
||||
@@ -1,26 +1,3 @@
|
||||
## 0.0.8
|
||||
|
||||
### New Queries
|
||||
|
||||
* 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.
|
||||
* 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
|
||||
|
||||
* Fix an issue with the `cpp/declaration-hides-variable` query where it would report variables that are unnamed in a database.
|
||||
* The `cpp/cleartext-storage-file` query has been upgraded with non-local taint flow and has been converted to a `path-problem` query.
|
||||
* The `cpp/return-stack-allocated-memory` query has been improved to produce fewer false positives. The
|
||||
query has also been converted to a `path-problem` query.
|
||||
* The "Cleartext transmission of sensitive information" (`cpp/cleartext-transmission`) query has been improved in several ways to reduce false positive results.
|
||||
* The "Potential improper null termination" (`cpp/improper-null-termination`) query now produces fewer false positive results around control flow branches and loops.
|
||||
* Added exception for GLib's gboolean to cpp/ambiguously-signed-bit-field.
|
||||
This change reduces the number of false positives in the query.
|
||||
|
||||
## 0.0.7
|
||||
|
||||
## 0.0.6
|
||||
|
||||
## 0.0.5
|
||||
|
||||
### New Queries
|
||||
|
||||
@@ -30,8 +30,8 @@ where
|
||||
// the next statement isn't breaking out of a switch
|
||||
not s.(BreakStmt).getBreakable() instanceof SwitchStmt and
|
||||
// the next statement isn't a loop that can be jumped into
|
||||
not s.(Loop).getStmt().getAChild*() instanceof LabelStmt and
|
||||
not s.(Loop).getStmt().getAChild*() instanceof SwitchCase and
|
||||
not exists(LabelStmt ls | s.(Loop).getStmt().getAChild*() = ls) and
|
||||
not exists(SwitchCase sc | s.(Loop).getStmt().getAChild*() = sc) and
|
||||
// no preprocessor logic applies
|
||||
not functionContainsPreprocCode(js.getEnclosingFunction())
|
||||
select js, "This statement makes $@ unreachable.", s, s.toString()
|
||||
|
||||
@@ -26,8 +26,6 @@ where
|
||||
// At least for C programs on Windows, BOOL is a common typedef for a type
|
||||
// representing BoolType.
|
||||
not bf.getType().hasName("BOOL") and
|
||||
// GLib's gboolean is a typedef for a type representing BoolType.
|
||||
not bf.getType().hasName("gboolean") and
|
||||
// If this is true, then there cannot be unsigned sign extension or overflow.
|
||||
not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 and
|
||||
not bf.isAnonymous() and
|
||||
|
||||
@@ -85,8 +85,7 @@ private predicate cancelingSubExprs(ComparisonOperation cmp, VariableAccess a1,
|
||||
exists(Variable v |
|
||||
exists(float m | m < 0 and cmpLinearSubVariable(cmp, v, a1, m)) and
|
||||
exists(float m | m > 0 and cmpLinearSubVariable(cmp, v, a2, m))
|
||||
) and
|
||||
not any(ClassTemplateInstantiation inst).getATemplateArgument() = cmp.getParent*()
|
||||
)
|
||||
}
|
||||
|
||||
from ComparisonOperation cmp, VariableAccess a1, VariableAccess a2
|
||||
|
||||
@@ -29,9 +29,7 @@ predicate pointlessSelfComparison(ComparisonOperation cmp) {
|
||||
not exists(lhs.getQualifier()) and // Avoid structure fields
|
||||
not exists(rhs.getQualifier()) and // Avoid structure fields
|
||||
not convertedExprMightOverflow(lhs) and
|
||||
not convertedExprMightOverflow(rhs) and
|
||||
// Don't warn if the comparison is part of a template argument.
|
||||
not any(ClassTemplateInstantiation inst).getATemplateArgument() = cmp.getParent*()
|
||||
not convertedExprMightOverflow(rhs)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The return value of a call to <code>snprintf</code> is the number of characters that <i>would have</i> been written to the buffer assuming there was sufficient space. In the event that the operation reaches the end of the buffer and more than one character is discarded, the return value will be greater than the buffer size. This can cause incorrect behavior, for example:
|
||||
<p>The return value of a call to <code>snprintf</code> is the number of characters that <i>would have</i> been written to the buffer assuming there was sufficient space. In the event that the operation reaches the end of the buffer and more than one character is discarded, the return value will be greater than the buffer size. This can cause incorrect behaviour, for example:
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user