mirror of
https://github.com/github/codeql.git
synced 2026-05-24 16:17:07 +02:00
Compare commits
1 Commits
testing/2/
...
release-pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ac5f83ea3 |
6
.github/labeler.yml
vendored
6
.github/labeler.yml
vendored
@@ -45,7 +45,11 @@ documentation:
|
||||
|
||||
# Since these are all shared files that need to be synced, just pick _one_ copy of each.
|
||||
"DataFlow Library":
|
||||
- "shared/dataflow/**/*"
|
||||
- "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll"
|
||||
- "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll"
|
||||
- "java/ql/lib/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll"
|
||||
- "java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll"
|
||||
- "java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll"
|
||||
|
||||
"ATM":
|
||||
- javascript/ql/experimental/adaptivethreatmodeling/**/*
|
||||
|
||||
2
.github/workflows/check-implicit-this.yml
vendored
2
.github/workflows/check-implicit-this.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check that implicit this warnings is enabled for all packs
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
2
.github/workflows/check-qldoc.yml
vendored
2
.github/workflows/check-qldoc.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
|
||||
2
.github/workflows/check-query-ids.yml
vendored
2
.github/workflows/check-query-ids.yml
vendored
@@ -16,6 +16,6 @@ jobs:
|
||||
name: Check query IDs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check for duplicate query IDs
|
||||
run: python3 misc/scripts/check-query-ids.py
|
||||
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
dotnet-version: 7.0.102
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
2
.github/workflows/compile-queries.yml
vendored
2
.github/workflows/compile-queries.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest-xl
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
with:
|
||||
|
||||
43
.github/workflows/csharp-qltest.yml
vendored
43
.github/workflows/csharp-qltest.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
qlupgrade:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- name: Check DB upgrade scripts
|
||||
run: |
|
||||
@@ -52,7 +52,8 @@ jobs:
|
||||
matrix:
|
||||
slice: ["1/2", "2/2"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: ./csharp/actions/create-extractor-pack
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
@@ -61,41 +62,25 @@ jobs:
|
||||
key: csharp-qltest-${{ matrix.slice }}
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
codeql test run --threads=0 --ram 50000 --slice ${{ matrix.slice }} --search-path extractor-pack --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
CODEQL_PATH=$(gh codeql version --format=json | jq -r .unpackedLocation)
|
||||
# The legacy ASP extractor is not in this repo, so take the one from the nightly build
|
||||
mv "$CODEQL_PATH/csharp/tools/extractor-asp.jar" "${{ github.workspace }}/csharp/extractor-pack/tools"
|
||||
# Safe guard against using the bundled extractor
|
||||
rm -rf "$CODEQL_PATH/csharp"
|
||||
codeql test run --threads=0 --ram 50000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}/csharp/extractor-pack" --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
unit-tests:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-2019]
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 7.0.102
|
||||
- name: Extractor unit tests
|
||||
run: |
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 extractor/Semmle.Util.Tests
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 extractor/Semmle.Extraction.Tests
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 autobuilder/Semmle.Autobuild.CSharp.Tests
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/extractor/Semmle.Util.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/extractor/Semmle.Extraction.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
|
||||
shell: bash
|
||||
stubgentest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: ./csharp/actions/create-extractor-pack
|
||||
- name: Run stub generator tests
|
||||
run: |
|
||||
# Generate (Asp)NetCore stubs
|
||||
STUBS_PATH=stubs_output
|
||||
python3 ql/src/Stubs/make_stubs_nuget.py webapp Swashbuckle.AspNetCore.Swagger latest "$STUBS_PATH"
|
||||
rm -rf ql/test/resources/stubs/_frameworks
|
||||
# Update existing stubs in the repo with the freshly generated ones
|
||||
mv "$STUBS_PATH/output/stubs/_frameworks" ql/test/resources/stubs/
|
||||
git status
|
||||
codeql test run --threads=0 --search-path extractor-pack --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries -- ql/test/library-tests/dataflow/flowsources/aspremote
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
|
||||
4
.github/workflows/csv-coverage-metrics.yml
vendored
4
.github/workflows/csv-coverage-metrics.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
- name: Create empty database
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
- name: Create empty database
|
||||
|
||||
@@ -31,11 +31,11 @@ jobs:
|
||||
GITHUB_CONTEXT: ${{ toJSON(github.event) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Clone self (github/codeql) - MERGE
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: merge
|
||||
- name: Clone self (github/codeql) - BASE
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
path: base
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
GITHUB_CONTEXT: ${{ toJSON(github.event) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Clone self (github/codeql)
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
|
||||
@@ -9,11 +9,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone self (github/codeql)
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: script
|
||||
- name: Clone self (github/codeql) for analysis
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: codeqlModels
|
||||
fetch-depth: 0
|
||||
|
||||
2
.github/workflows/csv-coverage-update.yml
vendored
2
.github/workflows/csv-coverage-update.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
GITHUB_CONTEXT: ${{ toJSON(github.event) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- name: Clone self (github/codeql)
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: ql
|
||||
fetch-depth: 0
|
||||
|
||||
4
.github/workflows/csv-coverage.yml
vendored
4
.github/workflows/csv-coverage.yml
vendored
@@ -13,11 +13,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone self (github/codeql)
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: script
|
||||
- name: Clone self (github/codeql) for analysis
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: codeqlModels
|
||||
ref: ${{ github.event.inputs.qlModelShaOverride || github.ref }}
|
||||
|
||||
2
.github/workflows/fast-forward.yml
vendored
2
.github/workflows/fast-forward.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Git config
|
||||
shell: bash
|
||||
|
||||
4
.github/workflows/go-tests-other-os.yml
vendored
4
.github/workflows/go-tests-other-os.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
2
.github/workflows/go-tests.yml
vendored
2
.github/workflows/go-tests.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up CodeQL CLI
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
65
.github/workflows/js-ml-tests.yml
vendored
Normal file
65
.github/workflows/js-ml-tests.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: JS ML-powered queries tests
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/**"
|
||||
- .github/workflows/js-ml-tests.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
branches:
|
||||
- main
|
||||
- "rc/*"
|
||||
pull_request:
|
||||
paths:
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/**"
|
||||
- .github/workflows/js-ml-tests.yml
|
||||
- .github/actions/fetch-codeql/action.yml
|
||||
- codeql-workspace.yml
|
||||
workflow_dispatch:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: javascript/ql/experimental/adaptivethreatmodeling
|
||||
|
||||
jobs:
|
||||
qltest:
|
||||
name: Test QL
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Install pack dependencies
|
||||
run: |
|
||||
for pack in modelbuilding src test; do
|
||||
codeql pack install --mode verify -- "${pack}"
|
||||
done
|
||||
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: js-ml-test
|
||||
|
||||
- name: Check QL compilation
|
||||
run: |
|
||||
codeql query compile \
|
||||
--check-only \
|
||||
--ram 50000 \
|
||||
--additional-packs "${{ github.workspace }}" \
|
||||
--threads=0 \
|
||||
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
|
||||
-- \
|
||||
lib modelbuilding src
|
||||
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
codeql test run \
|
||||
--threads=0 \
|
||||
--ram 50000 \
|
||||
--additional-packs "${{ github.workspace }}" \
|
||||
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
|
||||
-- \
|
||||
test
|
||||
4
.github/workflows/mad_modelDiff.yml
vendored
4
.github/workflows/mad_modelDiff.yml
vendored
@@ -27,12 +27,12 @@ jobs:
|
||||
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@v4
|
||||
uses: actions/checkout@v3
|
||||
if: github.event.pull_request
|
||||
with:
|
||||
path: codeql-pr
|
||||
- name: Clone github/codeql from main
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: codeql-main
|
||||
ref: main
|
||||
|
||||
4
.github/workflows/mad_regenerate-models.yml
vendored
4
.github/workflows/mad_regenerate-models.yml
vendored
@@ -27,11 +27,11 @@ jobs:
|
||||
ref: "placeholder"
|
||||
steps:
|
||||
- name: Clone self (github/codeql)
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup CodeQL binaries
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
- name: Clone repositories
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: repos/${{ matrix.ref }}
|
||||
ref: ${{ matrix.ref }}
|
||||
|
||||
2
.github/workflows/qhelp-pr-preview.yml
vendored
2
.github/workflows/qhelp-pr-preview.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 2
|
||||
persist-credentials: false
|
||||
|
||||
2
.github/workflows/ql-for-ql-build.yml
vendored
2
.github/workflows/ql-for-ql-build.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
### Build the queries ###
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Find codeql
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
- github/codeql
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Checkout ${{ matrix.repo }}
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ matrix.repo }}
|
||||
path: ${{ github.workspace }}/repo
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: measure
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: measurements
|
||||
|
||||
4
.github/workflows/ql-for-ql-tests.yml
vendored
4
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
qltest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@v2
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
needs: [qltest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install GNU tar
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
|
||||
2
.github/workflows/query-list.yml
vendored
2
.github/workflows/query-list.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone self (github/codeql)
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: codeql
|
||||
- name: Set up Python 3.8
|
||||
|
||||
8
.github/workflows/ruby-build.yml
vendored
8
.github/workflows/ruby-build.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install GNU tar
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
compile-queries:
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Fetch CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
- name: Cache compilation cache
|
||||
@@ -145,7 +145,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build, compile-queries]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: ruby.dbscheme
|
||||
@@ -206,7 +206,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: [package]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Fetch CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
|
||||
6
.github/workflows/ruby-dataset-measure.yml
vendored
6
.github/workflows/ruby-dataset-measure.yml
vendored
@@ -27,14 +27,14 @@ jobs:
|
||||
repo: [rails/rails, discourse/discourse, spree/spree, ruby/ruby]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- uses: ./ruby/actions/create-extractor-pack
|
||||
|
||||
- name: Checkout ${{ matrix.repo }}
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ matrix.repo }}
|
||||
path: ${{ github.workspace }}/repo
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
needs: measure
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: measurements
|
||||
|
||||
4
.github/workflows/ruby-qltest.yml
vendored
4
.github/workflows/ruby-qltest.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
qlupgrade:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- name: Check DB upgrade scripts
|
||||
run: |
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: ./ruby/actions/create-extractor-pack
|
||||
- name: Cache compilation cache
|
||||
|
||||
16
.github/workflows/swift.yml
vendored
16
.github/workflows/swift.yml
vendored
@@ -39,31 +39,31 @@ jobs:
|
||||
build-and-test-macos:
|
||||
runs-on: macos-12-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/build-and-test
|
||||
build-and-test-linux:
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/build-and-test
|
||||
qltests-linux:
|
||||
needs: build-and-test-linux
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-ql-tests
|
||||
qltests-macos:
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
needs: build-and-test-macos
|
||||
runs-on: macos-12-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-ql-tests
|
||||
integration-tests-linux:
|
||||
needs: build-and-test-linux
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-integration-tests
|
||||
integration-tests-macos:
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
@@ -71,13 +71,13 @@ jobs:
|
||||
runs-on: macos-12-xl
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./swift/actions/run-integration-tests
|
||||
codegen:
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: bazelbuild/setup-bazelisk@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
@@ -102,6 +102,6 @@ jobs:
|
||||
if : ${{ github.event_name == 'pull_request' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- uses: ./swift/actions/database-upgrade-scripts
|
||||
|
||||
2
.github/workflows/sync-files.yml
vendored
2
.github/workflows/sync-files.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check synchronized files
|
||||
run: python config/sync-files.py
|
||||
- name: Check dbscheme fragments
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check
|
||||
- name: Run tests
|
||||
@@ -35,12 +35,12 @@ jobs:
|
||||
fmt:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check formatting
|
||||
run: cargo fmt --check
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run clippy
|
||||
run: cargo clippy -- --no-deps -D warnings -A clippy::new_without_default -A clippy::too_many_arguments
|
||||
|
||||
2
.github/workflows/validate-change-notes.yml
vendored
2
.github/workflows/validate-change-notes.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForPathname.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll"
|
||||
],
|
||||
"TaintTracking Legacy Configuration Java/C++/C#/Go/Python/Ruby/Swift": [
|
||||
@@ -550,4 +552,4 @@
|
||||
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
|
||||
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
|
||||
]
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Make __is_trivial a builtin operation
|
||||
compatibility: full
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Treat functions that reach the end of the function as returning in the IR.
|
||||
They used to be treated as unreachable but it is allowed in C.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Functions that do not return due to calling functions that don't return (e.g. `exit`) are now detected as
|
||||
non-returning in the IR and dataflow.
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.10.0-dev
|
||||
version: 0.9.3
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -5,35 +5,155 @@
|
||||
import semmle.code.cpp.Element
|
||||
import semmle.code.cpp.Declaration
|
||||
import semmle.code.cpp.metrics.MetricFile
|
||||
private import codeql.util.FileSystem
|
||||
|
||||
private module Input implements InputSig {
|
||||
abstract class ContainerBase extends @container {
|
||||
abstract string getAbsolutePath();
|
||||
|
||||
ContainerBase getParentContainer() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
string toString() { result = this.getAbsolutePath() }
|
||||
}
|
||||
|
||||
class FolderBase extends ContainerBase, @folder {
|
||||
override string getAbsolutePath() { folders(underlyingElement(this), result) }
|
||||
}
|
||||
|
||||
class FileBase extends ContainerBase, @file {
|
||||
override string getAbsolutePath() { files(underlyingElement(this), result) }
|
||||
}
|
||||
|
||||
predicate hasSourceLocationPrefix = sourceLocationPrefix/1;
|
||||
}
|
||||
|
||||
private module Impl = Make<Input>;
|
||||
|
||||
/** A file or folder. */
|
||||
class Container extends Locatable, Impl::Container {
|
||||
override string toString() { result = Impl::Container.super.toString() }
|
||||
class Container extends Locatable, @container {
|
||||
/**
|
||||
* Gets the absolute, canonical path of this container, using forward slashes
|
||||
* as path separator.
|
||||
*
|
||||
* The path starts with a _root prefix_ followed by zero or more _path
|
||||
* segments_ separated by forward slashes.
|
||||
*
|
||||
* The root prefix is of one of the following forms:
|
||||
*
|
||||
* 1. A single forward slash `/` (Unix-style)
|
||||
* 2. An upper-case drive letter followed by a colon and a forward slash,
|
||||
* such as `C:/` (Windows-style)
|
||||
* 3. Two forward slashes, a computer name, and then another forward slash,
|
||||
* such as `//FileServer/` (UNC-style)
|
||||
*
|
||||
* Path segments are never empty (that is, absolute paths never contain two
|
||||
* contiguous slashes, except as part of a UNC-style root prefix). Also, path
|
||||
* segments never contain forward slashes, and no path segment is of the
|
||||
* form `.` (one dot) or `..` (two dots).
|
||||
*
|
||||
* Note that an absolute path never ends with a forward slash, except if it is
|
||||
* a bare root prefix, that is, the path has no path segments. A container
|
||||
* whose absolute path has no segments is always a `Folder`, not a `File`.
|
||||
*/
|
||||
string getAbsolutePath() { none() } // overridden by subclasses
|
||||
|
||||
/**
|
||||
* Gets the relative path of this file or folder from the root folder of the
|
||||
* analyzed source location. The relative path of the root folder itself is
|
||||
* the empty string.
|
||||
*
|
||||
* This has no result if the container is outside the source root, that is,
|
||||
* if the root folder is not a reflexive, transitive parent of this container.
|
||||
*/
|
||||
string getRelativePath() {
|
||||
exists(string absPath, string pref |
|
||||
absPath = this.getAbsolutePath() and sourceLocationPrefix(pref)
|
||||
|
|
||||
absPath = pref and result = ""
|
||||
or
|
||||
absPath = pref.regexpReplaceAll("/$", "") + "/" + result and
|
||||
not result.matches("/%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base name of this container including extension, that is, the last
|
||||
* segment of its absolute path, or the empty string if it has no segments.
|
||||
*
|
||||
* Here are some examples of absolute paths and the corresponding base names
|
||||
* (surrounded with quotes to avoid ambiguity):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Base name</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"tst.js"</td></tr>
|
||||
* <tr><td>"C:/Program Files (x86)"</td><td>"Program Files (x86)"</td></tr>
|
||||
* <tr><td>"/"</td><td>""</td></tr>
|
||||
* <tr><td>"C:/"</td><td>""</td></tr>
|
||||
* <tr><td>"D:/"</td><td>""</td></tr>
|
||||
* <tr><td>"//FileServer/"</td><td>""</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getBaseName() {
|
||||
result = this.getAbsolutePath().regexpCapture(".*/(([^/]*?)(?:\\.([^.]*))?)", 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the extension of this container, that is, the suffix of its base name
|
||||
* after the last dot character, if any.
|
||||
*
|
||||
* In particular,
|
||||
*
|
||||
* - if the name does not include a dot, there is no extension, so this
|
||||
* predicate has no result;
|
||||
* - if the name ends in a dot, the extension is the empty string;
|
||||
* - if the name contains multiple dots, the extension follows the last dot.
|
||||
*
|
||||
* Here are some examples of absolute paths and the corresponding extensions
|
||||
* (surrounded with quotes to avoid ambiguity):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Extension</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"js"</td></tr>
|
||||
* <tr><td>"/tmp/.classpath"</td><td>"classpath"</td></tr>
|
||||
* <tr><td>"/bin/bash"</td><td>not defined</td></tr>
|
||||
* <tr><td>"/tmp/tst2."</td><td>""</td></tr>
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"gz"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getExtension() {
|
||||
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(\\.([^.]*))?", 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stem of this container, that is, the prefix of its base name up to
|
||||
* (but not including) the last dot character if there is one, or the entire
|
||||
* base name if there is not.
|
||||
*
|
||||
* Here are some examples of absolute paths and the corresponding stems
|
||||
* (surrounded with quotes to avoid ambiguity):
|
||||
*
|
||||
* <table border="1">
|
||||
* <tr><th>Absolute path</th><th>Stem</th></tr>
|
||||
* <tr><td>"/tmp/tst.js"</td><td>"tst"</td></tr>
|
||||
* <tr><td>"/tmp/.classpath"</td><td>""</td></tr>
|
||||
* <tr><td>"/bin/bash"</td><td>"bash"</td></tr>
|
||||
* <tr><td>"/tmp/tst2."</td><td>"tst2"</td></tr>
|
||||
* <tr><td>"/tmp/x.tar.gz"</td><td>"x.tar"</td></tr>
|
||||
* </table>
|
||||
*/
|
||||
string getStem() {
|
||||
result = this.getAbsolutePath().regexpCapture(".*/([^/]*?)(?:\\.([^.]*))?", 1)
|
||||
}
|
||||
|
||||
/** Gets the parent container of this file or folder, if any. */
|
||||
Container getParentContainer() {
|
||||
containerparent(unresolveElement(result), underlyingElement(this))
|
||||
}
|
||||
|
||||
/** Gets a file or sub-folder in this container. */
|
||||
Container getAChildContainer() { this = result.getParentContainer() }
|
||||
|
||||
/** Gets a file in this container. */
|
||||
File getAFile() { result = this.getAChildContainer() }
|
||||
|
||||
/** Gets the file in this container that has the given `baseName`, if any. */
|
||||
File getFile(string baseName) {
|
||||
result = this.getAFile() and
|
||||
result.getBaseName() = baseName
|
||||
}
|
||||
|
||||
/** Gets a sub-folder in this container. */
|
||||
Folder getAFolder() { result = this.getAChildContainer() }
|
||||
|
||||
/** Gets the sub-folder in this container that has the given `baseName`, if any. */
|
||||
Folder getFolder(string baseName) {
|
||||
result = this.getAFolder() and
|
||||
result.getBaseName() = baseName
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a textual representation of the path of this container.
|
||||
*
|
||||
* This is the absolute path of the container.
|
||||
*/
|
||||
override string toString() { result = this.getAbsolutePath() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,7 +166,9 @@ class Container extends Locatable, Impl::Container {
|
||||
*
|
||||
* To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class Folder extends Container, Impl::Folder {
|
||||
class Folder extends Container, @folder {
|
||||
override string getAbsolutePath() { folders(underlyingElement(this), result) }
|
||||
|
||||
override Location getLocation() {
|
||||
result.getContainer() = this and
|
||||
result.hasLocationInfo(_, 0, 0, 0, 0)
|
||||
@@ -67,7 +189,9 @@ class Folder extends Container, Impl::Folder {
|
||||
* The base name further decomposes into the _stem_ and _extension_ -- see
|
||||
* `getStem` and `getExtension`. To get the full path, use `getAbsolutePath`.
|
||||
*/
|
||||
class File extends Container, Impl::File {
|
||||
class File extends Container, @file {
|
||||
override string getAbsolutePath() { files(underlyingElement(this), result) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "File" }
|
||||
|
||||
override Location getLocation() {
|
||||
|
||||
@@ -26,18 +26,17 @@ predicate callDereferences(FunctionCall fc, int i) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if evaluation of `op` dereferences `e` directly.
|
||||
*
|
||||
* This predicate does not recurse through function calls or arithmetic operations. To find
|
||||
* such cases, use `dereferencedByOperation`.
|
||||
* Holds if evaluation of `op` dereferences `e`.
|
||||
*/
|
||||
predicate directDereferencedByOperation(Expr op, Expr e) {
|
||||
predicate dereferencedByOperation(Expr op, Expr e) {
|
||||
exists(PointerDereferenceExpr deref |
|
||||
deref.getAChild() = e and
|
||||
deref = op and
|
||||
not deref.getParent*() instanceof SizeofOperator
|
||||
)
|
||||
or
|
||||
exists(CrementOperation crement | dereferencedByOperation(e, op) and crement.getOperand() = e)
|
||||
or
|
||||
exists(ArrayExpr ae |
|
||||
(
|
||||
not ae.getParent() instanceof AddressOfExpr and
|
||||
@@ -51,24 +50,6 @@ predicate directDereferencedByOperation(Expr op, Expr e) {
|
||||
)
|
||||
)
|
||||
or
|
||||
// ptr->Field
|
||||
e = op.(FieldAccess).getQualifier() and isClassPointerType(e.getType())
|
||||
or
|
||||
// ptr->method()
|
||||
e = op.(Call).getQualifier() and isClassPointerType(e.getType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if evaluation of `op` dereferences `e`.
|
||||
*
|
||||
* This includes the set of operations identified via `directDereferencedByOperation`, as well
|
||||
* as calls to function that are known to dereference an argument.
|
||||
*/
|
||||
predicate dereferencedByOperation(Expr op, Expr e) {
|
||||
directDereferencedByOperation(op, e)
|
||||
or
|
||||
exists(CrementOperation crement | dereferencedByOperation(e, op) and crement.getOperand() = e)
|
||||
or
|
||||
exists(AddressOfExpr addof, ArrayExpr ae |
|
||||
dereferencedByOperation(addof, op) and
|
||||
addof.getOperand() = ae and
|
||||
@@ -93,6 +74,12 @@ predicate dereferencedByOperation(Expr op, Expr e) {
|
||||
e = fc.getArgument(i) and
|
||||
op = fc
|
||||
)
|
||||
or
|
||||
// ptr->Field
|
||||
e = op.(FieldAccess).getQualifier() and isClassPointerType(e.getType())
|
||||
or
|
||||
// ptr->method()
|
||||
e = op.(Call).getQualifier() and isClassPointerType(e.getType())
|
||||
}
|
||||
|
||||
private predicate isClassPointerType(Type t) {
|
||||
|
||||
@@ -240,7 +240,7 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||
*/
|
||||
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
|
||||
exists(IRBlock irb |
|
||||
ir.controls(irb, testIsTrue) and
|
||||
forex(IRGuardCondition inst | inst = ir | inst.controls(irb, testIsTrue)) and
|
||||
irb.getAnInstruction().getAst().(ControlFlowNode).getBasicBlock() = controlled and
|
||||
not isUnreachedBlock(irb)
|
||||
)
|
||||
|
||||
@@ -79,3 +79,13 @@ class ArgumentPosition extends int {
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
/**
|
||||
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
|
||||
*
|
||||
* This is a temporary hook to support technical debt in the Go language; do not use.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate golangSpecificParamArgFilter(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
any()
|
||||
}
|
||||
|
||||
@@ -297,10 +297,6 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -297,10 +297,6 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -297,10 +297,6 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -297,10 +297,6 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -297,10 +297,6 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -208,8 +208,6 @@ predicate expectsContent(Node n, ContentSet c) { none() }
|
||||
|
||||
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
|
||||
|
||||
predicate localMustFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
Type getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
@@ -297,3 +295,12 @@ class ContentApprox = Unit;
|
||||
/** Gets an approximated value for content `c`. */
|
||||
pragma[inline]
|
||||
ContentApprox getContentApprox(Content c) { any() }
|
||||
|
||||
/**
|
||||
* Gets an additional term that is added to the `join` and `branch` computations to reflect
|
||||
* an additional forward or backwards branching factor that is not taken into account
|
||||
* when calculating the (virtual) dispatch cost.
|
||||
*
|
||||
* Argument `arg` is part of a path from a source to a sink, and `p` is the target parameter.
|
||||
*/
|
||||
int getAdditionalFlowIntoCallNodeTerm(ArgumentNode arg, ParameterNode p) { none() }
|
||||
|
||||
@@ -1547,21 +1547,3 @@ class BuiltInBitCast extends BuiltInOperation, @builtinbitcast {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInBitCast" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A C++ `__is_trivial` built-in operation (used by some implementations of the
|
||||
* `<type_traits>` header).
|
||||
*
|
||||
* Returns `true` if a type is a trivial type.
|
||||
* ```
|
||||
* template<typename _Tp>
|
||||
* struct is_trivial
|
||||
* : public integral_constant<bool, __is_trivial(_Tp)>
|
||||
* {};
|
||||
* ```
|
||||
*/
|
||||
class BuiltInIsTrivial extends BuiltInOperation, @istrivialexpr {
|
||||
override string toString() { result = "__is_trivial" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "BuiltInIsTrivial" }
|
||||
}
|
||||
|
||||
@@ -271,3 +271,13 @@ DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) {
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
/**
|
||||
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
|
||||
*
|
||||
* This is a temporary hook to support technical debt in the Go language; do not use.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate golangSpecificParamArgFilter(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
any()
|
||||
}
|
||||
|
||||
@@ -297,10 +297,6 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -297,10 +297,6 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -297,10 +297,6 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -297,10 +297,6 @@ private module Config implements FullStateConfigSig {
|
||||
|
||||
predicate isBarrierOut(Node node) { any(Configuration config).isBarrierOut(node) }
|
||||
|
||||
predicate isBarrierIn(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isBarrierOut(Node node, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(Node node1, Node node2) {
|
||||
singleConfiguration() and
|
||||
any(Configuration config).isAdditionalFlowStep(node1, node2)
|
||||
|
||||
@@ -18,6 +18,4 @@ module CppDataFlow implements InputSig {
|
||||
import Public
|
||||
|
||||
Node exprNode(DataFlowExpr e) { result = Public::exprNode(e) }
|
||||
|
||||
predicate getAdditionalFlowIntoCallNodeTerm = Private::getAdditionalFlowIntoCallNodeTerm/2;
|
||||
}
|
||||
|
||||
@@ -804,8 +804,6 @@ predicate expectsContent(Node n, ContentSet c) { none() }
|
||||
|
||||
predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() }
|
||||
|
||||
predicate localMustFlowStep(Node node1, Node node2) { none() }
|
||||
|
||||
/** Gets the type of `n` used for type pruning. */
|
||||
DataFlowType getNodeType(Node n) {
|
||||
suppressUnusedNode(n) and
|
||||
|
||||
@@ -193,23 +193,13 @@ class Node extends TIRDataFlowNode {
|
||||
* a `Conversion`, then the result is the underlying non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
Expr asExpr() { result = this.asExpr(_) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr asExpr(int n) { result = this.(ExprNode).getExpr(n) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr asIndirectExpr(int n, int index) { result = this.(IndirectExprNode).getExpr(n, index) }
|
||||
Expr asExpr() { result = this.(ExprNode).getExpr() }
|
||||
|
||||
/**
|
||||
* Gets the non-conversion expression that's indirectly tracked by this node
|
||||
* under `index` number of indirections.
|
||||
*/
|
||||
Expr asIndirectExpr(int index) { result = this.asIndirectExpr(_, index) }
|
||||
Expr asIndirectExpr(int index) { result = this.(IndirectExprNode).getExpr(index) }
|
||||
|
||||
/**
|
||||
* Gets the non-conversion expression that's indirectly tracked by this node
|
||||
@@ -221,26 +211,15 @@ class Node extends TIRDataFlowNode {
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
Expr asConvertedExpr() { result = this.asConvertedExpr(_) }
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
Expr asConvertedExpr(int n) { result = this.(ExprNode).getConvertedExpr(n) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr asIndirectConvertedExpr(int n, int index) {
|
||||
result = this.(IndirectExprNode).getConvertedExpr(n, index)
|
||||
}
|
||||
Expr asConvertedExpr() { result = this.(ExprNode).getConvertedExpr() }
|
||||
|
||||
/**
|
||||
* Gets the expression that's indirectly tracked by this node
|
||||
* behind `index` number of indirections.
|
||||
*/
|
||||
Expr asIndirectConvertedExpr(int index) { result = this.asIndirectConvertedExpr(_, index) }
|
||||
Expr asIndirectConvertedExpr(int index) {
|
||||
result = this.(IndirectExprNode).getConvertedExpr(index)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that's indirectly tracked by this node behind a
|
||||
@@ -275,7 +254,9 @@ class Node extends TIRDataFlowNode {
|
||||
* after the `f` has returned.
|
||||
*/
|
||||
Expr asDefiningArgument(int index) {
|
||||
this.(DefinitionByReferenceNode).getIndirectionIndex() = index and
|
||||
// Subtract one because `DefinitionByReferenceNode` is defined to be in
|
||||
// the range `[0 ... n - 1]` for some `n` instead of `[1 ... n]`.
|
||||
this.(DefinitionByReferenceNode).getIndirectionIndex() = index - 1 and
|
||||
result = this.(DefinitionByReferenceNode).getArgument()
|
||||
}
|
||||
|
||||
@@ -412,10 +393,9 @@ class Node extends TIRDataFlowNode {
|
||||
}
|
||||
|
||||
private string toExprString(Node n) {
|
||||
result = n.asExpr(0).toString()
|
||||
result = n.asExpr().toString()
|
||||
or
|
||||
not exists(n.asExpr()) and
|
||||
result = n.asIndirectExpr(0, 1).toString() + " indirection"
|
||||
result = n.asIndirectExpr().toString() + " indirection"
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -955,7 +935,7 @@ class RawIndirectOperand extends Node, TRawIndirectOperand {
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = operandNode(this.getOperand()).toStringImpl() + " indirection"
|
||||
result = instructionNode(this.getOperand().getDef()).toStringImpl() + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1062,130 +1042,77 @@ class RawIndirectInstruction extends Node, TRawIndirectInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
private module GetConvertedResultExpression {
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
|
||||
|
||||
private Operand getAnInitializeDynamicAllocationInstructionAddress() {
|
||||
result = any(InitializeDynamicAllocationInstruction init).getAllocationAddressOperand()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the expression that should be returned as the result expression from `instr`.
|
||||
*
|
||||
* Note that this predicate may return multiple results in cases where a conversion belongs to a
|
||||
* different AST element than its operand.
|
||||
*/
|
||||
Expr getConvertedResultExpression(Instruction instr, int n) {
|
||||
// Only fully converted instructions have a result for `asConvertedExpr`
|
||||
not conversionFlow(unique(Operand op |
|
||||
// The address operand of a `InitializeDynamicAllocationInstruction` is
|
||||
// special: we need to handle it during dataflow (since it's
|
||||
// effectively a store to an indirection), but it doesn't appear in
|
||||
// source syntax, so dataflow node <-> expression conversion shouldn't
|
||||
// care about it.
|
||||
op = getAUse(instr) and not op = getAnInitializeDynamicAllocationInstructionAddress()
|
||||
|
|
||||
op
|
||||
), _, false, false) and
|
||||
result = getConvertedResultExpressionImpl(instr) and
|
||||
n = 0
|
||||
or
|
||||
// If the conversion also has a result then we return multiple results
|
||||
exists(Operand operand | conversionFlow(operand, instr, false, false) |
|
||||
n = 1 and
|
||||
result = getConvertedResultExpressionImpl(operand.getDef())
|
||||
or
|
||||
result = getConvertedResultExpression(operand.getDef(), n - 1)
|
||||
)
|
||||
}
|
||||
|
||||
private Expr getConvertedResultExpressionImpl0(Instruction instr) {
|
||||
// For an expression such as `i += 2` we pretend that the generated
|
||||
// `StoreInstruction` contains the result of the expression even though
|
||||
// this isn't totally aligned with the C/C++ standard.
|
||||
exists(TranslatedAssignOperation tao |
|
||||
result = tao.getExpr() and
|
||||
instr = tao.getInstruction(any(AssignmentStoreTag tag))
|
||||
)
|
||||
or
|
||||
// Similarly for `i++` and `++i` we pretend that the generated
|
||||
// `StoreInstruction` is contains the result of the expression even though
|
||||
// this isn't totally aligned with the C/C++ standard.
|
||||
exists(TranslatedCrementOperation tco |
|
||||
result = tco.getExpr() and
|
||||
instr = tco.getInstruction(any(CrementStoreTag tag))
|
||||
)
|
||||
or
|
||||
// IR construction inserts an additional cast to a `size_t` on the extent
|
||||
// of a `new[]` expression. The resulting `ConvertInstruction` doesn't have
|
||||
// a result for `getConvertedResultExpression`. We remap this here so that
|
||||
// this `ConvertInstruction` maps to the result of the expression that
|
||||
// represents the extent.
|
||||
exists(TranslatedNonConstantAllocationSize tas |
|
||||
result = tas.getExtent().getExpr() and
|
||||
instr = tas.getInstruction(any(AllocationExtentConvertTag tag))
|
||||
)
|
||||
or
|
||||
// There's no instruction that returns `ParenthesisExpr`, but some queries
|
||||
// expect this
|
||||
exists(TranslatedTransparentConversion ttc |
|
||||
result = ttc.getExpr().(ParenthesisExpr) and
|
||||
instr = ttc.getResult()
|
||||
)
|
||||
}
|
||||
|
||||
private Expr getConvertedResultExpressionImpl(Instruction instr) {
|
||||
result = getConvertedResultExpressionImpl0(instr)
|
||||
or
|
||||
not exists(getConvertedResultExpressionImpl0(instr)) and
|
||||
result = instr.getConvertedResultExpression()
|
||||
}
|
||||
}
|
||||
|
||||
private import GetConvertedResultExpression
|
||||
|
||||
/** Holds if `node` is an `OperandNode` that should map `node.asExpr()` to `e`. */
|
||||
predicate exprNodeShouldBeOperand(OperandNode node, Expr e, int n) {
|
||||
predicate exprNodeShouldBeOperand(OperandNode node, Expr e) {
|
||||
exists(Instruction def |
|
||||
unique( | | getAUse(def)) = node.getOperand() and
|
||||
e = getConvertedResultExpression(def, n)
|
||||
e = def.getConvertedResultExpression()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate indirectExprNodeShouldBeIndirectOperand0(
|
||||
VariableAddressInstruction instr, RawIndirectOperand node, Expr e
|
||||
) {
|
||||
instr = node.getOperand().getDef() and
|
||||
e = instr.getAst().(Expr).getUnconverted()
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectOperand` that maps `node.asIndirectExpr()` to `e`. */
|
||||
private predicate indirectExprNodeShouldBeIndirectOperand(
|
||||
IndirectOperand node, Expr e, int n, int indirectionIndex
|
||||
) {
|
||||
exists(Instruction def |
|
||||
node.hasOperandAndIndirectionIndex(unique( | | getAUse(def)), indirectionIndex) and
|
||||
e = getConvertedResultExpression(def, n)
|
||||
private predicate indirectExprNodeShouldBeIndirectOperand(RawIndirectOperand node, Expr e) {
|
||||
exists(Instruction instr | instr = node.getOperand().getDef() |
|
||||
exists(Expr e0 |
|
||||
indirectExprNodeShouldBeIndirectOperand0(instr, node, e0) and
|
||||
e = e0.getFullyConverted()
|
||||
)
|
||||
or
|
||||
not indirectExprNodeShouldBeIndirectOperand0(_, node, _) and
|
||||
e = instr.getConvertedResultExpression()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node, Expr e, int n) {
|
||||
private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node, Expr e) {
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() instanceof Constructor and
|
||||
e = getConvertedResultExpression(call, n) and
|
||||
e = call.getConvertedResultExpression() and
|
||||
call.getThisArgumentOperand() = node.getAddressOperand()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an instruction node that maps `node.asExpr()` to `e`. */
|
||||
predicate exprNodeShouldBeInstruction(Node node, Expr e, int n) {
|
||||
not exprNodeShouldBeOperand(_, e, n) and
|
||||
not exprNodeShouldBeIndirectOutNode(_, e, n) and
|
||||
e = getConvertedResultExpression(node.asInstruction(), n)
|
||||
predicate exprNodeShouldBeInstruction(Node node, Expr e) {
|
||||
not exprNodeShouldBeOperand(_, e) and
|
||||
not exprNodeShouldBeIndirectOutNode(_, e) and
|
||||
(
|
||||
e = node.asInstruction().getConvertedResultExpression()
|
||||
or
|
||||
// The instruction that contains the result of an `AssignOperation` is
|
||||
// the unloaded left operand (see the comments in `TranslatedAssignOperation`).
|
||||
// That means that for cases like
|
||||
// ```cpp
|
||||
// int x = ...;
|
||||
// x += 1;
|
||||
// ```
|
||||
// the result of `x += 1` is the `VariableAddressInstruction` that represents `x`. But
|
||||
// that instruction doesn't receive the flow from this `AssignOperation`. So instead we
|
||||
// map the operation to the `AddInstruction`.
|
||||
node.asInstruction().getAst() = e.(AssignOperation)
|
||||
or
|
||||
// Same story for `CrementOperation`s (cf. the comments in the subclasses
|
||||
// of `TranslatedCrementOperation`).
|
||||
node.asInstruction().getAst() = e.(CrementOperation)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectInstruction` that maps `node.asIndirectExpr()` to `e`. */
|
||||
predicate indirectExprNodeShouldBeIndirectInstruction(
|
||||
IndirectInstruction node, Expr e, int n, int indirectionIndex
|
||||
) {
|
||||
not indirectExprNodeShouldBeIndirectOperand(_, e, n, indirectionIndex) and
|
||||
predicate indirectExprNodeShouldBeIndirectInstruction(IndirectInstruction node, Expr e) {
|
||||
exists(Instruction instr |
|
||||
node.hasInstructionAndIndirectionIndex(instr, indirectionIndex) and
|
||||
e = getConvertedResultExpression(instr, n)
|
||||
node.hasInstructionAndIndirectionIndex(instr, _) and
|
||||
not indirectExprNodeShouldBeIndirectOperand(_, e)
|
||||
|
|
||||
e = instr.(VariableAddressInstruction).getAst().(Expr).getFullyConverted()
|
||||
or
|
||||
not instr instanceof VariableAddressInstruction and
|
||||
e = instr.getConvertedResultExpression()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1194,32 +1121,30 @@ abstract private class ExprNodeBase extends Node {
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
abstract Expr getConvertedExpr(int n);
|
||||
abstract Expr getConvertedExpr();
|
||||
|
||||
/** Gets the non-conversion expression corresponding to this node, if any. */
|
||||
final Expr getExpr(int n) { result = this.getConvertedExpr(n).getUnconverted() }
|
||||
abstract Expr getExpr();
|
||||
}
|
||||
|
||||
private class InstructionExprNode extends ExprNodeBase, InstructionNode {
|
||||
InstructionExprNode() {
|
||||
exists(Expr e, int n |
|
||||
exprNodeShouldBeInstruction(this, e, n) and
|
||||
not exprNodeShouldBeInstruction(_, e, n + 1)
|
||||
)
|
||||
}
|
||||
InstructionExprNode() { exprNodeShouldBeInstruction(this, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeInstruction(this, result, n) }
|
||||
final override Expr getConvertedExpr() { exprNodeShouldBeInstruction(this, result) }
|
||||
|
||||
final override Expr getExpr() { result = this.getConvertedExpr().getUnconverted() }
|
||||
|
||||
final override string toStringImpl() { result = this.getConvertedExpr().toString() }
|
||||
}
|
||||
|
||||
private class OperandExprNode extends ExprNodeBase, OperandNode {
|
||||
OperandExprNode() {
|
||||
exists(Expr e, int n |
|
||||
exprNodeShouldBeOperand(this, e, n) and
|
||||
not exprNodeShouldBeOperand(_, e, n + 1)
|
||||
)
|
||||
}
|
||||
OperandExprNode() { exprNodeShouldBeOperand(this, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeOperand(this, result, n) }
|
||||
final override Expr getConvertedExpr() { exprNodeShouldBeOperand(this, result) }
|
||||
|
||||
final override Expr getExpr() { result = this.getConvertedExpr().getUnconverted() }
|
||||
|
||||
final override string toStringImpl() { result = this.getConvertedExpr().toString() }
|
||||
}
|
||||
|
||||
abstract private class IndirectExprNodeBase extends Node {
|
||||
@@ -1227,75 +1152,67 @@ abstract private class IndirectExprNodeBase extends Node {
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
abstract Expr getConvertedExpr(int n, int indirectionIndex);
|
||||
abstract Expr getConvertedExpr(int indirectionIndex);
|
||||
|
||||
/** Gets the non-conversion expression corresponding to this node, if any. */
|
||||
final Expr getExpr(int n, int indirectionIndex) {
|
||||
result = this.getConvertedExpr(n, indirectionIndex).getUnconverted()
|
||||
abstract Expr getExpr(int indirectionIndex);
|
||||
}
|
||||
|
||||
private class IndirectOperandIndirectExprNode extends IndirectExprNodeBase, RawIndirectOperand {
|
||||
IndirectOperandIndirectExprNode() { indirectExprNodeShouldBeIndirectOperand(this, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int index) {
|
||||
this.getIndirectionIndex() = index and
|
||||
indirectExprNodeShouldBeIndirectOperand(this, result)
|
||||
}
|
||||
|
||||
final override Expr getExpr(int index) {
|
||||
this.getIndirectionIndex() = index and
|
||||
result = this.getConvertedExpr(index).getUnconverted()
|
||||
}
|
||||
}
|
||||
|
||||
private class IndirectOperandIndirectExprNode extends IndirectExprNodeBase instanceof IndirectOperand
|
||||
private class IndirectInstructionIndirectExprNode extends IndirectExprNodeBase,
|
||||
RawIndirectInstruction
|
||||
{
|
||||
IndirectOperandIndirectExprNode() {
|
||||
exists(Expr e, int n, int indirectionIndex |
|
||||
indirectExprNodeShouldBeIndirectOperand(this, e, n, indirectionIndex) and
|
||||
not indirectExprNodeShouldBeIndirectOperand(_, e, n + 1, indirectionIndex)
|
||||
)
|
||||
IndirectInstructionIndirectExprNode() { indirectExprNodeShouldBeIndirectInstruction(this, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int index) {
|
||||
this.getIndirectionIndex() = index and
|
||||
indirectExprNodeShouldBeIndirectInstruction(this, result)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr(int n, int index) {
|
||||
indirectExprNodeShouldBeIndirectOperand(this, result, n, index)
|
||||
}
|
||||
}
|
||||
|
||||
private class IndirectInstructionIndirectExprNode extends IndirectExprNodeBase instanceof IndirectInstruction
|
||||
{
|
||||
IndirectInstructionIndirectExprNode() {
|
||||
exists(Expr e, int n, int indirectionIndex |
|
||||
indirectExprNodeShouldBeIndirectInstruction(this, e, n, indirectionIndex) and
|
||||
not indirectExprNodeShouldBeIndirectInstruction(_, e, n + 1, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr(int n, int index) {
|
||||
indirectExprNodeShouldBeIndirectInstruction(this, result, n, index)
|
||||
final override Expr getExpr(int index) {
|
||||
this.getIndirectionIndex() = index and
|
||||
result = this.getConvertedExpr(index).getUnconverted()
|
||||
}
|
||||
}
|
||||
|
||||
private class IndirectArgumentOutExprNode extends ExprNodeBase, IndirectArgumentOutNode {
|
||||
IndirectArgumentOutExprNode() { exprNodeShouldBeIndirectOutNode(this, _, _) }
|
||||
IndirectArgumentOutExprNode() { exprNodeShouldBeIndirectOutNode(this, _) }
|
||||
|
||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOutNode(this, result, n) }
|
||||
final override Expr getConvertedExpr() { exprNodeShouldBeIndirectOutNode(this, result) }
|
||||
|
||||
final override Expr getExpr() { result = this.getConvertedExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*/
|
||||
class ExprNode extends Node instanceof ExprNodeBase {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getExpr(int n) { result = super.getExpr(n) }
|
||||
|
||||
/**
|
||||
* Gets the non-conversion expression corresponding to this node, if any. If
|
||||
* this node strictly (in the sense of `getConvertedExpr`) corresponds to a
|
||||
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
final Expr getExpr() { result = this.getExpr(_) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getConvertedExpr(int n) { result = super.getConvertedExpr(n) }
|
||||
Expr getExpr() { result = super.getExpr() }
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
final Expr getConvertedExpr() { result = this.getConvertedExpr(_) }
|
||||
Expr getConvertedExpr() { result = super.getConvertedExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1308,27 +1225,13 @@ class IndirectExprNode extends Node instanceof IndirectExprNodeBase {
|
||||
* `Conversion`, then the result is that `Conversion`'s non-`Conversion` base
|
||||
* expression.
|
||||
*/
|
||||
final Expr getExpr(int indirectionIndex) { result = this.getExpr(_, indirectionIndex) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getExpr(int n, int indirectionIndex) { result = super.getExpr(n, indirectionIndex) }
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*/
|
||||
Expr getConvertedExpr(int n, int indirectionIndex) {
|
||||
result = super.getConvertedExpr(n, indirectionIndex)
|
||||
}
|
||||
Expr getExpr(int indirectionIndex) { result = super.getExpr(indirectionIndex) }
|
||||
|
||||
/**
|
||||
* Gets the expression corresponding to this node, if any. The returned
|
||||
* expression may be a `Conversion`.
|
||||
*/
|
||||
Expr getConvertedExpr(int indirectionIndex) {
|
||||
result = this.getConvertedExpr(_, indirectionIndex)
|
||||
}
|
||||
Expr getConvertedExpr(int indirectionIndex) { result = super.getConvertedExpr(indirectionIndex) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1354,9 +1257,6 @@ class ParameterNode extends Node {
|
||||
* pointer-indirection parameters are at further negative positions.
|
||||
*/
|
||||
predicate isParameterOf(Function f, ParameterPosition pos) { none() } // overridden by subclasses
|
||||
|
||||
/** Gets the `Parameter` associated with this node, if it exists. */
|
||||
Parameter getParameter() { none() } // overridden by subclasses
|
||||
}
|
||||
|
||||
/** An explicit positional parameter, including `this`, but not `...`. */
|
||||
@@ -1379,9 +1279,10 @@ private class ExplicitParameterNode extends ParameterNode, DirectParameterNode {
|
||||
f.getParameter(pos.(DirectPosition).getIndex()) = instr.getParameter()
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = instr.getParameter().toString() }
|
||||
/** Gets the `Parameter` associated with this node. */
|
||||
Parameter getParameter() { result = instr.getParameter() }
|
||||
|
||||
override Parameter getParameter() { result = instr.getParameter() }
|
||||
override string toStringImpl() { result = instr.getParameter().toString() }
|
||||
}
|
||||
|
||||
/** An implicit `this` parameter. */
|
||||
@@ -1543,7 +1444,7 @@ OperandNode operandNode(Operand operand) { result.getOperand() = operand }
|
||||
* _out of_ an expression, like when an argument is passed by reference, use
|
||||
* `definitionByReferenceNodeFromArgument` instead.
|
||||
*/
|
||||
ExprNode exprNode(Expr e) { result.getExpr(_) = e }
|
||||
ExprNode exprNode(Expr e) { result.getExpr() = e }
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to the value of evaluating `e`. Here, `e` may
|
||||
@@ -1551,7 +1452,7 @@ ExprNode exprNode(Expr e) { result.getExpr(_) = e }
|
||||
* argument is passed by reference, use
|
||||
* `definitionByReferenceNodeFromArgument` instead.
|
||||
*/
|
||||
ExprNode convertedExprNode(Expr e) { result.getConvertedExpr(_) = e }
|
||||
ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e }
|
||||
|
||||
/**
|
||||
* Gets the `Node` corresponding to the value of `p` at function entry.
|
||||
|
||||
@@ -447,16 +447,9 @@ class GlobalUse extends UseImpl, TGlobalUse {
|
||||
IRFunction getIRFunction() { result = f }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
// Similar to the `FinalParameterUse` case, we want to generate flow out of
|
||||
// globals at any exit so that we can flow out of non-returning functions.
|
||||
// Obviously this isn't correct as we can't actually flow but the global flow
|
||||
// requires this if we want to flow into children.
|
||||
exists(Instruction return |
|
||||
return instanceof ReturnInstruction or
|
||||
return instanceof UnreachedInstruction
|
||||
|
|
||||
block.getInstruction(index) = return and
|
||||
return.getEnclosingIRFunction() = f
|
||||
exists(ExitFunctionInstruction exit |
|
||||
exit = f.getExitFunctionInstruction() and
|
||||
block.getInstruction(index) = exit
|
||||
)
|
||||
}
|
||||
|
||||
@@ -825,7 +818,7 @@ predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) {
|
||||
or
|
||||
exists(PhiNode phiTo |
|
||||
phi != phiTo and
|
||||
lastRefRedefExt(phi, bb1, i1, phiTo) and
|
||||
lastRefRedefExt(phi, _, _, phiTo) and
|
||||
nodeTo.(SsaPhiNode).getPhiNode() = phiTo
|
||||
)
|
||||
)
|
||||
|
||||
@@ -405,6 +405,9 @@ predicate hasUnreachedInstruction(IRFunction func) {
|
||||
exists(Call c |
|
||||
c.getEnclosingFunction() = func.getFunction() and
|
||||
any(Options opt).exits(c.getTarget())
|
||||
) and
|
||||
not exists(TranslatedUnreachableReturnStmt return |
|
||||
return.getEnclosingFunction().getFunction() = func.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -824,9 +824,6 @@ abstract class TranslatedElement extends TTranslatedElement {
|
||||
/** DEPRECATED: Alias for getAst */
|
||||
deprecated Locatable getAST() { result = this.getAst() }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { result = this.getAst().getLocation() }
|
||||
|
||||
/**
|
||||
* Get the first instruction to be executed in the evaluation of this element.
|
||||
*/
|
||||
|
||||
@@ -1906,10 +1906,8 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
final override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
resultType = getTypeForPRValue(expr.getAllocator().getParameter(0).getType()) and
|
||||
(
|
||||
this.extentNeedsConversion() and
|
||||
// Convert the extent to `size_t`, because the AST doesn't do this already.
|
||||
tag = AllocationExtentConvertTag() and
|
||||
opcode instanceof Opcode::Convert
|
||||
tag = AllocationExtentConvertTag() and opcode instanceof Opcode::Convert
|
||||
or
|
||||
tag = AllocationElementSizeTag() and opcode instanceof Opcode::Constant
|
||||
or
|
||||
@@ -1920,7 +1918,6 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
final override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
|
||||
kind instanceof GotoEdge and
|
||||
(
|
||||
this.extentNeedsConversion() and
|
||||
tag = AllocationExtentConvertTag() and
|
||||
result = this.getInstruction(AllocationElementSizeTag())
|
||||
or
|
||||
@@ -1936,9 +1933,7 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
|
||||
final override Instruction getChildSuccessor(TranslatedElement child) {
|
||||
child = this.getExtent() and
|
||||
if this.extentNeedsConversion()
|
||||
then result = this.getInstruction(AllocationExtentConvertTag())
|
||||
else result = this.getInstruction(AllocationElementSizeTag())
|
||||
result = this.getInstruction(AllocationExtentConvertTag())
|
||||
}
|
||||
|
||||
final override string getInstructionConstantValue(InstructionTag tag) {
|
||||
@@ -1950,31 +1945,19 @@ class TranslatedNonConstantAllocationSize extends TranslatedAllocationSize {
|
||||
tag = AllocationSizeTag() and
|
||||
(
|
||||
operandTag instanceof LeftOperandTag and
|
||||
(
|
||||
if this.extentNeedsConversion()
|
||||
then result = this.getInstruction(AllocationExtentConvertTag())
|
||||
else result = this.getExtent().getResult()
|
||||
)
|
||||
result = this.getInstruction(AllocationExtentConvertTag())
|
||||
or
|
||||
operandTag instanceof RightOperandTag and
|
||||
result = this.getInstruction(AllocationElementSizeTag())
|
||||
)
|
||||
or
|
||||
this.extentNeedsConversion() and
|
||||
tag = AllocationExtentConvertTag() and
|
||||
operandTag instanceof UnaryOperandTag and
|
||||
result = this.getExtent().getResult()
|
||||
}
|
||||
|
||||
TranslatedExpr getExtent() { result = getTranslatedExpr(expr.getExtent().getFullyConverted()) }
|
||||
|
||||
/**
|
||||
* Holds if the result of `expr.getExtent()` does not have the same type as
|
||||
* the allocator's size parameter.
|
||||
*/
|
||||
private predicate extentNeedsConversion() {
|
||||
expr.getExtent().getFullyConverted().getUnspecifiedType() !=
|
||||
expr.getAllocator().getParameter(0).getUnspecifiedType()
|
||||
private TranslatedExpr getExtent() {
|
||||
result = getTranslatedExpr(expr.getExtent().getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ class TranslatedStaticStorageDurationVarInit extends TranslatedRootElement,
|
||||
|
||||
final override Declaration getFunction() { result = var }
|
||||
|
||||
final Location getLocation() { result = var.getLocation() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getInstruction(EnterFunctionTag()) }
|
||||
|
||||
override TranslatedElement getChild(int n) {
|
||||
|
||||
@@ -442,26 +442,29 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
|
||||
|
||||
/**
|
||||
* The IR translation of an implicit `return` statement generated by the extractor to handle control
|
||||
* flow that reaches the end of a non-`void`-returning function body. Such control flow
|
||||
* produces undefined behavior in C++ but not in C. However even in C using the return value is
|
||||
* undefined behaviour. We make it return uninitialized memory to get as much flow as possible.
|
||||
* flow that reaches the end of a non-`void`-returning function body. Since such control flow
|
||||
* produces undefined behavior, we simply generate an `Unreached` instruction to prevent that flow
|
||||
* from continuing on to pollute other analysis. The assumption is that the developer is certain
|
||||
* that the implicit `return` is unreachable, even if the compiler cannot prove it.
|
||||
*/
|
||||
class TranslatedNoValueReturnStmt extends TranslatedReturnStmt, TranslatedVariableInitialization {
|
||||
TranslatedNoValueReturnStmt() {
|
||||
class TranslatedUnreachableReturnStmt extends TranslatedReturnStmt {
|
||||
TranslatedUnreachableReturnStmt() {
|
||||
not stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction())
|
||||
}
|
||||
|
||||
final override Instruction getInitializationSuccessor() {
|
||||
result = this.getEnclosingFunction().getReturnSuccessorInstruction()
|
||||
override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::Unreached and
|
||||
resultType = getVoidType()
|
||||
}
|
||||
|
||||
final override Type getTargetType() { result = this.getEnclosingFunction().getReturnType() }
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
final override TranslatedInitialization getInitialization() { none() }
|
||||
|
||||
final override IRVariable getIRVariable() {
|
||||
result = this.getEnclosingFunction().getReturnVariable()
|
||||
}
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,65 +10,6 @@ predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
|
||||
or
|
||||
instr.getSuccessor(kind) instanceof UnreachedInstruction and
|
||||
kind instanceof GotoEdge
|
||||
or
|
||||
isCallToNonReturningFunction(instr) and exists(instr.getSuccessor(kind))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all calls to `f` never return (e.g. they call `exit` or loop forever)
|
||||
*/
|
||||
private predicate isNonReturningFunction(IRFunction f) {
|
||||
// If the function has an instruction with a missing successor then
|
||||
// the analysis is probably going to be incorrect, so assume they exit.
|
||||
not hasInstructionWithMissingSuccessor(f) and
|
||||
(
|
||||
// If all flows to the exit block are pass through an unreachable then f never returns.
|
||||
any(UnreachedInstruction instr).getBlock().postDominates(f.getEntryBlock())
|
||||
or
|
||||
// If there is no flow to the exit block then f never returns.
|
||||
not exists(IRBlock entry, IRBlock exit |
|
||||
exit = f.getExitFunctionInstruction().getBlock() and
|
||||
entry = f.getEntryBlock() and
|
||||
exit = entry.getASuccessor*()
|
||||
)
|
||||
or
|
||||
// If all flows to the exit block are pass through a call that never returns then f never returns.
|
||||
exists(CallInstruction ci |
|
||||
ci.getBlock().postDominates(f.getEntryBlock()) and
|
||||
isCallToNonReturningFunction(ci)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` has an instruction with a missing successor.
|
||||
* This matches `instructionWithoutSuccessor` from `IRConsistency`, but
|
||||
* avoids generating the error strings.
|
||||
*/
|
||||
predicate hasInstructionWithMissingSuccessor(IRFunction f) {
|
||||
exists(Instruction missingSucc |
|
||||
missingSucc.getEnclosingIRFunction() = f and
|
||||
not exists(missingSucc.getASuccessor()) and
|
||||
not missingSucc instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not missingSucc instanceof PhiInstruction and
|
||||
not missingSucc instanceof UnreachedInstruction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call `ci` never returns.
|
||||
*/
|
||||
private predicate isCallToNonReturningFunction(CallInstruction ci) {
|
||||
exists(IRFunction callee, Language::Function staticTarget |
|
||||
staticTarget = ci.getStaticCallTarget() and
|
||||
staticTarget = callee.getFunction() and
|
||||
// We can't easily tell if the call is virtual or not
|
||||
// if the callee is virtual. So assume that the call is virtual
|
||||
// if the target is.
|
||||
not staticTarget.isVirtual() and
|
||||
isNonReturningFunction(callee)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
import semmle.code.cpp.ir.implementation.raw.IR as IR
|
||||
import semmle.code.cpp.ir.implementation.raw.constant.ConstantAnalysis as ConstantAnalysis
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
|
||||
@@ -10,65 +10,6 @@ predicate isInfeasibleInstructionSuccessor(Instruction instr, EdgeKind kind) {
|
||||
or
|
||||
instr.getSuccessor(kind) instanceof UnreachedInstruction and
|
||||
kind instanceof GotoEdge
|
||||
or
|
||||
isCallToNonReturningFunction(instr) and exists(instr.getSuccessor(kind))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all calls to `f` never return (e.g. they call `exit` or loop forever)
|
||||
*/
|
||||
private predicate isNonReturningFunction(IRFunction f) {
|
||||
// If the function has an instruction with a missing successor then
|
||||
// the analysis is probably going to be incorrect, so assume they exit.
|
||||
not hasInstructionWithMissingSuccessor(f) and
|
||||
(
|
||||
// If all flows to the exit block are pass through an unreachable then f never returns.
|
||||
any(UnreachedInstruction instr).getBlock().postDominates(f.getEntryBlock())
|
||||
or
|
||||
// If there is no flow to the exit block then f never returns.
|
||||
not exists(IRBlock entry, IRBlock exit |
|
||||
exit = f.getExitFunctionInstruction().getBlock() and
|
||||
entry = f.getEntryBlock() and
|
||||
exit = entry.getASuccessor*()
|
||||
)
|
||||
or
|
||||
// If all flows to the exit block are pass through a call that never returns then f never returns.
|
||||
exists(CallInstruction ci |
|
||||
ci.getBlock().postDominates(f.getEntryBlock()) and
|
||||
isCallToNonReturningFunction(ci)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `f` has an instruction with a missing successor.
|
||||
* This matches `instructionWithoutSuccessor` from `IRConsistency`, but
|
||||
* avoids generating the error strings.
|
||||
*/
|
||||
predicate hasInstructionWithMissingSuccessor(IRFunction f) {
|
||||
exists(Instruction missingSucc |
|
||||
missingSucc.getEnclosingIRFunction() = f and
|
||||
not exists(missingSucc.getASuccessor()) and
|
||||
not missingSucc instanceof ExitFunctionInstruction and
|
||||
// Phi instructions aren't linked into the instruction-level flow graph.
|
||||
not missingSucc instanceof PhiInstruction and
|
||||
not missingSucc instanceof UnreachedInstruction
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call `ci` never returns.
|
||||
*/
|
||||
private predicate isCallToNonReturningFunction(CallInstruction ci) {
|
||||
exists(IRFunction callee, Language::Function staticTarget |
|
||||
staticTarget = ci.getStaticCallTarget() and
|
||||
staticTarget = callee.getFunction() and
|
||||
// We can't easily tell if the call is virtual or not
|
||||
// if the callee is virtual. So assume that the call is virtual
|
||||
// if the target is.
|
||||
not staticTarget.isVirtual() and
|
||||
isNonReturningFunction(callee)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR as IR
|
||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.constant.ConstantAnalysis as ConstantAnalysis
|
||||
import semmle.code.cpp.ir.internal.IRCppLanguage as Language
|
||||
|
||||
@@ -917,46 +917,25 @@ module RangeStage<
|
||||
bounded(cast.getOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate initialBoundedUpper(SemExpr e) {
|
||||
exists(D::Delta d |
|
||||
initialBounded(e, _, d, false, _, _, _) and
|
||||
D::toFloat(d) >= 0
|
||||
)
|
||||
}
|
||||
|
||||
private predicate noOverflow0(SemExpr e, boolean upper) {
|
||||
exists(boolean lower | lower = upper.booleanNot() |
|
||||
semExprDoesNotOverflow(lower, e)
|
||||
or
|
||||
upper = [true, false] and
|
||||
not potentiallyOverflowingExpr(lower, e)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate initialBoundedLower(SemExpr e) {
|
||||
exists(D::Delta d |
|
||||
initialBounded(e, _, d, true, _, _, _) and
|
||||
D::toFloat(d) <= 0
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate noOverflow(SemExpr e, boolean upper) {
|
||||
noOverflow0(e, upper)
|
||||
or
|
||||
upper = true and initialBoundedUpper(e)
|
||||
or
|
||||
upper = false and initialBoundedLower(e)
|
||||
}
|
||||
|
||||
predicate bounded(
|
||||
SemExpr e, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge, D::Delta origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
initialBounded(e, b, delta, upper, fromBackEdge, origdelta, reason) and
|
||||
noOverflow(e, upper)
|
||||
(
|
||||
semExprDoesNotOverflow(upper.booleanNot(), e)
|
||||
or
|
||||
not potentiallyOverflowingExpr(upper.booleanNot(), e)
|
||||
or
|
||||
exists(D::Delta otherDelta |
|
||||
initialBounded(e, _, otherDelta, upper.booleanNot(), _, _, _) and
|
||||
(
|
||||
upper = true and D::toFloat(otherDelta) >= 0
|
||||
or
|
||||
upper = false and D::toFloat(otherDelta) <= 0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate potentiallyOverflowingExpr(boolean positively, SemExpr expr) {
|
||||
|
||||
@@ -72,7 +72,7 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
|
||||
// Compute `delta` as the constant difference between `x` and `x + 1`.
|
||||
bounded1(any(Instruction instr | instr.getUnconvertedResultExpression() = size),
|
||||
any(LoadInstruction load | load.getUnconvertedResultExpression() = va), delta) and
|
||||
n.asExpr() = va and
|
||||
n.asConvertedExpr() = va.getFullyConverted() and
|
||||
state = delta
|
||||
)
|
||||
}
|
||||
@@ -210,7 +210,7 @@ private module InterestingPointerAddInstruction {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
// The sources is the same as in the sources for the second
|
||||
// projection in the `AllocToInvalidPointerConfig` module.
|
||||
hasSize(source.asExpr(), _, _)
|
||||
hasSize(source.asConvertedExpr(), _, _)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit() { result = allocationToInvalidPointerFieldFlowBranchLimit() }
|
||||
@@ -243,7 +243,7 @@ private module InterestingPointerAddInstruction {
|
||||
*/
|
||||
predicate isInterestingSize(DataFlow::Node n) {
|
||||
exists(DataFlow::Node alloc |
|
||||
hasSize(alloc.asExpr(), n, _) and
|
||||
hasSize(alloc.asConvertedExpr(), n, _) and
|
||||
flow(alloc, _)
|
||||
)
|
||||
}
|
||||
@@ -268,7 +268,7 @@ private module Config implements ProductFlow::StateConfigSig {
|
||||
// we use `state2` to remember that there was an offset (in this case an offset of `1`) added
|
||||
// to the size of the allocation. This state is then checked in `isSinkPair`.
|
||||
exists(unit) and
|
||||
hasSize(allocSource.asExpr(), sizeSource, sizeAddend)
|
||||
hasSize(allocSource.asConvertedExpr(), sizeSource, sizeAddend)
|
||||
}
|
||||
|
||||
int fieldFlowBranchLimit1() { result = allocationToInvalidPointerFieldFlowBranchLimit() }
|
||||
|
||||
@@ -1755,7 +1755,6 @@ case @expr.kind of
|
||||
| @istriviallydestructibleexpr
|
||||
| @istriviallyassignableexpr
|
||||
| @isnothrowassignableexpr
|
||||
| @istrivialexpr
|
||||
| @isstandardlayoutexpr
|
||||
| @istriviallycopyableexpr
|
||||
| @isliteraltypeexpr
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Make __is_trivial a builtin operation
|
||||
compatibility: full
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Potential double free
|
||||
* @description Freeing a resource more than once can lead to undefined behavior and cause memory corruption.
|
||||
* @kind path-problem
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/double-free
|
||||
* @problem.severity warning
|
||||
* @security-severity 9.3
|
||||
|
||||
@@ -98,11 +98,8 @@ module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
|
||||
* is being freed by a deallocation expression `dealloc`.
|
||||
*/
|
||||
predicate isFree(DataFlow::Node n, Expr e, DeallocationExpr dealloc) {
|
||||
exists(Expr conv |
|
||||
e = conv.getUnconverted() and
|
||||
conv = dealloc.getFreedExpr().getFullyConverted() and
|
||||
conv = n.asConvertedExpr()
|
||||
) and
|
||||
e = dealloc.getFreedExpr() and
|
||||
e = n.asExpr() and
|
||||
// Ignore realloc functions
|
||||
not exists(dealloc.(FunctionCall).getTarget().(AllocationFunction).getReallocPtrArg())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Potential use after free
|
||||
* @description An allocated memory block is used after it has been freed. Behavior in such cases is undefined and can cause memory corruption.
|
||||
* @kind path-problem
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/use-after-free
|
||||
* @problem.severity warning
|
||||
* @security-severity 9.3
|
||||
@@ -29,7 +29,8 @@ private predicate externalCallNeverDereferences(FormattingFunctionCall call, int
|
||||
)
|
||||
}
|
||||
|
||||
predicate isUse0(Expr e) {
|
||||
predicate isUse0(DataFlow::Node n, Expr e) {
|
||||
e = n.asExpr() and
|
||||
not isFree(_, e, _) and
|
||||
(
|
||||
e = any(PointerDereferenceExpr pde).getOperand()
|
||||
@@ -42,7 +43,7 @@ predicate isUse0(Expr e) {
|
||||
or
|
||||
// Assume any function without a body will dereference the pointer
|
||||
exists(int i, Call call, Function f |
|
||||
e = call.getArgument(i) and
|
||||
n.asExpr() = call.getArgument(i) and
|
||||
f = call.getTarget() and
|
||||
not f.hasEntryPoint() and
|
||||
// Exclude known functions we know won't dereference the pointer.
|
||||
@@ -56,7 +57,7 @@ module ParameterSinks {
|
||||
import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
predicate flowsToUse(DataFlow::Node n) {
|
||||
isUse0(n.asExpr())
|
||||
isUse0(n, _)
|
||||
or
|
||||
exists(DataFlow::Node succ |
|
||||
flowsToUse(succ) and
|
||||
@@ -89,7 +90,7 @@ module ParameterSinks {
|
||||
) {
|
||||
pragma[only_bind_out](source.asParameter()) = pragma[only_bind_out](init.getParameter()) and
|
||||
paramToUse(source, sink) and
|
||||
isUse0(sink.asExpr())
|
||||
isUse0(sink, _)
|
||||
}
|
||||
|
||||
private InitializeParameterInstruction getAnAlwaysDereferencedParameter0() {
|
||||
@@ -138,7 +139,7 @@ module IsUse {
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon
|
||||
|
||||
predicate isUse(DataFlow::Node n, Expr e) {
|
||||
isUse0(e) and n.asExpr() = e
|
||||
isUse0(n, e)
|
||||
or
|
||||
exists(CallInstruction call, InitializeParameterInstruction init |
|
||||
n.asOperand().getDef().getUnconvertedResultExpression() = e and
|
||||
|
||||
@@ -296,7 +296,7 @@ deprecated class PossibleYearArithmeticOperationCheckConfiguration extends Taint
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(Operation op | op = source.asExpr() |
|
||||
exists(Operation op | op = source.asConvertedExpr() |
|
||||
op.getAChild*().getValue().toInt() = 365 and
|
||||
(
|
||||
not op.getParent() instanceof Expr or
|
||||
@@ -321,7 +321,7 @@ deprecated class PossibleYearArithmeticOperationCheckConfiguration extends Taint
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(StructLikeClass dds, FieldAccess fa, AssignExpr aexpr |
|
||||
aexpr.getRValue() = sink.asExpr()
|
||||
aexpr.getRValue() = sink.asConvertedExpr()
|
||||
|
|
||||
(dds instanceof PackedTimeType or dds instanceof UnpackedTimeType) and
|
||||
fa.getQualifier().getUnderlyingType() = dds and
|
||||
@@ -336,7 +336,7 @@ deprecated class PossibleYearArithmeticOperationCheckConfiguration extends Taint
|
||||
*/
|
||||
private module PossibleYearArithmeticOperationCheckConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(Operation op | op = source.asExpr() |
|
||||
exists(Operation op | op = source.asConvertedExpr() |
|
||||
op.getAChild*().getValue().toInt() = 365 and
|
||||
(
|
||||
not op.getParent() instanceof Expr or
|
||||
@@ -361,7 +361,7 @@ private module PossibleYearArithmeticOperationCheckConfig implements DataFlow::C
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(StructLikeClass dds, FieldAccess fa, AssignExpr aexpr |
|
||||
aexpr.getRValue() = sink.asExpr()
|
||||
aexpr.getRValue() = sink.asConvertedExpr()
|
||||
|
|
||||
(dds instanceof PackedTimeType or dds instanceof UnpackedTimeType) and
|
||||
fa.getQualifier().getUnderlyingType() = dds and
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* @name Count AST inconsistencies
|
||||
* @description Counts the various AST inconsistencies that may occur.
|
||||
* This query is for internal use only and may change without notice.
|
||||
* @kind table
|
||||
* @id cpp/count-ast-inconsistencies
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate hasDuplicateFunctionEntryPointLocation(Function func) {
|
||||
count(func.getEntryPoint().getLocation()) > 1
|
||||
}
|
||||
|
||||
predicate hasDuplicateFunctionEntryPoint(Function func) { count(func.getEntryPoint()) > 1 }
|
||||
|
||||
select count(Function f | hasDuplicateFunctionEntryPoint(f) | f) as duplicateFunctionEntryPoint,
|
||||
count(Function f | hasDuplicateFunctionEntryPointLocation(f) | f) as duplicateFunctionEntryPointLocation
|
||||
@@ -30,7 +30,7 @@ Expr asSinkExpr(DataFlow::Node node) {
|
||||
result = node.asIndirectArgument()
|
||||
or
|
||||
// We want the conversion so we only get one node for the expression
|
||||
result = node.asExpr()
|
||||
result = node.asConvertedExpr()
|
||||
}
|
||||
|
||||
module SqlTaintedConfig implements DataFlow::ConfigSig {
|
||||
|
||||
@@ -38,7 +38,7 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
|
||||
// Compute `delta` as the constant difference between `x` and `x + 1`.
|
||||
bounded(any(Instruction instr | instr.getUnconvertedResultExpression() = size),
|
||||
any(LoadInstruction load | load.getUnconvertedResultExpression() = va), delta) and
|
||||
n.asExpr() = va and
|
||||
n.asConvertedExpr() = va.getFullyConverted() and
|
||||
state = delta
|
||||
)
|
||||
}
|
||||
@@ -213,7 +213,7 @@ module StringSizeConfig implements ProductFlow::StateConfigSig {
|
||||
// we use `state2` to remember that there was an offset (in this case an offset of `1`) added
|
||||
// to the size of the allocation. This state is then checked in `isSinkPair`.
|
||||
exists(state1) and
|
||||
hasSize(bufSource.asExpr(), sizeSource, state2) and
|
||||
hasSize(bufSource.asConvertedExpr(), sizeSource, state2) and
|
||||
validState(sizeSource, state2)
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ import TaintedAllocationSize::PathGraph
|
||||
* taint sink.
|
||||
*/
|
||||
predicate allocSink(HeuristicAllocationExpr alloc, DataFlow::Node sink) {
|
||||
exists(Expr e | e = sink.asExpr() |
|
||||
exists(Expr e | e = sink.asConvertedExpr() |
|
||||
e = alloc.getAChild() and
|
||||
e.getUnspecifiedType() instanceof IntegralType
|
||||
)
|
||||
|
||||
@@ -206,22 +206,25 @@ class Encrypted extends Expr {
|
||||
* operation `nsr`.
|
||||
*/
|
||||
predicate isSinkSendRecv(DataFlow::Node sink, NetworkSendRecv nsr) {
|
||||
[sink.asIndirectExpr(), sink.asExpr()] = nsr.getDataExpr()
|
||||
[sink.asIndirectConvertedExpr(), sink.asConvertedExpr()] = nsr.getDataExpr().getFullyConverted()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a node that is encrypted by `enc`.
|
||||
*/
|
||||
predicate isSinkEncrypt(DataFlow::Node sink, Encrypted enc) { sink.asExpr() = enc }
|
||||
predicate isSinkEncrypt(DataFlow::Node sink, Encrypted enc) {
|
||||
sink.asConvertedExpr() = enc.getFullyConverted()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `source` represents a use of a sensitive variable, or data returned by a
|
||||
* function returning sensitive data.
|
||||
*/
|
||||
predicate isSourceImpl(DataFlow::Node source) {
|
||||
exists(VariableAccess e |
|
||||
e = source.asExpr() and
|
||||
e.getTarget() instanceof SourceVariable
|
||||
exists(Expr e |
|
||||
e = source.asConvertedExpr() and
|
||||
e.getUnconverted().(VariableAccess).getTarget() instanceof SourceVariable and
|
||||
not e.hasConversion()
|
||||
)
|
||||
or
|
||||
source.asExpr().(FunctionCall).getTarget() instanceof SourceFunction
|
||||
|
||||
@@ -33,6 +33,14 @@ module ExposedSystemDataConfig implements DataFlow::ConfigSig {
|
||||
module ExposedSystemData = TaintTracking::Global<ExposedSystemDataConfig>;
|
||||
|
||||
from ExposedSystemData::PathNode source, ExposedSystemData::PathNode sink
|
||||
where ExposedSystemData::flowPath(source, sink)
|
||||
where
|
||||
ExposedSystemData::flowPath(source, sink) and
|
||||
not exists(
|
||||
DataFlow::Node alt // remove duplicate results on conversions
|
||||
|
|
||||
ExposedSystemData::flow(source.getNode(), alt) and
|
||||
alt.asConvertedExpr() = sink.getNode().asIndirectExpr() and
|
||||
alt != sink.getNode()
|
||||
)
|
||||
select sink, source, sink, "This operation exposes system data from $@.", source,
|
||||
source.getNode().toString()
|
||||
|
||||
@@ -34,7 +34,7 @@ class EnvData extends SystemData {
|
||||
.regexpMatch(".*(user|host|admin|root|home|path|http|ssl|snmp|sock|port|proxy|pass|token|crypt|key).*")
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnExpr() { result.asIndirectExpr() = this }
|
||||
override DataFlow::Node getAnExpr() { result.asIndirectConvertedExpr() = this }
|
||||
|
||||
override predicate isSensitive() {
|
||||
this.(EnvironmentRead)
|
||||
@@ -50,7 +50,7 @@ class EnvData extends SystemData {
|
||||
class SqlClientInfo extends SystemData {
|
||||
SqlClientInfo() { this.(FunctionCall).getTarget().hasName("mysql_get_client_info") }
|
||||
|
||||
override DataFlow::Node getAnExpr() { result.asIndirectExpr() = this }
|
||||
override DataFlow::Node getAnExpr() { result.asIndirectConvertedExpr() = this }
|
||||
|
||||
override predicate isSensitive() { any() }
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ class XercesDomParserLibrary extends XmlLibrary {
|
||||
// sink is the read of the qualifier of a call to `AbstractDOMParser.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof AbstractDomParserClass and
|
||||
call.getQualifier() = node.asIndirectExpr()
|
||||
call.getQualifier() = node.asIndirectConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
@@ -114,7 +114,7 @@ class CreateLSParserLibrary extends XmlLibrary {
|
||||
// sink is the read of the qualifier of a call to `DOMLSParserClass.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof DomLSParserClass and
|
||||
call.getQualifier() = node.asIndirectExpr()
|
||||
call.getQualifier() = node.asIndirectConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
@@ -155,7 +155,7 @@ class SaxParserLibrary extends XmlLibrary {
|
||||
// sink is the read of the qualifier of a call to `SAXParser.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof SaxParserClass and
|
||||
call.getQualifier() = node.asIndirectExpr()
|
||||
call.getQualifier() = node.asIndirectConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
@@ -192,7 +192,7 @@ class Sax2XmlReaderLibrary extends XmlLibrary {
|
||||
// sink is the read of the qualifier of a call to `SAX2XMLReader.parse`.
|
||||
exists(Call call |
|
||||
call.getTarget().getClassAndName("parse") instanceof Sax2XmlReader and
|
||||
call.getQualifier() = node.asIndirectExpr()
|
||||
call.getQualifier() = node.asIndirectConvertedExpr()
|
||||
) and
|
||||
flowstate instanceof XercesFlowState and
|
||||
not encodeXercesFlowState(flowstate, 1, 1) // safe configuration
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The number of duplicated dataflow paths reported by queries has been significantly reduced.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The queries `cpp/double-free` and `cpp/use-after-free` find fewer false positives
|
||||
in cases where a non-returning function is called.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: queryMetadata
|
||||
---
|
||||
* The `cpp/double-free` query has been further improved to reduce false positives and its precision has been increased from `medium` to `high`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: queryMetadata
|
||||
---
|
||||
* The `cpp/use-after-free` query has been further improved to reduce false positives and its precision has been increased from `medium` to `high`.
|
||||
@@ -1,68 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Using an object after its lifetime has ended results in undefined behavior.
|
||||
When an object's lifetime has ended it relinquishes ownership of its resources and the memory it occupied may be reused for other purposes.
|
||||
If the object is accessed after its lifetime has ended, the program may crash or behave in unexpected ways.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>
|
||||
Ensure that no object is accessed after its lifetime has ended.
|
||||
Use RAII ("Resource Acquisition Is Initialization") to manage the lifetime of objects, and avoid manual memory management, if possible.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>
|
||||
The following two examples demonstrate common lifetime violations when working with the C++ standard library.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The <code>bad_call_c_api</code> function contains a use of an expired lifetime.
|
||||
First, a temporary object of type <code>std::string</code> is constructed, and a pointer to its internal buffer is stored in a local variable.
|
||||
Once the <code>c_str()</code> call returns, the temporary object is destroyed, and the memory pointed to by <code>p</code> is freed.
|
||||
Thus, any attempt to dereference <code>p</code> inside <code>c_api</code> will result in a use-after-free vulnerability.
|
||||
|
||||
The <code>good_call_c_api</code> function contains a fixed version of the first example.
|
||||
The variable <code>hello</code> is declared as a local variable, and the pointer to its internal buffer is stored in <code>p</code>.
|
||||
The lifetime of hello outlives the call to <code>c_api</code>, so the pointer stored in <code>p</code> remains valid throughout the call to <code>c_api</code>.
|
||||
</p>
|
||||
<sample src="UseAfterExpiredLifetime_c_api_call.cpp" />
|
||||
|
||||
<p>
|
||||
The <code>bad_remove_even_numbers</code> function demonstrates a potential issue with iterator invalidation.
|
||||
Each C++ standard library container comes with a specification of which operations invalidates iterators pointing into the container.
|
||||
For example, calling <code>erase</code> on an object of type <code>std::vector<T></code> invalidates all its iterators, and thus any attempt to dereference the iterator can result in a use-after-free vulnerability.
|
||||
|
||||
The <code>good_remove_even_numbers</code> function contains a fixd version of the third example.
|
||||
The <code>erase</code> function returns an iterator to the element following the last element removed, and this return value is used to ensure that <code>it</code> remains valid after the call to <code>erase</code>.
|
||||
</p>
|
||||
<sample src="UseAfterExpiredLifetime_iterator_invalidation.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MEM30-C.+Do+not+access+freed+memory">MEM30-C. Do not access freed memory</a>.</li>
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://owasp.org/www-community/vulnerabilities/Using_freed_memory">Using freed memory</a>.
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/Lifetime.pdf">Lifetime safety: Preventing common dangling</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://en.cppreference.com/w/cpp/container">Containers library</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://en.cppreference.com/w/cpp/language/raii">RAII</a>
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,736 +0,0 @@
|
||||
/**
|
||||
* @id cpp/use-after-expired-lifetime
|
||||
* @name Use of object after its lifetime has ended
|
||||
* @description Accessing an object after its lifetime has ended can result in security vulnerabilities and undefined behavior.
|
||||
* @kind problem
|
||||
* @precision medium
|
||||
* @problem.severity error
|
||||
* @tags correctness
|
||||
* security
|
||||
* experimental
|
||||
* external/cwe/cwe-416
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.controlflow.Nullness
|
||||
|
||||
class StarOperator extends Operator {
|
||||
StarOperator() {
|
||||
this.hasName("operator*") and
|
||||
this.getNumberOfParameters() = 0
|
||||
}
|
||||
}
|
||||
|
||||
class IncrementOperator extends Operator {
|
||||
IncrementOperator() {
|
||||
this.hasName("operator++") and
|
||||
this.getNumberOfParameters() = 0
|
||||
}
|
||||
}
|
||||
|
||||
class StructureDerefOperator extends Operator {
|
||||
StructureDerefOperator() {
|
||||
this.hasName("operator->") and
|
||||
this.getNumberOfParameters() = 0
|
||||
}
|
||||
}
|
||||
|
||||
class SubscriptOperator extends Operator {
|
||||
SubscriptOperator() {
|
||||
this.hasName("operator[]") and
|
||||
this.getNumberOfParameters() = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A type which is an `Indirection` type according to the Lifetime profile.
|
||||
*
|
||||
* An indirection type is either a `LifetimePointerType` or `LifetimeOwnerType`.
|
||||
*/
|
||||
abstract class LifetimeIndirectionType extends Type {
|
||||
/**
|
||||
* Gets the `DerefType` of this indirection type.
|
||||
*
|
||||
* This corresponds to the owned or pointed to type.
|
||||
*/
|
||||
Type getDerefType() {
|
||||
result = this.(PointerType).getBaseType()
|
||||
or
|
||||
result = this.(ReferenceType).getBaseType()
|
||||
or
|
||||
exists(MemberFunction mf | mf.getDeclaringType() = this |
|
||||
result = mf.(StarOperator).getType().getUnspecifiedType().(ReferenceType).getBaseType()
|
||||
or
|
||||
result = mf.(SubscriptOperator).getType().getUnspecifiedType().(ReferenceType).getBaseType()
|
||||
or
|
||||
result =
|
||||
mf.(StructureDerefOperator).getType().getUnspecifiedType().(PointerType).getBaseType()
|
||||
or
|
||||
mf.getName() = "begin" and
|
||||
result = mf.getType().(LifetimePointerType).getDerefType()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A lifetime owner type.
|
||||
*
|
||||
* A type which owns another object. For example, `std::unique_ptr`. Includes
|
||||
* `LifetimeSharedOwnerType`.
|
||||
*/
|
||||
class LifetimeOwnerType extends LifetimeIndirectionType {
|
||||
LifetimeOwnerType() {
|
||||
// Any shared owner types are also owner types
|
||||
this instanceof LifetimeSharedOwnerType
|
||||
or
|
||||
// This is a container type, or a type with a star operator and..
|
||||
(
|
||||
this instanceof ContainerType
|
||||
or
|
||||
exists(StarOperator mf | mf.getDeclaringType() = this)
|
||||
) and
|
||||
// .. has a "user" provided destructor
|
||||
exists(Destructor d |
|
||||
d.getDeclaringType() = this and
|
||||
not d.isCompilerGenerated()
|
||||
)
|
||||
or
|
||||
// Any specified version of an owner type is also an owner type
|
||||
this.getUnspecifiedType() instanceof LifetimeOwnerType
|
||||
or
|
||||
// Has a field which is a lifetime owner type
|
||||
this.(Class).getAField().getType() instanceof LifetimeOwnerType
|
||||
or
|
||||
// Derived from a public base class which is a owner type
|
||||
exists(ClassDerivation cd |
|
||||
cd = this.(Class).getADerivation() and
|
||||
cd.getBaseClass() instanceof LifetimeOwnerType and
|
||||
cd.getASpecifier().hasName("public")
|
||||
)
|
||||
or
|
||||
// Lifetime profile treats the following types as owner types, even though they don't fully
|
||||
// adhere to the requirements above
|
||||
this.(Class)
|
||||
.hasQualifiedName("std",
|
||||
["stack", "queue", "priority_queue", "optional", "variant", "any", "regex"])
|
||||
or
|
||||
// Explicit annotation on the type
|
||||
this.getAnAttribute().getName().matches("gsl::Owner%")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `ContainerType`, based on `[container.requirements]` with some adaptions to capture more real
|
||||
* world containers.
|
||||
*/
|
||||
class ContainerType extends Class {
|
||||
ContainerType() {
|
||||
// We use a simpler set of heuristics than the `[container.requirements]`, requiring only
|
||||
// `begin()`/`end()`/`size()` as the minimum API for something to be considered a "container"
|
||||
// type
|
||||
this.getAMemberFunction().getName() = "begin" and
|
||||
this.getAMemberFunction().getName() = "end" and
|
||||
this.getAMemberFunction().getName() = "size"
|
||||
or
|
||||
// This class is a `ContainerType` if it is constructed from a `ContainerType`. This is
|
||||
// important, because templates may not have instantiated all the required member functions
|
||||
exists(TemplateClass tc |
|
||||
this.isConstructedFrom(tc) and
|
||||
tc instanceof ContainerType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A lifetime "shared owner" type.
|
||||
*
|
||||
* A shared owner is type that "owns" another object, and shares that ownership with other owners.
|
||||
* Examples include `std::shared_ptr` along with other reference counting types.
|
||||
*/
|
||||
class LifetimeSharedOwnerType extends Type {
|
||||
LifetimeSharedOwnerType() {
|
||||
/*
|
||||
* Find all types which can be dereferenced (i.e. have unary * operator), and are therefore
|
||||
* likely to be "owner"s or "pointer"s to other objects. We then consider these classes to be
|
||||
* shared owners if:
|
||||
* - They can be copied (a unique "owner" type would not be copyable)
|
||||
* - They can destroyed
|
||||
*/
|
||||
|
||||
// unary * (i.e. can be dereferenced)
|
||||
exists(StarOperator mf | mf.getDeclaringType() = this) and
|
||||
// "User" provided destructor
|
||||
exists(Destructor d |
|
||||
d.getDeclaringType() = this and
|
||||
not d.isCompilerGenerated()
|
||||
) and
|
||||
// A copy constructor and copy assignment operator
|
||||
exists(CopyConstructor cc | cc.getDeclaringType() = this and not cc.isDeleted()) and
|
||||
exists(CopyAssignmentOperator cc | cc.getDeclaringType() = this and not cc.isDeleted())
|
||||
or
|
||||
// This class is a `SharedOwnerType` if it is constructed from a `SharedOwnerType`. This is
|
||||
// important, because templates may not have instantiated all the required member functions
|
||||
exists(TemplateClass tc |
|
||||
this.(Class).isConstructedFrom(tc) and
|
||||
tc instanceof LifetimeSharedOwnerType
|
||||
)
|
||||
or
|
||||
// Any specified version of a shared owner type is also a shared owner type
|
||||
this.getUnspecifiedType() instanceof LifetimeSharedOwnerType
|
||||
or
|
||||
// Has a field which is a lifetime shared owner type
|
||||
this.(Class).getAField().getType() instanceof LifetimeSharedOwnerType
|
||||
or
|
||||
// Derived from a public base class which is a shared owner type
|
||||
exists(ClassDerivation cd |
|
||||
cd = this.(Class).getADerivation() and
|
||||
cd.getBaseClass() instanceof LifetimeSharedOwnerType and
|
||||
cd.getASpecifier().hasName("public")
|
||||
)
|
||||
or
|
||||
// Lifetime profile treats the following types as shared owner types, even though they don't
|
||||
// fully adhere to the requirements above
|
||||
this.(Class).hasQualifiedName("std", "shared_future")
|
||||
or
|
||||
// Explicit annotation on the type
|
||||
this.getAnAttribute().getName().matches("gsl::SharedOwner%")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `IteratorType`, based on `[iterator.requirements]` with some adaptions to capture more real
|
||||
* world iterators.
|
||||
*/
|
||||
class IteratorType extends Type {
|
||||
IteratorType() {
|
||||
// We consider anything with an increment and * operator to be sufficient to be an iterator type
|
||||
exists(StarOperator mf |
|
||||
mf.getDeclaringType() = this and mf.getType().getUnspecifiedType() instanceof ReferenceType
|
||||
) and
|
||||
exists(IncrementOperator op |
|
||||
op.getDeclaringType() = this and op.getType().(ReferenceType).getBaseType() = this
|
||||
)
|
||||
or
|
||||
// Along with unspecified versions of the types above
|
||||
this.getUnspecifiedType() instanceof IteratorType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A lifetime pointer type.
|
||||
*
|
||||
* A type which points to another object. For example, `std::unique_ptr`. Includes
|
||||
* `LifetimeSharedOwnerType`.
|
||||
*/
|
||||
class LifetimePointerType extends LifetimeIndirectionType {
|
||||
LifetimePointerType() {
|
||||
this instanceof IteratorType
|
||||
or
|
||||
this instanceof PointerType
|
||||
or
|
||||
this instanceof ReferenceType
|
||||
or
|
||||
// A shared owner type is a pointer type, but an owner type is not.
|
||||
this instanceof LifetimeSharedOwnerType and
|
||||
not this instanceof LifetimeOwnerType
|
||||
or
|
||||
this.(Class).hasQualifiedName("std", "reference_wrapper")
|
||||
or
|
||||
exists(Class vectorBool, UserType reference |
|
||||
vectorBool.hasQualifiedName("std", "vector") and
|
||||
vectorBool.getATemplateArgument() instanceof BoolType and
|
||||
reference.hasName("reference") and
|
||||
reference.getDeclaringType() = vectorBool and
|
||||
this = reference.getUnderlyingType()
|
||||
)
|
||||
or
|
||||
// Any specified version of a pointer type is also an owner type
|
||||
this.getUnspecifiedType() instanceof LifetimePointerType
|
||||
or
|
||||
// Has a field which is a lifetime pointer type
|
||||
this.(Class).getAField().getType() instanceof LifetimePointerType
|
||||
or
|
||||
// Derived from a public base class which is a pointer type
|
||||
exists(ClassDerivation cd |
|
||||
cd = this.(Class).getADerivation() and
|
||||
cd.getBaseClass() instanceof LifetimePointerType and
|
||||
cd.getASpecifier().hasName("public")
|
||||
)
|
||||
or
|
||||
// Explicit annotation on the type
|
||||
this.getAnAttribute().getName().matches("gsl::Pointer%")
|
||||
}
|
||||
}
|
||||
|
||||
/** A full expression as defined in [intro.execution] of N3797. */
|
||||
class FullExpr extends Expr {
|
||||
FullExpr() {
|
||||
// A full-expression is not a subexpression
|
||||
not exists(Expr p | this.getParent() = p)
|
||||
or
|
||||
// A sub-expression that is an unevaluated operand
|
||||
this.isUnevaluated()
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the `FullExpression` scope of the `TemporaryObjectExpr`. */
|
||||
FullExpr getTemporaryObjectExprScope(TemporaryObjectExpr toe) {
|
||||
result = toe.getUnconverted().getParent*()
|
||||
}
|
||||
|
||||
/**
|
||||
* See `LifetimeLocalVariable` and subclasses.
|
||||
*/
|
||||
private newtype TLifetimeLocalVariable =
|
||||
TLocalScopeVariable(LocalScopeVariable lsv) { not lsv.isStatic() } or
|
||||
TTemporaryObject(TemporaryObjectExpr toe)
|
||||
|
||||
/*
|
||||
* Note, the lifetime profile also supports locally initialized _aggregates_, which we could
|
||||
* support with something like this:
|
||||
* ```
|
||||
* TAggregateField(TLocalScopeVariable base, Field f) {
|
||||
* exists(LocalScopeVariable lsv |
|
||||
* base = TLocalScopeVariable(lsv) and
|
||||
* lsv.getType() = f.getDeclaringType() and
|
||||
* lsv.getType() instanceof LifetimeAggregateType
|
||||
* )
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* A "LocalVariable" as defined by the lifetime profile.
|
||||
*
|
||||
* This includes newly introduced objects with a local scope.
|
||||
*/
|
||||
class LifetimeLocalVariable extends TLifetimeLocalVariable {
|
||||
string toString() { none() } // specified in sub-classes
|
||||
|
||||
Type getType() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A parameter or `LocalVariable`, used as a `LifetimeLocalVariable`
|
||||
*/
|
||||
class LifetimeLocalScopeVariable extends TLocalScopeVariable, LifetimeLocalVariable {
|
||||
LocalScopeVariable getVariable() { this = TLocalScopeVariable(result) }
|
||||
|
||||
override Type getType() { result = this.getVariable().getType() }
|
||||
|
||||
override string toString() { result = this.getVariable().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A temporary object used as a `LifetimeLocalVariable`.
|
||||
*/
|
||||
class LifetimeTemporaryObject extends TTemporaryObject, LifetimeLocalVariable {
|
||||
TemporaryObjectExpr getTemporaryObjectExpr() { this = TTemporaryObject(result) }
|
||||
|
||||
override Type getType() { result = this.getTemporaryObjectExpr().getType() }
|
||||
|
||||
override string toString() { result = this.getTemporaryObjectExpr().toString() }
|
||||
}
|
||||
|
||||
newtype TInvalidReason =
|
||||
/** LifetimeLocalVariable is invalid because it hasn't been initialized. */
|
||||
TUninitialized(DeclStmt ds, Variable v) { ds.getADeclaration() = v } or
|
||||
/** LifetimeLocalVariable is invalid because it points to a variable which has gone out of scope. */
|
||||
TVariableOutOfScope(LocalScopeVariable v, ControlFlowNode cfn) { goesOutOfScopeAt(v, cfn) } or
|
||||
/** LifetimeLocalVariable is invalid because it points to a temporary object expression which has gone out of scope. */
|
||||
TTemporaryOutOfScope(TemporaryObjectExpr toe) or
|
||||
/** LifetimeLocalVariable is invalid because it points to data held by an owner which has since been invalidated. */
|
||||
TOwnerModified(FunctionCall fc)
|
||||
|
||||
/**
|
||||
* A reason why a pointer may be invalid.
|
||||
*/
|
||||
class InvalidReason extends TInvalidReason {
|
||||
/** Holds if this reason indicates the pointer is accessed before the lifetime of an object began. */
|
||||
predicate isBeforeLifetime() { this instanceof TUninitialized }
|
||||
|
||||
/** Holds if this reason indicates the pointer is accessed after the lifetime of an object has finished. */
|
||||
predicate isAfterLifetime() { not this.isBeforeLifetime() }
|
||||
|
||||
/** Gets a description of the reason why this pointer may be invalid. */
|
||||
string getDescription() {
|
||||
exists(DeclStmt ds, Variable v |
|
||||
this = TUninitialized(ds, v) and
|
||||
result = "variable " + v.getName() + " was never initialized"
|
||||
)
|
||||
or
|
||||
exists(LocalScopeVariable v, ControlFlowNode cfn |
|
||||
this = TVariableOutOfScope(v, cfn) and
|
||||
result = "variable " + v.getName() + " went out of scope"
|
||||
)
|
||||
or
|
||||
exists(TemporaryObjectExpr toe |
|
||||
this = TTemporaryOutOfScope(toe) and
|
||||
result = "temporary object went out of scope"
|
||||
)
|
||||
or
|
||||
exists(FunctionCall fc |
|
||||
this = TOwnerModified(fc) and
|
||||
result = "owner type was modified"
|
||||
)
|
||||
}
|
||||
|
||||
string toString() { result = this.getDescription() }
|
||||
|
||||
/** Get an element that explains the reason for the invalid determination. */
|
||||
private Element getExplanatoryElement() {
|
||||
exists(DeclStmt ds |
|
||||
this = TUninitialized(ds, _) and
|
||||
result = ds
|
||||
)
|
||||
or
|
||||
exists(ControlFlowNode cfn |
|
||||
this = TVariableOutOfScope(_, cfn) and
|
||||
result = cfn
|
||||
)
|
||||
or
|
||||
exists(TemporaryObjectExpr toe |
|
||||
this = TTemporaryOutOfScope(toe) and
|
||||
result = getTemporaryObjectExprScope(toe)
|
||||
)
|
||||
or
|
||||
exists(FunctionCall fc |
|
||||
this = TOwnerModified(fc) and
|
||||
result = fc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a `message` for use in alert messages.
|
||||
*
|
||||
* The message will contain a `$@` placeholder, for which `explanation` and `explanationDesc` are
|
||||
* the placeholder components which should be added as extra columns.
|
||||
*/
|
||||
predicate hasMessage(string message, Element explanation, string explanationDesc) {
|
||||
message = "because the " + this.getDescription() + " $@." and
|
||||
explanation = this.getExplanatoryElement() and
|
||||
explanationDesc = "here"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason why a pointer may be null.
|
||||
*/
|
||||
newtype TNullReason =
|
||||
// Null because the `NullValue` was assigned
|
||||
TNullAssignment(NullValue e)
|
||||
|
||||
class NullReason extends TNullReason {
|
||||
/** Gets a description of the reason why this pointer may be null. */
|
||||
string getDescription() {
|
||||
exists(NullValue nv |
|
||||
this = TNullAssignment(nv) and
|
||||
result = "null value was assigned"
|
||||
)
|
||||
}
|
||||
|
||||
string toString() { result = this.getDescription() }
|
||||
}
|
||||
|
||||
/** See `PSetEntry` and subclasses. */
|
||||
newtype TPSetEntry =
|
||||
/** Points to a lifetime local variable. */
|
||||
PSetVar(LifetimeLocalVariable lv) or
|
||||
/** Points to a lifetime local variable that represents an owner type. */
|
||||
PSetOwner(LifetimeLocalVariable lv, int level) {
|
||||
level = [0 .. 2] and lv.getType() instanceof LifetimeOwnerType
|
||||
} or
|
||||
/** Points to a global variable. */
|
||||
PSetGlobal() or
|
||||
/** A null pointer. */
|
||||
PSetNull(NullReason nr) or
|
||||
/** An invalid pointer, for the given reason. */
|
||||
PSetInvalid(InvalidReason ir) or
|
||||
/** An unknown pointer. */
|
||||
PSetUnknown()
|
||||
|
||||
/**
|
||||
* An entry in the points-to set for a particular "LifetimeLocalVariable" at a particular
|
||||
* point in the program.
|
||||
*/
|
||||
class PSetEntry extends TPSetEntry {
|
||||
string toString() {
|
||||
exists(LifetimeLocalVariable lv |
|
||||
this = PSetVar(lv) and
|
||||
result = "Var(" + lv.toString() + ")"
|
||||
)
|
||||
or
|
||||
this = PSetGlobal() and result = "global"
|
||||
or
|
||||
exists(LifetimeLocalVariable lv, int level |
|
||||
this = PSetOwner(lv, level) and
|
||||
result = "Owner(" + lv.toString() + "," + level + ")"
|
||||
)
|
||||
or
|
||||
exists(NullReason nr | this = PSetNull(nr) and result = "null because" + nr)
|
||||
or
|
||||
exists(InvalidReason ir | this = PSetInvalid(ir) and result = "invalid because " + ir)
|
||||
or
|
||||
this = PSetUnknown() and result = "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "pmap" or "points-to map" for a "lifetime" local variable.
|
||||
*/
|
||||
predicate pointsToMap(ControlFlowNode cfn, LifetimeLocalVariable lv, PSetEntry ps) {
|
||||
if isPSetReassigned(cfn, lv)
|
||||
then ps = getAnAssignedPSetEntry(cfn, lv)
|
||||
else
|
||||
// Exclude unknown for now
|
||||
exists(ControlFlowNode pred, PSetEntry prevPSet |
|
||||
pred = cfn.getAPredecessor() and
|
||||
pointsToMap(pred, lv, prevPSet) and
|
||||
// Not PSetNull() and a non-null successor of a null check
|
||||
not exists(AnalysedExpr ae |
|
||||
ps = PSetNull(_) and
|
||||
cfn = ae.getNonNullSuccessor(lv.(LifetimeLocalScopeVariable).getVariable())
|
||||
) and
|
||||
// lv is not out of scope at this node
|
||||
not goesOutOfScopeAt(lv.(LifetimeLocalScopeVariable).getVariable(), cfn)
|
||||
|
|
||||
// Propagate a PSetEntry from the predecessor node, so long as the
|
||||
// PSetEntry is not invalidated at this node
|
||||
ps = prevPSet and
|
||||
not exists(getAnInvalidation(prevPSet, cfn))
|
||||
or
|
||||
// Replace prevPSet with an invalidation reason at this node
|
||||
ps = getAnInvalidation(prevPSet, cfn)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isPSetReassigned(ControlFlowNode cfn, LifetimeLocalVariable lv) {
|
||||
exists(DeclStmt ds |
|
||||
cfn = ds and
|
||||
ds.getADeclaration() = lv.(LifetimeLocalScopeVariable).getVariable() and
|
||||
lv.getType() instanceof PointerType
|
||||
)
|
||||
or
|
||||
exists(TemporaryObjectExpr toe |
|
||||
toe = lv.(LifetimeTemporaryObject).getTemporaryObjectExpr() and
|
||||
cfn = toe
|
||||
)
|
||||
or
|
||||
// Assigned a value
|
||||
cfn = lv.(LifetimeLocalScopeVariable).getVariable().getAnAssignedValue()
|
||||
or
|
||||
// If the address of a local var is passed to a function, then assume it initializes it
|
||||
exists(Call fc, AddressOfExpr aoe |
|
||||
cfn = aoe and
|
||||
fc.getAnArgument() = aoe and
|
||||
lv.(LifetimeLocalScopeVariable).getVariable() = aoe.getOperand().(VariableAccess).getTarget()
|
||||
)
|
||||
}
|
||||
|
||||
/** Is the `lv` assigned or reassigned at this ControlFlowNode `cfn`. */
|
||||
private PSetEntry getAnAssignedPSetEntry(ControlFlowNode cfn, LifetimeLocalVariable lv) {
|
||||
exists(DeclStmt ds |
|
||||
cfn = ds and
|
||||
ds.getADeclaration() = lv.(LifetimeLocalScopeVariable).getVariable()
|
||||
|
|
||||
lv.getType() instanceof PointerType and
|
||||
result = PSetInvalid(TUninitialized(ds, lv.(LifetimeLocalScopeVariable).getVariable()))
|
||||
)
|
||||
or
|
||||
exists(TemporaryObjectExpr toe |
|
||||
toe = lv.(LifetimeTemporaryObject).getTemporaryObjectExpr() and
|
||||
cfn = toe and
|
||||
result = PSetVar(lv)
|
||||
)
|
||||
or
|
||||
// Assigned a value
|
||||
exists(Expr assign |
|
||||
assign = lv.(LifetimeLocalScopeVariable).getVariable().getAnAssignedValue() and
|
||||
cfn = assign
|
||||
|
|
||||
if isKnownAssignmentType(assign)
|
||||
then knownAssignmentType(assign, result)
|
||||
else result = PSetUnknown()
|
||||
)
|
||||
or
|
||||
// If the address of a local var is passed to a function, then assume it initializes it
|
||||
exists(Call fc, AddressOfExpr aoe |
|
||||
cfn = aoe and
|
||||
fc.getAnArgument() = aoe and
|
||||
lv.(LifetimeLocalScopeVariable).getVariable() = aoe.getOperand().(VariableAccess).getTarget() and
|
||||
result = PSetUnknown()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isKnownAssignmentType(Expr assign) {
|
||||
assign = any(LocalScopeVariable lv).getAnAssignedValue() and
|
||||
(
|
||||
exists(Variable v | v = assign.(AddressOfExpr).getOperand().(VariableAccess).getTarget() |
|
||||
v instanceof LocalScopeVariable
|
||||
or
|
||||
v instanceof GlobalVariable
|
||||
)
|
||||
or
|
||||
// Assignment of a previous variable
|
||||
exists(VariableAccess va |
|
||||
va = assign and
|
||||
va.getTarget().(LocalScopeVariable).getType() instanceof LifetimePointerType
|
||||
)
|
||||
or
|
||||
assign instanceof NullValue
|
||||
or
|
||||
exists(FunctionCall fc |
|
||||
assign = fc and
|
||||
fc.getNumberOfArguments() = 0 and
|
||||
fc.getType() instanceof LifetimePointerType
|
||||
|
|
||||
// A function call is a product of its inputs (just handle qualifiers at the moment)
|
||||
exists(LifetimeLocalVariable lv |
|
||||
lv = TTemporaryObject(fc.getQualifier().getConversion())
|
||||
or
|
||||
lv = TLocalScopeVariable(fc.getQualifier().(VariableAccess).getTarget())
|
||||
|
|
||||
lv.getType() instanceof LifetimePointerType
|
||||
or
|
||||
lv.getType() instanceof LifetimeOwnerType
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression which is assigned to a `LocalScopeVariable`, which has a known PSet value i.e. not
|
||||
* an "Unknown" PSet value.
|
||||
*/
|
||||
predicate knownAssignmentType(Expr assign, PSetEntry ps) {
|
||||
assign = any(LocalScopeVariable lv).getAnAssignedValue() and
|
||||
(
|
||||
// The assigned value is `&v`
|
||||
exists(Variable v | v = assign.(AddressOfExpr).getOperand().(VariableAccess).getTarget() |
|
||||
v instanceof LocalScopeVariable and
|
||||
(
|
||||
// If the variable we are taking the address of is a reference type, then we are really
|
||||
// taking the address of whatever the reference type "points-to". Use the `pointsToMap`
|
||||
// to determine viable `LifetimeLocalScopeVariable`s this could point to.
|
||||
if v.getType() instanceof ReferenceType
|
||||
then
|
||||
pointsToMap(assign.getAPredecessor(),
|
||||
any(LifetimeLocalScopeVariable lv | lv.getVariable() = v), ps)
|
||||
else
|
||||
// This assignment points-to `v` itself.
|
||||
ps = PSetVar(TLocalScopeVariable(v))
|
||||
)
|
||||
or
|
||||
// If the variable we are taking the address of is a reference variable, then this points-to
|
||||
// a global. If the variable we taking the address of is a reference type, we need to consider
|
||||
// that it might point-to a global, even if it is a LocalScopeVariable (this case is required
|
||||
// so that we still produce a result even if the pointsToMap is empty for `lv`).
|
||||
(v instanceof GlobalVariable or v.getType() instanceof ReferenceType) and
|
||||
ps = PSetGlobal()
|
||||
)
|
||||
or
|
||||
// Assignment of a previous variable
|
||||
exists(VariableAccess va |
|
||||
va = assign and
|
||||
va.getTarget().(LocalScopeVariable).getType() instanceof LifetimePointerType and
|
||||
// PSet of that become PSet of this
|
||||
pointsToMap(assign.getAPredecessor(),
|
||||
any(LifetimeLocalScopeVariable lv | lv.getVariable() = va.getTarget()), ps)
|
||||
)
|
||||
or
|
||||
// The `NullValue` class covers all types of null equivalent expressions. This case also handles
|
||||
// default and value initialization, where an "implicit" null value expression is added by the
|
||||
// extractor
|
||||
assign instanceof NullValue and ps = PSetNull(TNullAssignment(assign))
|
||||
or
|
||||
exists(FunctionCall fc |
|
||||
assign = fc and
|
||||
// If the assignment is being converted via a ReferenceDereferenceExpr, then
|
||||
// we are essentially copying the original object
|
||||
not assign.getFullyConverted() instanceof ReferenceDereferenceExpr and
|
||||
fc.getNumberOfArguments() = 0 and
|
||||
fc.getType() instanceof LifetimePointerType
|
||||
|
|
||||
// A function call is a product of its inputs (just handle qualifiers at the moment)
|
||||
exists(LifetimeLocalVariable lv |
|
||||
lv = TTemporaryObject(fc.getQualifier().getConversion())
|
||||
or
|
||||
lv = TLocalScopeVariable(fc.getQualifier().(VariableAccess).getTarget())
|
||||
|
|
||||
ps = PSetVar(lv) and lv.getType() instanceof LifetimePointerType
|
||||
or
|
||||
ps = PSetOwner(lv, 0) and lv.getType() instanceof LifetimeOwnerType
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `cfn` is a node that occur directly after the local scope variable `lv` has gone out of scope.
|
||||
*/
|
||||
predicate goesOutOfScopeAt(LocalScopeVariable lv, ControlFlowNode cfn) {
|
||||
exists(BlockStmt scope |
|
||||
scope = lv.getParentScope() and
|
||||
scope.getAChild+() = cfn.getAPredecessor().getEnclosingStmt() and
|
||||
not scope.getAChild+() = cfn.getEnclosingStmt()
|
||||
)
|
||||
}
|
||||
|
||||
PSetInvalid getAnInvalidation(PSetEntry ps, ControlFlowNode cfn) {
|
||||
exists(LifetimeLocalScopeVariable lv | ps = PSetVar(lv) |
|
||||
result = PSetInvalid(TVariableOutOfScope(lv.getVariable(), cfn))
|
||||
)
|
||||
or
|
||||
exists(LifetimeLocalScopeVariable lv | ps = PSetOwner(lv, _) |
|
||||
result = PSetInvalid(TVariableOutOfScope(lv.getVariable(), cfn))
|
||||
or
|
||||
exists(FunctionCall fc |
|
||||
fc = cfn and
|
||||
fc.getQualifier() = lv.getVariable().getAnAccess() and
|
||||
not fc.getTarget() instanceof ConstMemberFunction and
|
||||
// non-const versions of begin and end should nevertheless be considered const
|
||||
not fc.getTarget().hasName(["begin", "end"]) and
|
||||
result = PSetInvalid(TOwnerModified(fc))
|
||||
)
|
||||
)
|
||||
or
|
||||
// temporary objects end after the full expression
|
||||
exists(LifetimeTemporaryObject lto |
|
||||
ps = PSetVar(lto)
|
||||
or
|
||||
ps = PSetOwner(lto, _)
|
||||
|
|
||||
cfn = lto.getTemporaryObjectExpr().getUnconverted().getParent*().(FullExpr).getASuccessor() and
|
||||
result = PSetInvalid(TTemporaryOutOfScope(lto.getTemporaryObjectExpr()))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression which is dereferenced and may be an "invalid" value.
|
||||
*/
|
||||
class InvalidDereference extends VariableAccess {
|
||||
InvalidReason ir;
|
||||
|
||||
InvalidDereference() {
|
||||
// The local points to map suggests this points to an invalid set
|
||||
exists(LocalScopeVariable lv |
|
||||
lv = this.getTarget() and
|
||||
pointsToMap(this, TLocalScopeVariable(lv), PSetInvalid(ir))
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a reason why this dereference could point to an invalid value. */
|
||||
InvalidReason getAnInvalidReason() { result = ir }
|
||||
}
|
||||
|
||||
from
|
||||
InvalidDereference e, Element explanation, string explanationDesc, InvalidReason ir,
|
||||
string invalidMessage
|
||||
where
|
||||
ir = e.getAnInvalidReason() and
|
||||
ir.isAfterLifetime() and
|
||||
ir.hasMessage(invalidMessage, explanation, explanationDesc)
|
||||
select e,
|
||||
e.(VariableAccess).getTarget().getName() + " is dereferenced here but accesses invalid memory " +
|
||||
invalidMessage, explanation, explanationDesc
|
||||
@@ -1,14 +0,0 @@
|
||||
void c_api(const char*);
|
||||
|
||||
void bad_call_c_api() {
|
||||
// BAD: the memory returned by `c_str()` is freed when the temporary string is destroyed
|
||||
const char* p = std::string("hello").c_str();
|
||||
c_api(p);
|
||||
}
|
||||
|
||||
void good_call_c_api() {
|
||||
// GOOD: the "hello" string outlives the pointer returned by `c_str()`, so it's safe to pass it to `c_api()`
|
||||
std::string hello("hello");
|
||||
const char* p = hello.c_str();
|
||||
c_api(p);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
void bad_remove_even_numbers(std::vector<int>& v) {
|
||||
// BAD: the iterator is invalidated after the call to `erase`.
|
||||
for(std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
|
||||
if(*it % 2 == 0) {
|
||||
v.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void good_remove_even_numbers(std::vector<int>& v) {
|
||||
// GOOD: `erase` returns the iterator to the next element.
|
||||
for(std::vector<int>::iterator it = v.begin(); it != v.end(); ) {
|
||||
if(*it % 2 == 0) {
|
||||
it = v.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.8.0-dev
|
||||
groups:
|
||||
version: 0.7.5
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
dependencies:
|
||||
codeql/cpp-all: ${workspace}
|
||||
codeql/suite-helpers: ${workspace}
|
||||
codeql/util: ${workspace}
|
||||
codeql/cpp-all: ${workspace}
|
||||
codeql/suite-helpers: ${workspace}
|
||||
codeql/util: ${workspace}
|
||||
suites: codeql-suites
|
||||
extractor: cpp
|
||||
defaultSuiteFile: codeql-suites/cpp-code-scanning.qls
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
| printf.cpp:5:5:5:10 | call to printf | Argument to printf isn't hard-coded. |
|
||||
| printf.cpp:6:5:6:10 | call to printf | Argument to printf isn't hard-coded. |
|
||||
|
||||
@@ -1,28 +1,58 @@
|
||||
edges
|
||||
| test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... |
|
||||
| test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... |
|
||||
| test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... |
|
||||
| test.cpp:22:17:22:21 | ... * ... | test.cpp:23:33:23:37 | size1 |
|
||||
| test.cpp:22:17:22:21 | ... * ... | test.cpp:23:33:23:37 | size1 |
|
||||
| test.cpp:37:24:37:27 | size | test.cpp:37:46:37:49 | size |
|
||||
| test.cpp:45:36:45:40 | ... * ... | test.cpp:37:24:37:27 | size |
|
||||
| test.cpp:45:36:45:40 | ... * ... | test.cpp:45:36:45:40 | ... * ... |
|
||||
| test.cpp:45:36:45:40 | ... * ... | test.cpp:45:36:45:40 | ... * ... |
|
||||
| test.cpp:46:36:46:40 | ... * ... | test.cpp:46:36:46:40 | ... * ... |
|
||||
nodes
|
||||
| test.cpp:13:33:13:37 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:13:33:13:37 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:13:33:13:37 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:15:31:15:35 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:15:31:15:35 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:15:31:15:35 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:19:34:19:38 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:19:34:19:38 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:19:34:19:38 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:22:17:22:21 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:22:17:22:21 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:23:33:23:37 | size1 | semmle.label | size1 |
|
||||
| test.cpp:30:18:30:32 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:31:18:31:32 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:30:27:30:31 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:31:27:31:31 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:37:24:37:27 | size | semmle.label | size |
|
||||
| test.cpp:37:46:37:49 | size | semmle.label | size |
|
||||
| test.cpp:45:36:45:40 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:45:36:45:40 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:45:36:45:40 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:45:36:45:40 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:46:36:46:40 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:46:36:46:40 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:46:36:46:40 | ... * ... | semmle.label | ... * ... |
|
||||
subpaths
|
||||
#select
|
||||
| test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:13:33:13:37 | ... * ... | multiplication |
|
||||
| test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:13:33:13:37 | ... * ... | multiplication |
|
||||
| test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:13:33:13:37 | ... * ... | multiplication |
|
||||
| test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:15:31:15:35 | ... * ... | multiplication |
|
||||
| test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:15:31:15:35 | ... * ... | multiplication |
|
||||
| test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:15:31:15:35 | ... * ... | multiplication |
|
||||
| test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:19:34:19:38 | ... * ... | multiplication |
|
||||
| test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:19:34:19:38 | ... * ... | multiplication |
|
||||
| test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:19:34:19:38 | ... * ... | multiplication |
|
||||
| test.cpp:23:33:23:37 | size1 | test.cpp:22:17:22:21 | ... * ... | test.cpp:23:33:23:37 | size1 | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:22:17:22:21 | ... * ... | multiplication |
|
||||
| test.cpp:30:18:30:32 | ... * ... | test.cpp:30:18:30:32 | ... * ... | test.cpp:30:18:30:32 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:30:18:30:32 | ... * ... | multiplication |
|
||||
| test.cpp:31:18:31:32 | ... * ... | test.cpp:31:18:31:32 | ... * ... | test.cpp:31:18:31:32 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:31:18:31:32 | ... * ... | multiplication |
|
||||
| test.cpp:23:33:23:37 | size1 | test.cpp:22:17:22:21 | ... * ... | test.cpp:23:33:23:37 | size1 | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:22:17:22:21 | ... * ... | multiplication |
|
||||
| test.cpp:30:27:30:31 | ... * ... | test.cpp:30:27:30:31 | ... * ... | test.cpp:30:27:30:31 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:30:27:30:31 | ... * ... | multiplication |
|
||||
| test.cpp:31:27:31:31 | ... * ... | test.cpp:31:27:31:31 | ... * ... | test.cpp:31:27:31:31 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:31:27:31:31 | ... * ... | multiplication |
|
||||
| test.cpp:37:46:37:49 | size | test.cpp:45:36:45:40 | ... * ... | test.cpp:37:46:37:49 | size | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:45:36:45:40 | ... * ... | multiplication |
|
||||
| test.cpp:37:46:37:49 | size | test.cpp:45:36:45:40 | ... * ... | test.cpp:37:46:37:49 | size | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:45:36:45:40 | ... * ... | multiplication |
|
||||
| test.cpp:45:36:45:40 | ... * ... | test.cpp:45:36:45:40 | ... * ... | test.cpp:45:36:45:40 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:45:36:45:40 | ... * ... | multiplication |
|
||||
| test.cpp:45:36:45:40 | ... * ... | test.cpp:45:36:45:40 | ... * ... | test.cpp:45:36:45:40 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:45:36:45:40 | ... * ... | multiplication |
|
||||
| test.cpp:45:36:45:40 | ... * ... | test.cpp:45:36:45:40 | ... * ... | test.cpp:45:36:45:40 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:45:36:45:40 | ... * ... | multiplication |
|
||||
| test.cpp:46:36:46:40 | ... * ... | test.cpp:46:36:46:40 | ... * ... | test.cpp:46:36:46:40 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:46:36:46:40 | ... * ... | multiplication |
|
||||
| test.cpp:46:36:46:40 | ... * ... | test.cpp:46:36:46:40 | ... * ... | test.cpp:46:36:46:40 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:46:36:46:40 | ... * ... | multiplication |
|
||||
| test.cpp:46:36:46:40 | ... * ... | test.cpp:46:36:46:40 | ... * ... | test.cpp:46:36:46:40 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:46:36:46:40 | ... * ... | multiplication |
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
edges
|
||||
| test.cpp:45:18:45:23 | buffer | test.cpp:45:7:45:10 | func indirection |
|
||||
| test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode |
|
||||
| test.cpp:74:24:74:30 | medical | test.cpp:78:24:78:27 | temp |
|
||||
| test.cpp:74:24:74:30 | medical | test.cpp:81:22:81:28 | medical |
|
||||
| test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp |
|
||||
@@ -7,12 +8,23 @@ edges
|
||||
| test.cpp:81:17:81:20 | call to func | test.cpp:82:24:82:28 | buff5 |
|
||||
| test.cpp:81:22:81:28 | medical | test.cpp:45:18:45:23 | buffer |
|
||||
| test.cpp:81:22:81:28 | medical | test.cpp:81:17:81:20 | call to func |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:96:37:96:46 | theZipcode |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:96:37:96:46 | theZipcode |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
|
||||
| test.cpp:99:61:99:70 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
|
||||
| test.cpp:99:61:99:70 | theZipcode | test.cpp:99:42:99:51 | theZipcode |
|
||||
nodes
|
||||
| test.cpp:45:7:45:10 | func indirection | semmle.label | func indirection |
|
||||
| test.cpp:45:18:45:23 | buffer | semmle.label | buffer |
|
||||
| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:57:9:57:18 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:74:24:74:30 | medical | semmle.label | medical |
|
||||
| test.cpp:74:24:74:30 | medical | semmle.label | medical |
|
||||
| test.cpp:77:16:77:22 | medical | semmle.label | medical |
|
||||
@@ -22,12 +34,19 @@ nodes
|
||||
| test.cpp:82:24:82:28 | buff5 | semmle.label | buff5 |
|
||||
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:96:37:96:46 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:99:42:99:51 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:99:61:99:70 | theZipcode | semmle.label | theZipcode |
|
||||
| test.cpp:99:61:99:70 | theZipcode | semmle.label | theZipcode |
|
||||
subpaths
|
||||
| test.cpp:81:22:81:28 | medical | test.cpp:45:18:45:23 | buffer | test.cpp:45:7:45:10 | func indirection | test.cpp:81:17:81:20 | call to func |
|
||||
#select
|
||||
| test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:57:9:57:18 | theZipcode | this source of private data. |
|
||||
| test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:57:9:57:18 | theZipcode | this source of private data. |
|
||||
| test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | test.cpp:57:9:57:18 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:57:9:57:18 | theZipcode | this source of private data. |
|
||||
| test.cpp:74:24:74:30 | medical | test.cpp:74:24:74:30 | medical | test.cpp:74:24:74:30 | medical | This write into the external location 'medical' may contain unencrypted data from $@. | test.cpp:74:24:74:30 | medical | this source of private data. |
|
||||
| test.cpp:78:24:78:27 | temp | test.cpp:74:24:74:30 | medical | test.cpp:78:24:78:27 | temp | This write into the external location 'temp' may contain unencrypted data from $@. | test.cpp:74:24:74:30 | medical | this source of private data. |
|
||||
| test.cpp:78:24:78:27 | temp | test.cpp:77:16:77:22 | medical | test.cpp:78:24:78:27 | temp | This write into the external location 'temp' may contain unencrypted data from $@. | test.cpp:77:16:77:22 | medical | this source of private data. |
|
||||
@@ -35,6 +54,14 @@ subpaths
|
||||
| test.cpp:82:24:82:28 | buff5 | test.cpp:77:16:77:22 | medical | test.cpp:82:24:82:28 | buff5 | This write into the external location 'buff5' may contain unencrypted data from $@. | test.cpp:77:16:77:22 | medical | this source of private data. |
|
||||
| test.cpp:82:24:82:28 | buff5 | test.cpp:81:22:81:28 | medical | test.cpp:82:24:82:28 | buff5 | This write into the external location 'buff5' may contain unencrypted data from $@. | test.cpp:81:22:81:28 | medical | this source of private data. |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:96:37:96:46 | theZipcode | test.cpp:96:37:96:46 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:96:37:96:46 | theZipcode | this source of private data. |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:96:37:96:46 | theZipcode | test.cpp:96:37:96:46 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:96:37:96:46 | theZipcode | this source of private data. |
|
||||
| test.cpp:96:37:96:46 | theZipcode | test.cpp:96:37:96:46 | theZipcode | test.cpp:96:37:96:46 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:96:37:96:46 | theZipcode | this source of private data. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:96:37:96:46 | theZipcode | this source of private data. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:96:37:96:46 | theZipcode | this source of private data. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:96:37:96:46 | theZipcode | this source of private data. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:96:37:96:46 | theZipcode | test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:96:37:96:46 | theZipcode | this source of private data. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:99:42:99:51 | theZipcode | test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:99:42:99:51 | theZipcode | this source of private data. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:99:42:99:51 | theZipcode | test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:99:42:99:51 | theZipcode | this source of private data. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:99:42:99:51 | theZipcode | test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:99:42:99:51 | theZipcode | this source of private data. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:99:61:99:70 | theZipcode | test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:99:61:99:70 | theZipcode | this source of private data. |
|
||||
| test.cpp:99:42:99:51 | theZipcode | test.cpp:99:61:99:70 | theZipcode | test.cpp:99:42:99:51 | theZipcode | This write into the external location 'theZipcode' may contain unencrypted data from $@. | test.cpp:99:61:99:70 | theZipcode | this source of private data. |
|
||||
|
||||
@@ -86,10 +86,3 @@ bool bok_is_void2 = __is_void(int);
|
||||
|
||||
bool bok_is_volatile1 = __is_volatile(volatile int);
|
||||
bool bok_is_volatile2 = __is_volatile(int);
|
||||
|
||||
struct S2 {
|
||||
S2() {}
|
||||
};
|
||||
|
||||
bool bok_is_trivial1 = __is_trivial(int);
|
||||
bool bok_is_trivial2 = __is_trivial(S2);
|
||||
|
||||
@@ -121,10 +121,6 @@
|
||||
| clang.cpp:87:25:87:51 | volatile int | | <none> |
|
||||
| clang.cpp:88:25:88:42 | __is_volatile | int | 0 |
|
||||
| clang.cpp:88:25:88:42 | int | | <none> |
|
||||
| clang.cpp:94:24:94:40 | __is_trivial | int | 1 |
|
||||
| clang.cpp:94:24:94:40 | int | | <none> |
|
||||
| clang.cpp:95:24:95:39 | S2 | | <none> |
|
||||
| clang.cpp:95:24:95:39 | __is_trivial | S2 | 0 |
|
||||
| file://:0:0:0:0 | 0 | | 0 |
|
||||
| file://:0:0:0:0 | 1 | | 1 |
|
||||
| file://:0:0:0:0 | 2 | | 2 |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user