mirror of
https://github.com/github/codeql.git
synced 2026-05-26 17:11:24 +02:00
Compare commits
1 Commits
henrymerce
...
post-relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3c6b06da5 |
@@ -4,18 +4,14 @@
|
||||
"*/ql/lib/qlpack.yml",
|
||||
"*/ql/test/qlpack.yml",
|
||||
"*/ql/examples/qlpack.yml",
|
||||
"*/upgrades/qlpack.yml",
|
||||
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
|
||||
"javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml",
|
||||
"javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml",
|
||||
"csharp/ql/campaigns/Solorigate/lib/qlpack.yml",
|
||||
"csharp/ql/campaigns/Solorigate/src/qlpack.yml",
|
||||
"csharp/ql/campaigns/Solorigate/test/qlpack.yml",
|
||||
"misc/legacy-support/*/qlpack.yml",
|
||||
"misc/suite-helpers/qlpack.yml",
|
||||
"ruby/extractor-pack/codeql-extractor.yml",
|
||||
"ruby/ql/consistency-queries/qlpack.yml",
|
||||
"ql/ql/consistency-queries/qlpack.yml",
|
||||
"ql/extractor-pack/codeql-extractor.yml"
|
||||
"ruby/ql/consistency-queries/qlpack.yml"
|
||||
],
|
||||
"versionPolicies": {
|
||||
"default": {
|
||||
|
||||
3
.github/labeler.yml
vendored
3
.github/labeler.yml
vendored
@@ -26,6 +26,3 @@ documentation:
|
||||
- "**/*.qhelp"
|
||||
- "**/*.md"
|
||||
- docs/**/*
|
||||
|
||||
"QL-for-QL":
|
||||
- ql/**/*
|
||||
1
.github/workflows/check-change-note.yml
vendored
1
.github/workflows/check-change-note.yml
vendored
@@ -7,7 +7,6 @@ on:
|
||||
- "*/ql/src/**/*.ql"
|
||||
- "*/ql/src/**/*.qll"
|
||||
- "!**/experimental/**"
|
||||
- "!ql/**"
|
||||
|
||||
jobs:
|
||||
check-change-note:
|
||||
|
||||
192
.github/workflows/ql-for-ql-build.yml
vendored
192
.github/workflows/ql-for-ql-build.yml
vendored
@@ -1,192 +0,0 @@
|
||||
name: Run QL for QL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
queries:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@erik-krogh/ql
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- name: Get CodeQL version
|
||||
id: get-codeql-version
|
||||
run: |
|
||||
echo "::set-output name=version::$("${CODEQL}" --version | head -n 1 | rev | cut -d " " -f 1 | rev)"
|
||||
shell: bash
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Cache queries
|
||||
id: cache-queries
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ runner.temp }}/query-pack.zip
|
||||
key: queries-${{ hashFiles('ql/**/*.ql*') }}-${{ hashFiles('ql/ql/src/ql.dbscheme*') }}-${{ steps.get-codeql-version.outputs.version }}
|
||||
- name: Build query pack
|
||||
if: steps.cache-queries.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd ql/ql/src
|
||||
"${CODEQL}" pack create
|
||||
cd .codeql/pack/codeql/ql-all/0.0.0
|
||||
zip "${PACKZIP}" -r .
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
PACKZIP: ${{ runner.temp }}/query-pack.zip
|
||||
- name: Upload query pack
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: query-pack-zip
|
||||
path: ${{ runner.temp }}/query-pack.zip
|
||||
|
||||
extractors:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache entire extractor
|
||||
id: cache-extractor
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
ql/target/release/ql-autobuilder
|
||||
ql/target/release/ql-autobuilder.exe
|
||||
ql/target/release/ql-extractor
|
||||
ql/target/release/ql-extractor.exe
|
||||
key: ${{ runner.os }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
|
||||
- name: Cache cargo
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-rust-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo fmt --all -- --check
|
||||
- name: Build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo build --verbose
|
||||
- name: Run tests
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo test --verbose
|
||||
- name: Release build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo build --release
|
||||
- name: Generate dbscheme
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: ql/target/release/ql-generator --dbscheme ql/ql/src/ql.dbscheme --library ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: extractor-ubuntu-latest
|
||||
path: |
|
||||
ql/target/release/ql-autobuilder
|
||||
ql/target/release/ql-autobuilder.exe
|
||||
ql/target/release/ql-extractor
|
||||
ql/target/release/ql-extractor.exe
|
||||
retention-days: 1
|
||||
package:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs:
|
||||
- extractors
|
||||
- queries
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: query-pack-zip
|
||||
path: query-pack-zip
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: extractor-ubuntu-latest
|
||||
path: linux64
|
||||
- run: |
|
||||
unzip query-pack-zip/*.zip -d pack
|
||||
cp -r ql/codeql-extractor.yml ql/tools ql/ql/src/ql.dbscheme.stats pack/
|
||||
mkdir -p pack/tools/linux64
|
||||
if [[ -f linux64/ql-autobuilder ]]; then
|
||||
cp linux64/ql-autobuilder pack/tools/linux64/autobuilder
|
||||
chmod +x pack/tools/linux64/autobuilder
|
||||
fi
|
||||
if [[ -f linux64/ql-extractor ]]; then
|
||||
cp linux64/ql-extractor pack/tools/linux64/extractor
|
||||
chmod +x pack/tools/linux64/extractor
|
||||
fi
|
||||
cd pack
|
||||
zip -rq ../codeql-ql.zip .
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: codeql-ql-pack
|
||||
path: codeql-ql.zip
|
||||
retention-days: 1
|
||||
analyze:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
folder: [cpp, csharp, java, javascript, python, ql, ruby]
|
||||
|
||||
needs:
|
||||
- package
|
||||
|
||||
steps:
|
||||
- name: Download pack
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: codeql-ql-pack
|
||||
path: ${{ runner.temp }}/codeql-ql-pack-artifact
|
||||
|
||||
- name: Prepare pack
|
||||
run: |
|
||||
unzip "${PACK_ARTIFACT}/*.zip" -d "${PACK}"
|
||||
env:
|
||||
PACK_ARTIFACT: ${{ runner.temp }}/codeql-ql-pack-artifact
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
- name: Hack codeql-action options
|
||||
run: |
|
||||
JSON=$(jq -nc --arg pack "${PACK}" '.resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .database.init=["--search-path", $pack]')
|
||||
echo "CODEQL_ACTION_EXTRA_OPTIONS=${JSON}" >> ${GITHUB_ENV}
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Create CodeQL config file
|
||||
run: |
|
||||
echo "paths:" > ${CONF}
|
||||
echo " - ${FOLDER}" >> ${CONF}
|
||||
echo "paths-ignore:" >> ${CONF}
|
||||
echo " - ql/ql/test" >> ${CONF}
|
||||
echo "Config file: "
|
||||
cat ${CONF}
|
||||
env:
|
||||
CONF: ./ql-for-ql-config.yml
|
||||
FOLDER: ${{ matrix.folder }}
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@erik-krogh/ql
|
||||
with:
|
||||
languages: ql
|
||||
db-location: ${{ runner.temp }}/db
|
||||
config-file: ./ql-for-ql-config.yml
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@erik-krogh/ql
|
||||
with:
|
||||
category: "ql-for-ql-${{ matrix.folder }}"
|
||||
|
||||
84
.github/workflows/ql-for-ql-dataset_measure.yml
vendored
84
.github/workflows/ql-for-ql-dataset_measure.yml
vendored
@@ -1,84 +0,0 @@
|
||||
name: Collect database stats for QL for QL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ql/ql/src/ql.dbscheme
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ql/ql/src/ql.dbscheme
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
measure:
|
||||
env:
|
||||
CODEQL_THREADS: 4 # TODO: remove this once it's set by the CLI
|
||||
strategy:
|
||||
matrix:
|
||||
repo:
|
||||
- github/codeql
|
||||
- github/codeql-go
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@erik-krogh/ql
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Build Extractor
|
||||
run: cd ql; env "PATH=$PATH:`dirname ${CODEQL}`" ./create-extractor-pack.sh
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Checkout ${{ matrix.repo }}
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ${{ matrix.repo }}
|
||||
path: ${{ github.workspace }}/repo
|
||||
- name: Create database
|
||||
run: |
|
||||
"${CODEQL}" database create \
|
||||
--search-path "ql/extractor-pack" \
|
||||
--threads 4 \
|
||||
--language ql --source-root "${{ github.workspace }}/repo" \
|
||||
"${{ runner.temp }}/database"
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Measure database
|
||||
run: |
|
||||
mkdir -p "stats/${{ matrix.repo }}"
|
||||
"${CODEQL}" dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ql"
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: measurements
|
||||
path: stats
|
||||
retention-days: 1
|
||||
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
needs: measure
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: measurements
|
||||
path: stats
|
||||
- run: |
|
||||
python -m pip install --user lxml
|
||||
find stats -name 'stats.xml' -print0 | sort -z | xargs -0 python ql/scripts/merge_stats.py --output ql/ql/src/ql.dbscheme.stats --normalise ql_tokeninfo
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: ql.dbscheme.stats
|
||||
path: ql/ql/src/ql.dbscheme.stats
|
||||
52
.github/workflows/ql-for-ql-tests.yml
vendored
52
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -1,52 +0,0 @@
|
||||
name: Run QL for QL Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "ql/**"
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "ql/**"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
qltest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@erik-krogh/ql
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
- name: Build extractor
|
||||
run: |
|
||||
cd ql;
|
||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
||||
env "PATH=$PATH:$codeqlpath" ./create-extractor-pack.sh
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries ql/ql/test
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Check QL formatting
|
||||
run: |
|
||||
find ql/ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 "${CODEQL}" query format --check-only
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Check QL compilation
|
||||
run: |
|
||||
"${CODEQL}" query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ql/extractor-pack" "ql/ql/src" "ql/ql/examples"
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
@@ -25,6 +25,3 @@
|
||||
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||
/docs/ql-language-reference/ @github/codeql-frontend-reviewers
|
||||
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
|
||||
|
||||
# QL for QL reviewers
|
||||
/ql/ @erik-krogh @tausbn
|
||||
@@ -1,11 +1,11 @@
|
||||
# CodeQL
|
||||
|
||||
This open source repository contains the standard CodeQL libraries and queries that power [GitHub Advanced Security](https://github.com/features/security/code) and the other application security products that [GitHub](https://github.com/features/security/) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
|
||||
This open source repository contains the standard CodeQL libraries and queries that power [LGTM](https://lgtm.com) and the other CodeQL products that [GitHub](https://github.com) makes available to its customers worldwide. For the queries, libraries, and extractor that power Go analysis, visit the [CodeQL for Go repository](https://github.com/github/codeql-go).
|
||||
|
||||
## How do I learn CodeQL and run queries?
|
||||
|
||||
There is [extensive documentation](https://codeql.github.com/docs/) on getting started with writing CodeQL.
|
||||
You can use the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension or the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com (Semmle Legacy product) to try out your queries on any open source project that's currently being analyzed.
|
||||
You can use the [interactive query console](https://lgtm.com/help/lgtm/using-query-console) on LGTM.com or the [CodeQL for Visual Studio Code](https://codeql.github.com/docs/codeql-for-visual-studio-code/) extension to try out your queries on any open source project that's currently being analyzed.
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -13,7 +13,7 @@ We welcome contributions to our standard library and standard checks. Do you hav
|
||||
|
||||
## License
|
||||
|
||||
The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com). The use of CodeQL on open source code is licensed under specific [Terms & Conditions](https://securitylab.github.com/tools/codeql/license/) UNLESS you have a commercial license in place. If you'd like to use CodeQL with a commercial codebase, please [contact us](https://github.com/enterprise/contact) for further help.
|
||||
The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com).
|
||||
|
||||
## Visual Studio Code integration
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
# benjamin-buttons.md
|
||||
|
||||
This file describes the changes that have been applied to
|
||||
the library to make it behave as if it was younger.
|
||||
|
||||
## TaintedPath.ql
|
||||
|
||||
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
|
||||
|
||||
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
|
||||
|
||||
Sinks added between 2018-08-02 and 2020-01-01 have been removed. Found by looking at:
|
||||
|
||||
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+pathinjection
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+tainted-path
|
||||
|
||||
Sinks from the "graceful-fs" and "fs-extra" (added before the open-sourcing squash).
|
||||
|
||||
## Xss.ql
|
||||
|
||||
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
|
||||
|
||||
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
|
||||
|
||||
- recursive type tracking for `jQuery::dollar`, `DOM::domValueRef`.
|
||||
|
||||
## SqlInjection.ql
|
||||
|
||||
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
|
||||
|
||||
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-089
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
|
||||
|
||||
Sinks added between 2018-08-02 and 2020-01-01 have been removed. Found by looking at:
|
||||
|
||||
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-089
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
|
||||
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sql
|
||||
|
||||
TypeTracking in SQL.qll (added before the open-sourcing squash)
|
||||
|
||||
The model of `mssql` and `sequelize` (added before the open-sourcing squash)
|
||||
|
||||
## PseudoProperties
|
||||
|
||||
Pseudo-properties (`$name$`) used in type-tracking and global dataflow configurations have been disabled.
|
||||
Found by searching for `"\$.*\$"`.
|
||||
@@ -452,15 +452,9 @@
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll"
|
||||
],
|
||||
"CryptoAlgorithms Python/JS/Ruby": [
|
||||
"CryptoAlgorithms Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
|
||||
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/CryptoAlgorithms.qll"
|
||||
],
|
||||
"CryptoAlgorithmNames Python/JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/internal/CryptoAlgorithmNames.qll",
|
||||
"python/ql/lib/semmle/python/concepts/internal/CryptoAlgorithmNames.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/internal/CryptoAlgorithmNames.qll"
|
||||
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll"
|
||||
],
|
||||
"SensitiveDataHeuristics Python/JS": [
|
||||
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.Build" Version="16.9.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
lgtm,codescanning
|
||||
* The "Cleartext transmission of sensitive information" (`cpp/cleartext-transmission`) query has been improved, reducing the number of false positive results when encryption is present.
|
||||
@@ -73,7 +73,7 @@ class Options extends string {
|
||||
* __assume(0);
|
||||
* ```
|
||||
* (note that in this case if the hint is wrong and the expression is reached at
|
||||
* runtime, the program's behavior is undefined)
|
||||
* runtime, the program's behaviour is undefined)
|
||||
*/
|
||||
predicate exprExits(Expr e) {
|
||||
e.(AssumeExpr).getChild(0).(CompileTimeConstantInt).getIntValue() = 0 or
|
||||
|
||||
@@ -50,7 +50,7 @@ class CustomOptions extends Options {
|
||||
* __assume(0);
|
||||
* ```
|
||||
* (note that in this case if the hint is wrong and the expression is reached at
|
||||
* runtime, the program's behavior is undefined)
|
||||
* runtime, the program's behaviour is undefined)
|
||||
*/
|
||||
override predicate exprExits(Expr e) { Options.super.exprExits(e) }
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition {
|
||||
* dependencies. Without this information, range analysis might work for
|
||||
* simple cases but will go into infinite loops on complex code.
|
||||
*
|
||||
* For example, when modeling the definition by reference in a call to an
|
||||
* For example, when modelling the definition by reference in a call to an
|
||||
* overloaded `operator=`, written as `v = e`, the definition of `(this, v)`
|
||||
* depends on `e`.
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* `Instruction` level), and then using the array length analysis and the range
|
||||
* analysis together to prove that some of these pointer dereferences are safe.
|
||||
*
|
||||
* The analysis is soundy, i.e. it is sound if no undefined behavior is present
|
||||
* The analysis is soundy, i.e. it is sound if no undefined behaviour is present
|
||||
* in the program.
|
||||
* Furthermore, it crucially depends on the soundiness of the range analysis and
|
||||
* the array length analysis.
|
||||
|
||||
@@ -4,4 +4,5 @@ groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/cpp-upgrades: ^0.0.3
|
||||
|
||||
@@ -9,83 +9,6 @@ import semmle.code.cpp.models.interfaces.FormattingFunction
|
||||
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
private newtype TBufferWriteEstimationReason =
|
||||
TNoSpecifiedEstimateReason() or
|
||||
TTypeBoundsAnalysis() or
|
||||
TValueFlowAnalysis()
|
||||
|
||||
/**
|
||||
* A reason for a specific buffer write size estimate.
|
||||
*/
|
||||
abstract class BufferWriteEstimationReason extends TBufferWriteEstimationReason {
|
||||
/**
|
||||
* Returns the name of the concrete class.
|
||||
*/
|
||||
abstract string toString();
|
||||
|
||||
/**
|
||||
* Returns a human readable representation of this reason.
|
||||
*/
|
||||
abstract string getDescription();
|
||||
|
||||
/**
|
||||
* Combine estimate reasons. Used to give a reason for the size of a format string
|
||||
* conversion given reasons coming from its individual specifiers.
|
||||
*/
|
||||
abstract BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other);
|
||||
}
|
||||
|
||||
/**
|
||||
* No particular reason given. This is currently used for backward compatibility so that
|
||||
* classes derived from BufferWrite and overriding `getMaxData/0` still work with the
|
||||
* queries as intended.
|
||||
*/
|
||||
class NoSpecifiedEstimateReason extends BufferWriteEstimationReason, TNoSpecifiedEstimateReason {
|
||||
override string toString() { result = "NoSpecifiedEstimateReason" }
|
||||
|
||||
override string getDescription() { result = "no reason specified" }
|
||||
|
||||
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
|
||||
// this reason should not be used in format specifiers, so it should not be combined
|
||||
// with other reasons
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The estimation comes from rough bounds just based on the type (e.g.
|
||||
* `0 <= x < 2^32` for an unsigned 32 bit integer).
|
||||
*/
|
||||
class TypeBoundsAnalysis extends BufferWriteEstimationReason, TTypeBoundsAnalysis {
|
||||
override string toString() { result = "TypeBoundsAnalysis" }
|
||||
|
||||
override string getDescription() { result = "based on type bounds" }
|
||||
|
||||
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
|
||||
other != TNoSpecifiedEstimateReason() and result = TTypeBoundsAnalysis()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The estimation comes from non trivial bounds found via actual flow analysis.
|
||||
* For example
|
||||
* ```
|
||||
* unsigned u = x;
|
||||
* if (u < 1000) {
|
||||
* //... <- estimation done here based on u
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class ValueFlowAnalysis extends BufferWriteEstimationReason, TValueFlowAnalysis {
|
||||
override string toString() { result = "ValueFlowAnalysis" }
|
||||
|
||||
override string getDescription() { result = "based on flow analysis of value bounds" }
|
||||
|
||||
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
|
||||
other != TNoSpecifiedEstimateReason() and result = other
|
||||
}
|
||||
}
|
||||
|
||||
class PrintfFormatAttribute extends FormatAttribute {
|
||||
PrintfFormatAttribute() { this.getArchetype() = ["printf", "__printf__"] }
|
||||
}
|
||||
@@ -1067,14 +990,7 @@ class FormatLiteral extends Literal {
|
||||
* conversion specifier of this format string; has no result if this cannot
|
||||
* be determined.
|
||||
*/
|
||||
int getMaxConvertedLength(int n) { result = max(getMaxConvertedLength(n, _)) }
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by the nth
|
||||
* conversion specifier of this format string, specifying the estimation reason;
|
||||
* has no result if this cannot be determined.
|
||||
*/
|
||||
int getMaxConvertedLength(int n, BufferWriteEstimationReason reason) {
|
||||
int getMaxConvertedLength(int n) {
|
||||
exists(int len |
|
||||
(
|
||||
(
|
||||
@@ -1086,12 +1002,10 @@ class FormatLiteral extends Literal {
|
||||
) and
|
||||
(
|
||||
this.getConversionChar(n) = "%" and
|
||||
len = 1 and
|
||||
reason = TValueFlowAnalysis()
|
||||
len = 1
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "c" and
|
||||
len = 1 and
|
||||
reason = TValueFlowAnalysis() // e.g. 'a'
|
||||
len = 1 // e.g. 'a'
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "f" and
|
||||
exists(int dot, int afterdot |
|
||||
@@ -1105,8 +1019,7 @@ class FormatLiteral extends Literal {
|
||||
afterdot = 6
|
||||
) and
|
||||
len = 1 + 309 + dot + afterdot
|
||||
) and
|
||||
reason = TTypeBoundsAnalysis() // e.g. -1e308="-100000"...
|
||||
) // e.g. -1e308="-100000"...
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "e" and
|
||||
exists(int dot, int afterdot |
|
||||
@@ -1120,8 +1033,7 @@ class FormatLiteral extends Literal {
|
||||
afterdot = 6
|
||||
) and
|
||||
len = 1 + 1 + dot + afterdot + 1 + 1 + 3
|
||||
) and
|
||||
reason = TTypeBoundsAnalysis() // -1e308="-1.000000e+308"
|
||||
) // -1e308="-1.000000e+308"
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "g" and
|
||||
exists(int dot, int afterdot |
|
||||
@@ -1144,80 +1056,67 @@ class FormatLiteral extends Literal {
|
||||
// (e.g. 123456, 0.000123456 are just OK)
|
||||
// so case %f can be at most P characters + 4 zeroes, sign, dot = P + 6
|
||||
len = (afterdot.maximum(1) + 6).maximum(1 + 1 + dot + afterdot + 1 + 1 + 3)
|
||||
) and
|
||||
reason = TTypeBoundsAnalysis() // (e.g. "-1.59203e-319")
|
||||
) // (e.g. "-1.59203e-319")
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = ["d", "i"] and
|
||||
// e.g. -2^31 = "-2147483648"
|
||||
exists(float typeBasedBound, float valueBasedBound |
|
||||
// The first case handles length sub-specifiers
|
||||
// Subtract one in the exponent because one bit is for the sign.
|
||||
// Add 1 to account for the possible sign in the output.
|
||||
typeBasedBound =
|
||||
1 + lengthInBase10(2.pow(this.getIntegralDisplayType(n).getSize() * 8 - 1)) and
|
||||
// The second case uses range analysis to deduce a length that's shorter than the length
|
||||
// of the number -2^31.
|
||||
exists(Expr arg, float lower, float upper, float typeLower, float typeUpper |
|
||||
arg = this.getUse().getConversionArgument(n) and
|
||||
lower = lowerBound(arg.getFullyConverted()) and
|
||||
upper = upperBound(arg.getFullyConverted()) and
|
||||
typeLower = exprMinVal(arg.getFullyConverted()) and
|
||||
typeUpper = exprMaxVal(arg.getFullyConverted())
|
||||
|
|
||||
valueBasedBound =
|
||||
max(int cand |
|
||||
// Include the sign bit in the length if it can be negative
|
||||
(
|
||||
if lower < 0
|
||||
then cand = 1 + lengthInBase10(lower.abs())
|
||||
else cand = lengthInBase10(lower)
|
||||
len =
|
||||
min(float cand |
|
||||
// The first case handles length sub-specifiers
|
||||
// Subtract one in the exponent because one bit is for the sign.
|
||||
// Add 1 to account for the possible sign in the output.
|
||||
cand = 1 + lengthInBase10(2.pow(this.getIntegralDisplayType(n).getSize() * 8 - 1))
|
||||
or
|
||||
// The second case uses range analysis to deduce a length that's shorter than the length
|
||||
// of the number -2^31.
|
||||
exists(Expr arg, float lower, float upper |
|
||||
arg = this.getUse().getConversionArgument(n) and
|
||||
lower = lowerBound(arg.getFullyConverted()) and
|
||||
upper = upperBound(arg.getFullyConverted())
|
||||
|
|
||||
cand =
|
||||
max(int cand0 |
|
||||
// Include the sign bit in the length if it can be negative
|
||||
(
|
||||
if lower < 0
|
||||
then cand0 = 1 + lengthInBase10(lower.abs())
|
||||
else cand0 = lengthInBase10(lower)
|
||||
)
|
||||
or
|
||||
(
|
||||
if upper < 0
|
||||
then cand0 = 1 + lengthInBase10(upper.abs())
|
||||
else cand0 = lengthInBase10(upper)
|
||||
)
|
||||
)
|
||||
or
|
||||
(
|
||||
if upper < 0
|
||||
then cand = 1 + lengthInBase10(upper.abs())
|
||||
else cand = lengthInBase10(upper)
|
||||
)
|
||||
) and
|
||||
(
|
||||
if lower > typeLower or upper < typeUpper
|
||||
then reason = TValueFlowAnalysis()
|
||||
else reason = TTypeBoundsAnalysis()
|
||||
)
|
||||
) and
|
||||
len = valueBasedBound.minimum(typeBasedBound)
|
||||
)
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "u" and
|
||||
// e.g. 2^32 - 1 = "4294967295"
|
||||
exists(float typeBasedBound, float valueBasedBound |
|
||||
// The first case handles length sub-specifiers
|
||||
typeBasedBound = lengthInBase10(2.pow(this.getIntegralDisplayType(n).getSize() * 8) - 1) and
|
||||
// The second case uses range analysis to deduce a length that's shorter than
|
||||
// the length of the number 2^31 - 1.
|
||||
exists(Expr arg, float lower, float upper, float typeLower, float typeUpper |
|
||||
arg = this.getUse().getConversionArgument(n) and
|
||||
lower = lowerBound(arg.getFullyConverted()) and
|
||||
upper = upperBound(arg.getFullyConverted()) and
|
||||
typeLower = exprMinVal(arg.getFullyConverted()) and
|
||||
typeUpper = exprMaxVal(arg.getFullyConverted())
|
||||
|
|
||||
valueBasedBound =
|
||||
lengthInBase10(max(float cand |
|
||||
len =
|
||||
min(float cand |
|
||||
// The first case handles length sub-specifiers
|
||||
cand = 2.pow(this.getIntegralDisplayType(n).getSize() * 8)
|
||||
or
|
||||
// The second case uses range analysis to deduce a length that's shorter than
|
||||
// the length of the number 2^31 - 1.
|
||||
exists(Expr arg, float lower |
|
||||
arg = this.getUse().getConversionArgument(n) and
|
||||
lower = lowerBound(arg.getFullyConverted())
|
||||
|
|
||||
cand =
|
||||
max(float cand0 |
|
||||
// If lower can be negative we use `(unsigned)-1` as the candidate value.
|
||||
lower < 0 and
|
||||
cand = 2.pow(any(IntType t | t.isUnsigned()).getSize() * 8)
|
||||
cand0 = 2.pow(any(IntType t | t.isUnsigned()).getSize() * 8)
|
||||
or
|
||||
cand = upper
|
||||
)) and
|
||||
(
|
||||
if lower > typeLower or upper < typeUpper
|
||||
then reason = TValueFlowAnalysis()
|
||||
else reason = TTypeBoundsAnalysis()
|
||||
cand0 = upperBound(arg.getFullyConverted())
|
||||
)
|
||||
)
|
||||
) and
|
||||
len = valueBasedBound.minimum(typeBasedBound)
|
||||
)
|
||||
|
|
||||
lengthInBase10(cand)
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "x" and
|
||||
// e.g. "12345678"
|
||||
@@ -1236,8 +1135,7 @@ class FormatLiteral extends Literal {
|
||||
(
|
||||
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||
)
|
||||
) and
|
||||
reason = TTypeBoundsAnalysis()
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "p" and
|
||||
exists(PointerType ptrType, int baseLen |
|
||||
@@ -1246,8 +1144,7 @@ class FormatLiteral extends Literal {
|
||||
(
|
||||
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||
)
|
||||
) and
|
||||
reason = TValueFlowAnalysis()
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "o" and
|
||||
// e.g. 2^32 - 1 = "37777777777"
|
||||
@@ -1266,16 +1163,14 @@ class FormatLiteral extends Literal {
|
||||
(
|
||||
if this.hasAlternateFlag(n) then len = 1 + baseLen else len = baseLen // "0"
|
||||
)
|
||||
) and
|
||||
reason = TTypeBoundsAnalysis()
|
||||
)
|
||||
or
|
||||
this.getConversionChar(n).toLowerCase() = "s" and
|
||||
len =
|
||||
min(int v |
|
||||
v = this.getPrecision(n) or
|
||||
v = this.getUse().getFormatArgument(n).(AnalysedString).getMaxLength() - 1 // (don't count null terminator)
|
||||
) and
|
||||
reason = TValueFlowAnalysis()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1287,19 +1182,10 @@ class FormatLiteral extends Literal {
|
||||
* determining whether a buffer overflow is caused by long float to string
|
||||
* conversions.
|
||||
*/
|
||||
int getMaxConvertedLengthLimited(int n) { result = max(getMaxConvertedLengthLimited(n, _)) }
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by the nth
|
||||
* conversion specifier of this format string, specifying the reason for the
|
||||
* estimation, except that float to string conversions are assumed to be 8
|
||||
* characters. This is helpful for determining whether a buffer overflow is
|
||||
* caused by long float to string conversions.
|
||||
*/
|
||||
int getMaxConvertedLengthLimited(int n, BufferWriteEstimationReason reason) {
|
||||
int getMaxConvertedLengthLimited(int n) {
|
||||
if this.getConversionChar(n).toLowerCase() = "f"
|
||||
then result = this.getMaxConvertedLength(n, reason).minimum(8)
|
||||
else result = this.getMaxConvertedLength(n, reason)
|
||||
then result = this.getMaxConvertedLength(n).minimum(8)
|
||||
else result = this.getMaxConvertedLength(n)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1339,35 +1225,29 @@ class FormatLiteral extends Literal {
|
||||
)
|
||||
}
|
||||
|
||||
private int getMaxConvertedLengthAfter(int n, BufferWriteEstimationReason reason) {
|
||||
private int getMaxConvertedLengthAfter(int n) {
|
||||
if n = this.getNumConvSpec()
|
||||
then result = this.getConstantSuffix().length() + 1 and reason = TValueFlowAnalysis()
|
||||
then result = this.getConstantSuffix().length() + 1
|
||||
else
|
||||
exists(BufferWriteEstimationReason headReason, BufferWriteEstimationReason tailReason |
|
||||
result =
|
||||
this.getConstantPart(n).length() + this.getMaxConvertedLength(n, headReason) +
|
||||
this.getMaxConvertedLengthAfter(n + 1, tailReason) and
|
||||
reason = headReason.combineWith(tailReason)
|
||||
)
|
||||
result =
|
||||
this.getConstantPart(n).length() + this.getMaxConvertedLength(n) +
|
||||
this.getMaxConvertedLengthAfter(n + 1)
|
||||
}
|
||||
|
||||
private int getMaxConvertedLengthAfterLimited(int n, BufferWriteEstimationReason reason) {
|
||||
private int getMaxConvertedLengthAfterLimited(int n) {
|
||||
if n = this.getNumConvSpec()
|
||||
then result = this.getConstantSuffix().length() + 1 and reason = TValueFlowAnalysis()
|
||||
then result = this.getConstantSuffix().length() + 1
|
||||
else
|
||||
exists(BufferWriteEstimationReason headReason, BufferWriteEstimationReason tailReason |
|
||||
result =
|
||||
this.getConstantPart(n).length() + this.getMaxConvertedLengthLimited(n, headReason) +
|
||||
this.getMaxConvertedLengthAfterLimited(n + 1, tailReason) and
|
||||
reason = headReason.combineWith(tailReason)
|
||||
)
|
||||
result =
|
||||
this.getConstantPart(n).length() + this.getMaxConvertedLengthLimited(n) +
|
||||
this.getMaxConvertedLengthAfterLimited(n + 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by this format
|
||||
* string. Has no result if this cannot be determined.
|
||||
*/
|
||||
int getMaxConvertedLength() { result = this.getMaxConvertedLengthAfter(0, _) }
|
||||
int getMaxConvertedLength() { result = this.getMaxConvertedLengthAfter(0) }
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by this format
|
||||
@@ -1375,24 +1255,5 @@ class FormatLiteral extends Literal {
|
||||
* characters. This is helpful for determining whether a buffer overflow
|
||||
* is caused by long float to string conversions.
|
||||
*/
|
||||
int getMaxConvertedLengthLimited() { result = this.getMaxConvertedLengthAfterLimited(0, _) }
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by this format
|
||||
* string, specifying the reason for the estimate. Has no result if no estimate
|
||||
* can be found.
|
||||
*/
|
||||
int getMaxConvertedLengthWithReason(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxConvertedLengthAfter(0, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum length of the string that can be produced by this format
|
||||
* string, specifying the reason for the estimate, except that float to string
|
||||
* conversions are assumed to be 8 characters. This is helpful for determining
|
||||
* whether a buffer overflow is caused by long float to string conversions.
|
||||
*/
|
||||
int getMaxConvertedLengthLimitedWithReason(BufferWriteEstimationReason reason) {
|
||||
result = this.getMaxConvertedLengthAfterLimited(0, reason)
|
||||
}
|
||||
int getMaxConvertedLengthLimited() { result = this.getMaxConvertedLengthAfterLimited(0) }
|
||||
}
|
||||
|
||||
@@ -153,11 +153,9 @@ library class SSAHelper extends int {
|
||||
* Modern Compiler Implementation by Andrew Appel.
|
||||
*/
|
||||
private predicate frontier_phi_node(StackVariable v, BasicBlock b) {
|
||||
exists(BasicBlock x |
|
||||
dominanceFrontier(x, b) and ssa_defn_rec(pragma[only_bind_into](v), pragma[only_bind_into](x))
|
||||
) and
|
||||
exists(BasicBlock x | dominanceFrontier(x, b) and ssa_defn_rec(v, x)) and
|
||||
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
|
||||
live_at_start_of_bb(pragma[only_bind_into](v), b)
|
||||
live_at_start_of_bb(v, b)
|
||||
}
|
||||
|
||||
private predicate ssa_defn_rec(StackVariable v, BasicBlock b) {
|
||||
|
||||
@@ -15,23 +15,11 @@ module Consistency {
|
||||
class ConsistencyConfiguration extends TConsistencyConfiguration {
|
||||
string toString() { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
|
||||
predicate uniqueEnclosingCallableExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
|
||||
predicate uniqueNodeLocationExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `missingLocation`. */
|
||||
predicate missingLocationExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
|
||||
predicate postWithInFlowExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
|
||||
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
|
||||
predicate reverseReadExclude(Node n) { none() }
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -58,7 +46,6 @@ module Consistency {
|
||||
n instanceof RelevantNode and
|
||||
c = count(nodeGetEnclosingCallable(n)) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
}
|
||||
@@ -79,7 +66,6 @@ module Consistency {
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
|
||||
msg = "Node should have one location but has " + c + "."
|
||||
)
|
||||
}
|
||||
@@ -90,8 +76,7 @@ module Consistency {
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
||||
)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
)
|
||||
@@ -187,7 +172,6 @@ module Consistency {
|
||||
|
||||
query predicate reverseRead(Node n, string msg) {
|
||||
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
|
||||
not any(ConsistencyConfiguration conf).reverseReadExclude(n) and
|
||||
msg = "Origin of readStep is missing a PostUpdateNode."
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ class LambdaCapture extends Locatable, @lambdacapture {
|
||||
* An identifier is captured by reference if:
|
||||
* - It is explicitly captured by reference.
|
||||
* - It is implicitly captured, and the lambda's default capture mode is by-reference.
|
||||
* - The identifier is "this". [Said behavior is dictated by the C++11 standard, but it
|
||||
* - The identifier is "this". [Said behaviour is dictated by the C++11 standard, but it
|
||||
* is actually "*this" being captured rather than "this".]
|
||||
*/
|
||||
predicate isCapturedByReference() { lambda_capture(this, _, _, _, true, _, _) }
|
||||
|
||||
@@ -63,7 +63,7 @@ private module VirtualDispatch {
|
||||
this.flowsFrom(other, allowOtherFromArg)
|
||||
|
|
||||
// Call argument
|
||||
exists(DataFlowCall call, Position i |
|
||||
exists(DataFlowCall call, int i |
|
||||
other
|
||||
.(DataFlow::ParameterNode)
|
||||
.isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and
|
||||
@@ -268,6 +268,16 @@ Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) {
|
||||
)
|
||||
}
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition extends int {
|
||||
ParameterPosition() { any(ParameterNode p).isParameterOf(_, this) }
|
||||
}
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition extends int {
|
||||
ArgumentPosition() { any(ArgumentNode a).argumentOf(_, this) }
|
||||
}
|
||||
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
@@ -15,23 +15,11 @@ module Consistency {
|
||||
class ConsistencyConfiguration extends TConsistencyConfiguration {
|
||||
string toString() { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
|
||||
predicate uniqueEnclosingCallableExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
|
||||
predicate uniqueNodeLocationExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `missingLocation`. */
|
||||
predicate missingLocationExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
|
||||
predicate postWithInFlowExclude(Node n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
|
||||
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
|
||||
|
||||
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
|
||||
predicate reverseReadExclude(Node n) { none() }
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -58,7 +46,6 @@ module Consistency {
|
||||
n instanceof RelevantNode and
|
||||
c = count(nodeGetEnclosingCallable(n)) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
|
||||
msg = "Node should have one enclosing callable but has " + c + "."
|
||||
)
|
||||
}
|
||||
@@ -79,7 +66,6 @@ module Consistency {
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
c != 1 and
|
||||
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
|
||||
msg = "Node should have one location but has " + c + "."
|
||||
)
|
||||
}
|
||||
@@ -90,8 +76,7 @@ module Consistency {
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
||||
)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
)
|
||||
@@ -187,7 +172,6 @@ module Consistency {
|
||||
|
||||
query predicate reverseRead(Node n, string msg) {
|
||||
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
|
||||
not any(ConsistencyConfiguration conf).reverseReadExclude(n) and
|
||||
msg = "Origin of readStep is missing a PostUpdateNode."
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ abstract class ArgumentNode extends OperandNode {
|
||||
* Holds if this argument occurs at the given position in the given call.
|
||||
* The instance argument is considered to have index `-1`.
|
||||
*/
|
||||
abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
|
||||
abstract predicate argumentOf(DataFlowCall call, int pos);
|
||||
|
||||
/** Gets the call in which this node is an argument. */
|
||||
DataFlowCall getCall() { this.argumentOf(result, _) }
|
||||
@@ -42,9 +42,7 @@ private class PrimaryArgumentNode extends ArgumentNode {
|
||||
|
||||
PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
op = call.getArgumentOperand(pos.(DirectPosition).getIndex())
|
||||
}
|
||||
override predicate argumentOf(DataFlowCall call, int pos) { op = call.getArgumentOperand(pos) }
|
||||
|
||||
override string toString() {
|
||||
exists(Expr unconverted |
|
||||
@@ -73,9 +71,9 @@ private class SideEffectArgumentNode extends ArgumentNode {
|
||||
|
||||
SideEffectArgumentNode() { op = read.getSideEffectOperand() }
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
override predicate argumentOf(DataFlowCall call, int pos) {
|
||||
read.getPrimaryInstruction() = call and
|
||||
pos.(IndirectionPosition).getIndex() = read.getIndex()
|
||||
pos = getArgumentPosOfSideEffect(read.getIndex())
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
@@ -92,54 +90,6 @@ private class SideEffectArgumentNode extends ArgumentNode {
|
||||
}
|
||||
}
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition = Position;
|
||||
|
||||
/** An argument position represented by an integer. */
|
||||
class ArgumentPosition = Position;
|
||||
|
||||
class Position extends TPosition {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
class DirectPosition extends TDirectPosition {
|
||||
int index;
|
||||
|
||||
DirectPosition() { this = TDirectPosition(index) }
|
||||
|
||||
string toString() {
|
||||
index = -1 and
|
||||
result = "this"
|
||||
or
|
||||
index != -1 and
|
||||
result = index.toString()
|
||||
}
|
||||
|
||||
int getIndex() { result = index }
|
||||
}
|
||||
|
||||
class IndirectionPosition extends TIndirectionPosition {
|
||||
int index;
|
||||
|
||||
IndirectionPosition() { this = TIndirectionPosition(index) }
|
||||
|
||||
string toString() {
|
||||
index = -1 and
|
||||
result = "this"
|
||||
or
|
||||
index != -1 and
|
||||
result = index.toString()
|
||||
}
|
||||
|
||||
int getIndex() { result = index }
|
||||
}
|
||||
|
||||
newtype TPosition =
|
||||
TDirectPosition(int index) { exists(any(CallInstruction c).getArgument(index)) } or
|
||||
TIndirectionPosition(int index) {
|
||||
exists(ReadSideEffectInstruction instr | instr.getIndex() = index)
|
||||
}
|
||||
|
||||
private newtype TReturnKind =
|
||||
TNormalReturnKind() or
|
||||
TIndirectReturnKind(ParameterIndex index)
|
||||
|
||||
@@ -490,6 +490,19 @@ class ExprNode extends InstructionNode {
|
||||
override string toString() { result = this.asConvertedExpr().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use. Translates a parameter/argument index into a negative
|
||||
* number that denotes the index of its side effect (pointer indirection).
|
||||
*/
|
||||
bindingset[index]
|
||||
int getArgumentPosOfSideEffect(int index) {
|
||||
// -1 -> -2
|
||||
// 0 -> -3
|
||||
// 1 -> -4
|
||||
// ...
|
||||
result = -3 - index
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph. This includes both explicit parameters such as `x` in `f(x)`
|
||||
@@ -512,7 +525,7 @@ class ParameterNode extends InstructionNode {
|
||||
* implicit `this` parameter is considered to have position `-1`, and
|
||||
* pointer-indirection parameters are at further negative positions.
|
||||
*/
|
||||
predicate isParameterOf(Function f, ParameterPosition pos) { none() } // overridden by subclasses
|
||||
predicate isParameterOf(Function f, int pos) { none() } // overridden by subclasses
|
||||
}
|
||||
|
||||
/** An explicit positional parameter, not including `this` or `...`. */
|
||||
@@ -521,8 +534,8 @@ private class ExplicitParameterNode extends ParameterNode {
|
||||
|
||||
ExplicitParameterNode() { exists(instr.getParameter()) }
|
||||
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
f.getParameter(pos.(DirectPosition).getIndex()) = instr.getParameter()
|
||||
override predicate isParameterOf(Function f, int pos) {
|
||||
f.getParameter(pos) = instr.getParameter()
|
||||
}
|
||||
|
||||
/** Gets the `Parameter` associated with this node. */
|
||||
@@ -537,8 +550,8 @@ class ThisParameterNode extends ParameterNode {
|
||||
|
||||
ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable }
|
||||
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
pos.(DirectPosition).getIndex() = -1 and instr.getEnclosingFunction() = f
|
||||
override predicate isParameterOf(Function f, int pos) {
|
||||
pos = -1 and instr.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
override string toString() { result = "this" }
|
||||
@@ -548,12 +561,12 @@ class ThisParameterNode extends ParameterNode {
|
||||
class ParameterIndirectionNode extends ParameterNode {
|
||||
override InitializeIndirectionInstruction instr;
|
||||
|
||||
override predicate isParameterOf(Function f, ParameterPosition pos) {
|
||||
override predicate isParameterOf(Function f, int pos) {
|
||||
exists(int index |
|
||||
instr.getEnclosingFunction() = f and
|
||||
instr.hasIndex(index)
|
||||
|
|
||||
pos.(IndirectionPosition).getIndex() = index
|
||||
pos = getArgumentPosOfSideEffect(index)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -659,15 +659,4 @@ module Consistency {
|
||||
not phiHasInputFromBlock(_, def, _) and
|
||||
not uncertainWriteDefinitionInput(_, def)
|
||||
}
|
||||
|
||||
query predicate notDominatedByDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef) |
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
(bb != bbDef or i < iDef)
|
||||
or
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
not ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,16 @@ private newtype TDefOrUse =
|
||||
TExplicitUse(Operand op) { isExplicitUse(op) } or
|
||||
TReturnParamIndirection(Operand op) { returnParameterIndirection(op, _) }
|
||||
|
||||
pragma[nomagic]
|
||||
private int getRank(DefOrUse defOrUse, IRBlock block) {
|
||||
defOrUse =
|
||||
rank[result](int i, DefOrUse cand |
|
||||
block.getInstruction(i) = toInstruction(cand)
|
||||
|
|
||||
cand order by i
|
||||
)
|
||||
}
|
||||
|
||||
private class DefOrUse extends TDefOrUse {
|
||||
/** Gets the instruction associated with this definition, if any. */
|
||||
Instruction asDef() { none() }
|
||||
@@ -64,10 +74,9 @@ private class DefOrUse extends TDefOrUse {
|
||||
/** Gets the block of this definition or use. */
|
||||
abstract IRBlock getBlock();
|
||||
|
||||
/** Holds if this definition or use has index `index` in block `block`. */
|
||||
final predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
block.getInstruction(index) = toInstruction(this)
|
||||
}
|
||||
/** Holds if this definition or use has rank `rank` in block `block`. */
|
||||
cached
|
||||
final predicate hasRankInBlock(IRBlock block, int rnk) { rnk = getRank(this, block) }
|
||||
|
||||
/** Gets the location of this element. */
|
||||
abstract Cpp::Location getLocation();
|
||||
@@ -304,8 +313,8 @@ cached
|
||||
private module Cached {
|
||||
private predicate defUseFlow(Node nodeFrom, Node nodeTo) {
|
||||
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, DefOrUse defOrUse, Use use |
|
||||
defOrUse.hasIndexInBlock(bb1, i1) and
|
||||
use.hasIndexInBlock(bb2, i2) and
|
||||
defOrUse.hasRankInBlock(bb1, i1) and
|
||||
use.hasRankInBlock(bb2, i2) and
|
||||
adjacentDefRead(_, bb1, i1, bb2, i2) and
|
||||
nodeFrom.asInstruction() = toInstruction(defOrUse) and
|
||||
flowOutOfAddressStep(use.getOperand(), nodeTo)
|
||||
@@ -317,9 +326,9 @@ private module Cached {
|
||||
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, Def def, Use use |
|
||||
nodeFrom.isTerminal() and
|
||||
def.getInstruction() = nodeFrom.getStoreInstruction() and
|
||||
def.hasIndexInBlock(bb1, i1) and
|
||||
def.hasRankInBlock(bb1, i1) and
|
||||
adjacentDefRead(_, bb1, i1, bb2, i2) and
|
||||
use.hasIndexInBlock(bb2, i2) and
|
||||
use.hasRankInBlock(bb2, i2) and
|
||||
flowOutOfAddressStep(use.getOperand(), nodeTo)
|
||||
)
|
||||
or
|
||||
@@ -350,8 +359,8 @@ private module Cached {
|
||||
|
||||
private predicate fromReadNode(ReadNode nodeFrom, Node nodeTo) {
|
||||
exists(IRBlock bb1, int i1, IRBlock bb2, int i2, Use use1, Use use2 |
|
||||
use1.hasIndexInBlock(bb1, i1) and
|
||||
use2.hasIndexInBlock(bb2, i2) and
|
||||
use1.hasRankInBlock(bb1, i1) and
|
||||
use2.hasRankInBlock(bb2, i2) and
|
||||
use1.getOperand().getDef() = nodeFrom.getInstruction() and
|
||||
adjacentDefRead(_, bb1, i1, bb2, i2) and
|
||||
flowOutOfAddressStep(use2.getOperand(), nodeTo)
|
||||
@@ -362,7 +371,7 @@ private module Cached {
|
||||
exists(PhiNode phi, Use use, IRBlock block, int rnk |
|
||||
phi = nodeFrom.getPhiNode() and
|
||||
adjacentDefRead(phi, _, _, block, rnk) and
|
||||
use.hasIndexInBlock(block, rnk) and
|
||||
use.hasRankInBlock(block, rnk) and
|
||||
flowOutOfAddressStep(use.getOperand(), nodeTo)
|
||||
)
|
||||
}
|
||||
@@ -370,7 +379,7 @@ private module Cached {
|
||||
private predicate toPhiNode(Node nodeFrom, SsaPhiNode nodeTo) {
|
||||
// Flow to phi nodes
|
||||
exists(Def def, IRBlock block, int rnk |
|
||||
def.hasIndexInBlock(block, rnk) and
|
||||
def.hasRankInBlock(block, rnk) and
|
||||
nodeTo.hasInputAtRankInBlock(block, rnk)
|
||||
|
|
||||
exists(StoreNodeInstr storeNode |
|
||||
@@ -503,8 +512,8 @@ private module Cached {
|
||||
|
|
||||
store = def.getInstruction() and
|
||||
store.getSourceValueOperand() = operand and
|
||||
def.hasIndexInBlock(block1, rnk1) and
|
||||
use.hasIndexInBlock(block2, rnk2) and
|
||||
def.hasRankInBlock(block1, rnk1) and
|
||||
use.hasRankInBlock(block2, rnk2) and
|
||||
adjacentDefRead(_, block1, rnk1, block2, rnk2)
|
||||
|
|
||||
// The shared SSA library has determined that `use` is the next use of the operand
|
||||
@@ -534,12 +543,12 @@ private module Cached {
|
||||
not operand = getSourceAddressOperand(_) and
|
||||
exists(Use use1, Use use2, IRBlock block1, int rnk1, IRBlock block2, int rnk2 |
|
||||
use1.getOperand() = operand and
|
||||
use1.hasIndexInBlock(block1, rnk1) and
|
||||
use1.hasRankInBlock(block1, rnk1) and
|
||||
// Don't flow to the next use if this use is part of a store operation that totally
|
||||
// overrides a variable.
|
||||
not explicitWrite(true, _, use1.getOperand().getDef()) and
|
||||
adjacentDefRead(_, block1, rnk1, block2, rnk2) and
|
||||
use2.hasIndexInBlock(block2, rnk2) and
|
||||
use2.hasRankInBlock(block2, rnk2) and
|
||||
flowOutOfAddressStep(use2.getOperand(), nodeTo)
|
||||
)
|
||||
or
|
||||
@@ -611,7 +620,7 @@ import Cached
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
exists(Def def |
|
||||
def.hasIndexInBlock(bb, i) and
|
||||
def.hasRankInBlock(bb, i) and
|
||||
v = def.getSourceVariable() and
|
||||
(if def.isCertain() then certain = true else certain = false)
|
||||
)
|
||||
@@ -623,7 +632,7 @@ predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
*/
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(Use use |
|
||||
use.hasIndexInBlock(bb, i) and
|
||||
use.hasRankInBlock(bb, i) and
|
||||
v = use.getSourceVariable() and
|
||||
certain = true
|
||||
)
|
||||
|
||||
@@ -266,20 +266,6 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
||||
bitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate initializeParameterInstructionHasVariable(
|
||||
IRVariable var, InitializeParameterInstruction init
|
||||
) {
|
||||
init.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate instructionInitializesThisInFunction(
|
||||
Language::Function f, InitializeParameterInstruction init
|
||||
) {
|
||||
initializeParameterInstructionHasVariable(any(IRThisVariable var), pragma[only_bind_into](init)) and
|
||||
init.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
private predicate isArgumentForParameter(
|
||||
CallInstruction ci, Operand operand, InitializeParameterInstruction init
|
||||
) {
|
||||
@@ -289,7 +275,8 @@ private predicate isArgumentForParameter(
|
||||
(
|
||||
init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
|
||||
or
|
||||
instructionInitializesThisInFunction(f, init) and
|
||||
init.getIRVariable() instanceof IRThisVariable and
|
||||
unique( | | init.getEnclosingFunction()) = f and
|
||||
operand instanceof ThisArgumentOperand
|
||||
) and
|
||||
not Language::isFunctionVirtual(f) and
|
||||
|
||||
@@ -266,20 +266,6 @@ private predicate operandReturned(Operand operand, IntValue bitOffset) {
|
||||
bitOffset = Ints::unknown()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate initializeParameterInstructionHasVariable(
|
||||
IRVariable var, InitializeParameterInstruction init
|
||||
) {
|
||||
init.getIRVariable() = var
|
||||
}
|
||||
|
||||
private predicate instructionInitializesThisInFunction(
|
||||
Language::Function f, InitializeParameterInstruction init
|
||||
) {
|
||||
initializeParameterInstructionHasVariable(any(IRThisVariable var), pragma[only_bind_into](init)) and
|
||||
init.getEnclosingFunction() = f
|
||||
}
|
||||
|
||||
private predicate isArgumentForParameter(
|
||||
CallInstruction ci, Operand operand, InitializeParameterInstruction init
|
||||
) {
|
||||
@@ -289,7 +275,8 @@ private predicate isArgumentForParameter(
|
||||
(
|
||||
init.getParameter() = f.getParameter(operand.(PositionalArgumentOperand).getIndex())
|
||||
or
|
||||
instructionInitializesThisInFunction(f, init) and
|
||||
init.getIRVariable() instanceof IRThisVariable and
|
||||
unique( | | init.getEnclosingFunction()) = f and
|
||||
operand instanceof ThisArgumentOperand
|
||||
) and
|
||||
not Language::isFunctionVirtual(f) and
|
||||
|
||||
@@ -71,30 +71,13 @@ abstract class BufferWrite extends Expr {
|
||||
*/
|
||||
int getMaxData() { none() }
|
||||
|
||||
/**
|
||||
* Gets an upper bound to the amount of data that's being written (if one
|
||||
* can be found), specifying the reason for the estimation.
|
||||
*/
|
||||
int getMaxData(BufferWriteEstimationReason reason) {
|
||||
reason instanceof NoSpecifiedEstimateReason and result = getMaxData()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an upper bound to the amount of data that's being written (if one
|
||||
* can be found), except that float to string conversions are assumed to be
|
||||
* much smaller (8 bytes) than their true maximum length. This can be
|
||||
* helpful in determining the cause of a buffer overflow issue.
|
||||
*/
|
||||
int getMaxDataLimited() { result = getMaxData() }
|
||||
|
||||
/**
|
||||
* Gets an upper bound to the amount of data that's being written (if one
|
||||
* can be found), specifying the reason for the estimation, except that
|
||||
* float to string conversions are assumed to be much smaller (8 bytes)
|
||||
* than their true maximum length. This can be helpful in determining the
|
||||
* cause of a buffer overflow issue.
|
||||
*/
|
||||
int getMaxDataLimited(BufferWriteEstimationReason reason) { result = getMaxData(reason) }
|
||||
int getMaxDataLimited() { result = this.getMaxData() }
|
||||
|
||||
/**
|
||||
* Gets the size of a single character of the type this
|
||||
@@ -152,16 +135,10 @@ class StrCopyBW extends BufferWriteCall {
|
||||
result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize()
|
||||
}
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
// when result exists, it is an exact flow analysis
|
||||
reason instanceof ValueFlowAnalysis and
|
||||
override int getMaxData() {
|
||||
result =
|
||||
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize()
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) { result = getMaxDataImpl(reason) }
|
||||
|
||||
override int getMaxData() { result = max(getMaxDataImpl(_)) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,16 +173,10 @@ class StrCatBW extends BufferWriteCall {
|
||||
result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize()
|
||||
}
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
// when result exists, it is an exact flow analysis
|
||||
reason instanceof ValueFlowAnalysis and
|
||||
override int getMaxData() {
|
||||
result =
|
||||
this.getArgument(this.getParamSrc()).(AnalysedString).getMaxLength() * this.getCharSize()
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) { result = getMaxDataImpl(reason) }
|
||||
|
||||
override int getMaxData() { result = max(getMaxDataImpl(_)) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,29 +233,19 @@ class SprintfBW extends BufferWriteCall {
|
||||
|
||||
override Expr getDest() { result = this.getArgument(f.getOutputParameterIndex(false)) }
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
override int getMaxData() {
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.(FormattingFunctionCall).getFormat() and
|
||||
result = fl.getMaxConvertedLengthWithReason(reason) * this.getCharSize()
|
||||
result = fl.getMaxConvertedLength() * this.getCharSize()
|
||||
)
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) { result = getMaxDataImpl(reason) }
|
||||
|
||||
override int getMaxData() { result = max(getMaxDataImpl(_)) }
|
||||
|
||||
private int getMaxDataLimitedImpl(BufferWriteEstimationReason reason) {
|
||||
override int getMaxDataLimited() {
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.(FormattingFunctionCall).getFormat() and
|
||||
result = fl.getMaxConvertedLengthLimitedWithReason(reason) * this.getCharSize()
|
||||
result = fl.getMaxConvertedLengthLimited() * this.getCharSize()
|
||||
)
|
||||
}
|
||||
|
||||
override int getMaxDataLimited(BufferWriteEstimationReason reason) {
|
||||
result = getMaxDataLimitedImpl(reason)
|
||||
}
|
||||
|
||||
override int getMaxDataLimited() { result = max(getMaxDataLimitedImpl(_)) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,29 +336,19 @@ class SnprintfBW extends BufferWriteCall {
|
||||
result = this.getArgument(this.getParamSize()).getValue().toInt() * this.getCharSize()
|
||||
}
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
override int getMaxData() {
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.(FormattingFunctionCall).getFormat() and
|
||||
result = fl.getMaxConvertedLengthWithReason(reason) * this.getCharSize()
|
||||
result = fl.getMaxConvertedLength() * this.getCharSize()
|
||||
)
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) { result = getMaxDataImpl(reason) }
|
||||
|
||||
override int getMaxData() { result = max(getMaxDataImpl(_)) }
|
||||
|
||||
private int getMaxDataLimitedImpl(BufferWriteEstimationReason reason) {
|
||||
override int getMaxDataLimited() {
|
||||
exists(FormatLiteral fl |
|
||||
fl = this.(FormattingFunctionCall).getFormat() and
|
||||
result = fl.getMaxConvertedLengthLimitedWithReason(reason) * this.getCharSize()
|
||||
result = fl.getMaxConvertedLengthLimited() * this.getCharSize()
|
||||
)
|
||||
}
|
||||
|
||||
override int getMaxDataLimited(BufferWriteEstimationReason reason) {
|
||||
result = getMaxDataLimitedImpl(reason)
|
||||
}
|
||||
|
||||
override int getMaxDataLimited() { result = max(getMaxDataLimitedImpl(_)) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,9 +436,7 @@ class ScanfBW extends BufferWrite {
|
||||
|
||||
override Expr getDest() { result = this }
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
// when this returns, it is based on exact flow analysis
|
||||
reason instanceof ValueFlowAnalysis and
|
||||
override int getMaxData() {
|
||||
exists(ScanfFunctionCall fc, ScanfFormatLiteral fl, int arg |
|
||||
this = fc.getArgument(arg) and
|
||||
fl = fc.getFormat() and
|
||||
@@ -495,10 +444,6 @@ class ScanfBW extends BufferWrite {
|
||||
)
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) { result = getMaxDataImpl(reason) }
|
||||
|
||||
override int getMaxData() { result = max(getMaxDataImpl(_)) }
|
||||
|
||||
override string getBWDesc() {
|
||||
exists(FunctionCall fc |
|
||||
this = fc.getArgument(_) and
|
||||
@@ -529,14 +474,8 @@ class RealpathBW extends BufferWriteCall {
|
||||
|
||||
override Expr getASource() { result = this.getArgument(0) }
|
||||
|
||||
private int getMaxDataImpl(BufferWriteEstimationReason reason) {
|
||||
// although there may be some unknown invariants guaranteeing that a real path is shorter than PATH_MAX, we can consider providing less than PATH_MAX a problem with high precision
|
||||
reason instanceof ValueFlowAnalysis and
|
||||
override int getMaxData() {
|
||||
result = path_max() and
|
||||
this = this // Suppress a compiler warning
|
||||
}
|
||||
|
||||
override int getMaxData(BufferWriteEstimationReason reason) { result = getMaxDataImpl(reason) }
|
||||
|
||||
override int getMaxData() { result = max(getMaxDataImpl(_)) }
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import semmle.code.cpp.controlflow.Dominance
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* Holds if the value of `use` is guarded using `abs`.
|
||||
@@ -17,16 +16,53 @@ import semmle.code.cpp.controlflow.Guards
|
||||
predicate guardedAbs(Operation e, Expr use) {
|
||||
exists(FunctionCall fc | fc.getTarget().getName() = ["abs", "labs", "llabs", "imaxabs"] |
|
||||
fc.getArgument(0).getAChild*() = use and
|
||||
exists(GuardCondition c | c.ensuresLt(fc, _, _, e.getBasicBlock(), true))
|
||||
guardedLesser(e, fc)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of `stmt` in basic block `block` (this is a thin layer
|
||||
* over `BasicBlock.getNode`, intended to improve performance).
|
||||
*/
|
||||
pragma[noinline]
|
||||
private int getStmtIndexInBlock(BasicBlock block, Stmt stmt) { block.getNode(result) = stmt }
|
||||
|
||||
pragma[inline]
|
||||
private predicate stmtDominates(Stmt dominator, Stmt dominated) {
|
||||
// In same block
|
||||
exists(BasicBlock block, int dominatorIndex, int dominatedIndex |
|
||||
dominatorIndex = getStmtIndexInBlock(block, dominator) and
|
||||
dominatedIndex = getStmtIndexInBlock(block, dominated) and
|
||||
dominatedIndex >= dominatorIndex
|
||||
)
|
||||
or
|
||||
// In (possibly) different blocks
|
||||
bbStrictlyDominates(dominator.getBasicBlock(), dominated.getBasicBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value of `use` is guarded to be less than something, and `e`
|
||||
* is in code controlled by that guard (where the guard condition held).
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate guardedLesser(Operation e, Expr use) {
|
||||
exists(GuardCondition c | c.ensuresLt(use, _, _, e.getBasicBlock(), true))
|
||||
exists(IfStmt c, RelationalOperation guard |
|
||||
use = guard.getLesserOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
stmtDominates(c.getThen(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(Loop c, RelationalOperation guard |
|
||||
use = guard.getLesserOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
stmtDominates(c.getStmt(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(ConditionalExpr c, RelationalOperation guard |
|
||||
use = guard.getLesserOperand().getAChild*() and
|
||||
guard = c.getCondition().getAChild*() and
|
||||
c.getThen().getAChild*() = e
|
||||
)
|
||||
or
|
||||
guardedAbs(e, use)
|
||||
}
|
||||
@@ -35,8 +71,25 @@ predicate guardedLesser(Operation e, Expr use) {
|
||||
* Holds if the value of `use` is guarded to be greater than something, and `e`
|
||||
* is in code controlled by that guard (where the guard condition held).
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate guardedGreater(Operation e, Expr use) {
|
||||
exists(GuardCondition c | c.ensuresLt(use, _, _, e.getBasicBlock(), false))
|
||||
exists(IfStmt c, RelationalOperation guard |
|
||||
use = guard.getGreaterOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
stmtDominates(c.getThen(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(Loop c, RelationalOperation guard |
|
||||
use = guard.getGreaterOperand().getAChild*() and
|
||||
guard = c.getControllingExpr().getAChild*() and
|
||||
stmtDominates(c.getStmt(), e.getEnclosingStmt())
|
||||
)
|
||||
or
|
||||
exists(ConditionalExpr c, RelationalOperation guard |
|
||||
use = guard.getGreaterOperand().getAChild*() and
|
||||
guard = c.getCondition().getAChild*() and
|
||||
c.getThen().getAChild*() = e
|
||||
)
|
||||
or
|
||||
guardedAbs(e, use)
|
||||
}
|
||||
|
||||
@@ -26,8 +26,6 @@ where
|
||||
// At least for C programs on Windows, BOOL is a common typedef for a type
|
||||
// representing BoolType.
|
||||
not bf.getType().hasName("BOOL") and
|
||||
// GLib's gboolean is a typedef for a type representing BoolType.
|
||||
not bf.getType().hasName("gboolean") and
|
||||
// If this is true, then there cannot be unsigned sign extension or overflow.
|
||||
not bf.getDeclaredNumBits() = bf.getType().getSize() * 8 and
|
||||
not bf.isAnonymous() and
|
||||
|
||||
@@ -85,8 +85,7 @@ private predicate cancelingSubExprs(ComparisonOperation cmp, VariableAccess a1,
|
||||
exists(Variable v |
|
||||
exists(float m | m < 0 and cmpLinearSubVariable(cmp, v, a1, m)) and
|
||||
exists(float m | m > 0 and cmpLinearSubVariable(cmp, v, a2, m))
|
||||
) and
|
||||
not any(ClassTemplateInstantiation inst).getATemplateArgument() = cmp.getParent*()
|
||||
)
|
||||
}
|
||||
|
||||
from ComparisonOperation cmp, VariableAccess a1, VariableAccess a2
|
||||
|
||||
@@ -29,9 +29,7 @@ predicate pointlessSelfComparison(ComparisonOperation cmp) {
|
||||
not exists(lhs.getQualifier()) and // Avoid structure fields
|
||||
not exists(rhs.getQualifier()) and // Avoid structure fields
|
||||
not convertedExprMightOverflow(lhs) and
|
||||
not convertedExprMightOverflow(rhs) and
|
||||
// Don't warn if the comparison is part of a template argument.
|
||||
not any(ClassTemplateInstantiation inst).getATemplateArgument() = cmp.getParent*()
|
||||
not convertedExprMightOverflow(rhs)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The return value of a call to <code>snprintf</code> is the number of characters that <i>would have</i> been written to the buffer assuming there was sufficient space. In the event that the operation reaches the end of the buffer and more than one character is discarded, the return value will be greater than the buffer size. This can cause incorrect behavior, for example:
|
||||
<p>The return value of a call to <code>snprintf</code> is the number of characters that <i>would have</i> been written to the buffer assuming there was sufficient space. In the event that the operation reaches the end of the buffer and more than one character is discarded, the return value will be greater than the buffer size. This can cause incorrect behaviour, for example:
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
|
||||
@@ -21,15 +21,14 @@ import semmle.code.cpp.commons.Alloc
|
||||
* See CWE-120/UnboundedWrite.ql for a summary of CWE-120 alert cases.
|
||||
*/
|
||||
|
||||
from BufferWrite bw, Expr dest, int destSize, int estimated
|
||||
from BufferWrite bw, Expr dest, int destSize
|
||||
where
|
||||
not bw.hasExplicitLimit() and // has no explicit size limit
|
||||
dest = bw.getDest() and
|
||||
destSize = getBufferSize(dest, _) and
|
||||
estimated = bw.getMaxDataLimited(_) and
|
||||
// we can deduce that too much data may be copied (even without
|
||||
// long '%f' conversions)
|
||||
estimated > destSize
|
||||
bw.getMaxDataLimited() > destSize
|
||||
select bw,
|
||||
"This '" + bw.getBWDesc() + "' operation requires " + estimated +
|
||||
"This '" + bw.getBWDesc() + "' operation requires " + bw.getMaxData() +
|
||||
" bytes but the destination is only " + destSize + " bytes."
|
||||
|
||||
@@ -21,15 +21,14 @@ import semmle.code.cpp.security.BufferWrite
|
||||
* See CWE-120/UnboundedWrite.ql for a summary of CWE-120 alert cases.
|
||||
*/
|
||||
|
||||
from BufferWrite bw, int destSize, int estimated, BufferWriteEstimationReason reason
|
||||
from BufferWrite bw, int destSize
|
||||
where
|
||||
not bw.hasExplicitLimit() and
|
||||
// has no explicit size limit
|
||||
destSize = getBufferSize(bw.getDest(), _) and
|
||||
estimated = bw.getMaxData(reason) and
|
||||
estimated > destSize and
|
||||
bw.getMaxData() > destSize and
|
||||
// and we can deduce that too much data may be copied
|
||||
bw.getMaxDataLimited(reason) <= destSize // but it would fit without long '%f' conversions
|
||||
bw.getMaxDataLimited() <= destSize // but it would fit without long '%f' conversions
|
||||
select bw,
|
||||
"This '" + bw.getBWDesc() + "' operation may require " + estimated +
|
||||
"This '" + bw.getBWDesc() + "' operation may require " + bw.getMaxData() +
|
||||
" bytes because of float conversions, but the target is only " + destSize + " bytes."
|
||||
|
||||
@@ -44,7 +44,7 @@ import TaintedWithPath
|
||||
|
||||
predicate isUnboundedWrite(BufferWrite bw) {
|
||||
not bw.hasExplicitLimit() and // has no explicit size limit
|
||||
not exists(bw.getMaxData(_)) // and we can't deduce an upper bound to the amount copied
|
||||
not exists(bw.getMaxData()) // and we can't deduce an upper bound to the amount copied
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 8.6
|
||||
* @precision high
|
||||
* @precision medium
|
||||
* @id cpp/uncontrolled-arithmetic
|
||||
* @tags security
|
||||
* external/cwe/cwe-190
|
||||
@@ -82,11 +82,8 @@ predicate missingGuard(VariableAccess va, string effect) {
|
||||
op.getUnspecifiedType().(IntegralType).isUnsigned() and
|
||||
not op instanceof MulExpr
|
||||
or
|
||||
// overflow - only report signed integer overflow since unsigned overflow
|
||||
// is well-defined.
|
||||
op.getUnspecifiedType().(IntegralType).isSigned() and
|
||||
missingGuardAgainstOverflow(op, va) and
|
||||
effect = "overflow"
|
||||
// overflow
|
||||
missingGuardAgainstOverflow(op, va) and effect = "overflow"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -14,188 +14,105 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.SensitiveExprs
|
||||
import semmle.code.cpp.dataflow.TaintTracking
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A DataFlow node corresponding to a variable or function call that
|
||||
* might contain or return a password or other sensitive information.
|
||||
*/
|
||||
class SensitiveNode extends DataFlow::Node {
|
||||
SensitiveNode() {
|
||||
this.asExpr() = any(SensitiveVariable sv).getInitializer().getExpr() or
|
||||
this.asExpr().(VariableAccess).getTarget() =
|
||||
any(SensitiveVariable sv).(GlobalOrNamespaceVariable) or
|
||||
this.asUninitialized() instanceof SensitiveVariable or
|
||||
this.asParameter() instanceof SensitiveVariable or
|
||||
this.asExpr().(FunctionCall).getTarget() instanceof SensitiveFunction
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that sends or receives data over a network.
|
||||
*/
|
||||
abstract class SendRecv extends Function {
|
||||
/**
|
||||
* Gets the expression for the socket or similar object used for sending or
|
||||
* receiving data through the function call `call` (if any).
|
||||
*/
|
||||
abstract Expr getSocketExpr(Call call);
|
||||
|
||||
/**
|
||||
* Gets the expression for the buffer to be sent from / received into through
|
||||
* the function call `call`.
|
||||
*/
|
||||
abstract Expr getDataExpr(Call call);
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that sends data over a network.
|
||||
*/
|
||||
class Send extends SendRecv instanceof RemoteFlowSinkFunction {
|
||||
override Expr getSocketExpr(Call call) {
|
||||
call.getTarget() = this and
|
||||
exists(FunctionInput input, int arg |
|
||||
super.hasSocketInput(input) and
|
||||
input.isParameter(arg) and
|
||||
result = call.getArgument(arg)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getDataExpr(Call call) {
|
||||
call.getTarget() = this and
|
||||
exists(FunctionInput input, int arg |
|
||||
super.hasRemoteFlowSink(input, _) and
|
||||
input.isParameterDeref(arg) and
|
||||
result = call.getArgument(arg)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that receives data over a network.
|
||||
*/
|
||||
class Recv extends SendRecv instanceof RemoteFlowSourceFunction {
|
||||
override Expr getSocketExpr(Call call) {
|
||||
call.getTarget() = this and
|
||||
exists(FunctionInput input, int arg |
|
||||
super.hasSocketInput(input) and
|
||||
input.isParameter(arg) and
|
||||
result = call.getArgument(arg)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getDataExpr(Call call) {
|
||||
call.getTarget() = this and
|
||||
exists(FunctionOutput output, int arg |
|
||||
super.hasRemoteFlowSource(output, _) and
|
||||
output.isParameterDeref(arg) and
|
||||
result = call.getArgument(arg)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function call that sends or receives data over a network.
|
||||
*
|
||||
* note: function calls such as `write` may be writing to a network source
|
||||
* or a file. We could attempt to determine which, and sort results into
|
||||
* `cpp/cleartext-transmission` and perhaps `cpp/cleartext-storage-file`. In
|
||||
* practice it usually isn't very important which query reports a result as
|
||||
* long as its reported exactly once.
|
||||
*
|
||||
* We do exclude function calls that specify a constant socket, which is
|
||||
* likely to mean standard input, standard output or a similar channel.
|
||||
*/
|
||||
abstract class NetworkSendRecv extends FunctionCall {
|
||||
SendRecv target;
|
||||
/**
|
||||
* Gets the expression for the socket or similar object used for sending or
|
||||
* receiving data (if any).
|
||||
*/
|
||||
abstract Expr getSocketExpr();
|
||||
|
||||
NetworkSendRecv() {
|
||||
this.getTarget() = target and
|
||||
// exclude calls based on the socket...
|
||||
not exists(GVN g |
|
||||
g = globalValueNumber(target.getSocketExpr(this)) and
|
||||
(
|
||||
// literal constant
|
||||
globalValueNumber(any(Literal l)) = g
|
||||
or
|
||||
// variable (such as a global) initialized to a literal constant
|
||||
exists(Variable v |
|
||||
v.getInitializer().getExpr() instanceof Literal and
|
||||
g = globalValueNumber(v.getAnAccess())
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
final Expr getDataExpr() { result = target.getDataExpr(this) }
|
||||
/**
|
||||
* Gets the expression for the buffer to be sent from / received into.
|
||||
*/
|
||||
abstract Expr getDataExpr();
|
||||
}
|
||||
|
||||
/**
|
||||
* A function call that sends data over a network.
|
||||
*
|
||||
* note: functions such as `write` may be writing to a network source or a file. We could attempt to determine which, and sort results into `cpp/cleartext-transmission` and perhaps `cpp/cleartext-storage-file`. In practice it usually isn't very important which query reports a result as long as its reported exactly once.
|
||||
*/
|
||||
class NetworkSend extends NetworkSendRecv {
|
||||
override Send target;
|
||||
RemoteFlowSinkFunction target;
|
||||
|
||||
NetworkSend() { target = this.getTarget() }
|
||||
|
||||
override Expr getSocketExpr() {
|
||||
exists(FunctionInput input, int arg |
|
||||
target.hasSocketInput(input) and
|
||||
input.isParameter(arg) and
|
||||
result = this.getArgument(arg)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getDataExpr() {
|
||||
exists(FunctionInput input, int arg |
|
||||
target.hasRemoteFlowSink(input, _) and
|
||||
input.isParameterDeref(arg) and
|
||||
result = this.getArgument(arg)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function call that receives data over a network.
|
||||
*/
|
||||
class NetworkRecv extends NetworkSendRecv {
|
||||
override Recv target;
|
||||
}
|
||||
RemoteFlowSourceFunction target;
|
||||
|
||||
/**
|
||||
* An expression that is an argument or return value from an encryption or
|
||||
* decryption call.
|
||||
*/
|
||||
class Encrypted extends Expr {
|
||||
Encrypted() {
|
||||
exists(FunctionCall fc |
|
||||
fc.getTarget().getName().toLowerCase().regexpMatch(".*(crypt|encode|decode).*") and
|
||||
(
|
||||
this = fc or
|
||||
this = fc.getAnArgument()
|
||||
)
|
||||
NetworkRecv() { target = this.getTarget() }
|
||||
|
||||
override Expr getSocketExpr() {
|
||||
exists(FunctionInput input, int arg |
|
||||
target.hasSocketInput(input) and
|
||||
input.isParameter(arg) and
|
||||
result = this.getArgument(arg)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getDataExpr() {
|
||||
exists(FunctionOutput output, int arg |
|
||||
target.hasRemoteFlowSource(output, _) and
|
||||
output.isParameterDeref(arg) and
|
||||
result = this.getArgument(arg)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint flow from a sensitive expression.
|
||||
* Taint flow from a sensitive expression to a network operation with data
|
||||
* tainted by that expression.
|
||||
*/
|
||||
class FromSensitiveConfiguration extends TaintTracking::Configuration {
|
||||
FromSensitiveConfiguration() { this = "FromSensitiveConfiguration" }
|
||||
class SensitiveSendRecvConfiguration extends TaintTracking::Configuration {
|
||||
SensitiveSendRecvConfiguration() { this = "SensitiveSendRecvConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof SensitiveNode }
|
||||
override predicate isSource(DataFlow::Node source) { source.asExpr() instanceof SensitiveExpr }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(NetworkSendRecv nsr).getDataExpr()
|
||||
or
|
||||
sink.asExpr() instanceof Encrypted
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// flow through encryption functions to the return value (in case we can reach other sinks)
|
||||
node2.asExpr().(Encrypted).(FunctionCall).getAnArgument() = node1.asExpr()
|
||||
exists(NetworkSendRecv transmission |
|
||||
sink.asExpr() = transmission.getDataExpr() and
|
||||
// a zero socket descriptor is standard input, which is not interesting for this query.
|
||||
not exists(Zero zero |
|
||||
DataFlow::localFlow(DataFlow::exprNode(zero),
|
||||
DataFlow::exprNode(transmission.getSocketExpr()))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from
|
||||
FromSensitiveConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
NetworkSendRecv networkSendRecv, string msg
|
||||
SensitiveSendRecvConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
NetworkSendRecv transmission, string msg
|
||||
where
|
||||
// flow from sensitive -> network data
|
||||
config.hasFlowPath(source, sink) and
|
||||
sink.getNode().asExpr() = networkSendRecv.getDataExpr() and
|
||||
// no flow from sensitive -> evidence of encryption
|
||||
not exists(DataFlow::Node encrypted |
|
||||
config.hasFlow(source.getNode(), encrypted) and
|
||||
encrypted.asExpr() instanceof Encrypted
|
||||
) and
|
||||
// construct result
|
||||
if networkSendRecv instanceof NetworkSend
|
||||
sink.getNode().asExpr() = transmission.getDataExpr() and
|
||||
if transmission instanceof NetworkSend
|
||||
then
|
||||
msg =
|
||||
"This operation transmits '" + sink.toString() +
|
||||
@@ -204,4 +121,4 @@ where
|
||||
msg =
|
||||
"This operation receives into '" + sink.toString() +
|
||||
"', which may put unencrypted sensitive data into $@"
|
||||
select networkSendRecv, source, sink, msg, source, source.getNode().toString()
|
||||
select transmission, source, sink, msg, source, source.getNode().asExpr().toString()
|
||||
|
||||
@@ -28,11 +28,6 @@ class PrivateHostName extends string {
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate privateHostNameFlowsToExpr(Expr e) {
|
||||
TaintTracking::localExprTaint(any(StringLiteral p | p.getValue() instanceof PrivateHostName), e)
|
||||
}
|
||||
|
||||
/**
|
||||
* A string containing an HTTP URL not in a private domain.
|
||||
*/
|
||||
@@ -43,9 +38,11 @@ class HttpStringLiteral extends StringLiteral {
|
||||
or
|
||||
exists(string tail |
|
||||
tail = s.regexpCapture("http://(.*)", 1) and not tail instanceof PrivateHostName
|
||||
)
|
||||
) and
|
||||
not privateHostNameFlowsToExpr(this.getParent*())
|
||||
) and
|
||||
not TaintTracking::localExprTaint(any(StringLiteral p |
|
||||
p.getValue() instanceof PrivateHostName
|
||||
), this.getParent*())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,83 +89,45 @@ predicate referenceTo(Expr source, Expr use) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate statCallWithPointer(Expr checkPath, Expr call, Expr e, Variable v) {
|
||||
call = stat(checkPath, e) and
|
||||
e.getAChild*().(VariableAccess).getTarget() = v
|
||||
}
|
||||
|
||||
predicate checksPath(Expr check, Expr checkPath) {
|
||||
// either:
|
||||
// an access check
|
||||
check = accessCheck(checkPath)
|
||||
or
|
||||
// a stat
|
||||
check = stat(checkPath, _)
|
||||
or
|
||||
// access to a member variable on the stat buf
|
||||
// (morally, this should be a use-use pair, but it seems unlikely
|
||||
// that this variable will get reused in practice)
|
||||
exists(Expr call, Expr e, Variable v |
|
||||
statCallWithPointer(checkPath, call, e, v) and
|
||||
check.(VariableAccess).getTarget() = v and
|
||||
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate checkPathControlsUse(Expr check, Expr checkPath, Expr use) {
|
||||
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
|
||||
guard.controls(use.getBasicBlock(), _)
|
||||
) and
|
||||
checksPath(pragma[only_bind_into](check), checkPath)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate fileNameOperationControlsUse(Expr check, Expr checkPath, Expr use) {
|
||||
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
|
||||
guard.controls(use.getBasicBlock(), _)
|
||||
) and
|
||||
pragma[only_bind_into](check) = filenameOperation(checkPath)
|
||||
}
|
||||
|
||||
predicate checkUse(Expr check, Expr checkPath, FunctionCall use, Expr usePath) {
|
||||
// `check` is part of a guard that controls `use`
|
||||
checkPathControlsUse(check, checkPath, use) and
|
||||
// `check` looks like a check on a filename
|
||||
checksPath(check, checkPath) and
|
||||
// `op` looks like an operation on a filename
|
||||
use = filenameOperation(usePath)
|
||||
or
|
||||
// `check` is part of a guard that controls `use`
|
||||
fileNameOperationControlsUse(check, checkPath, use) and
|
||||
// another filename operation (null pointers can indicate errors)
|
||||
check = filenameOperation(checkPath) and
|
||||
// `op` looks like a sensitive operation on a filename
|
||||
use = sensitiveFilenameOperation(usePath)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate isCheckedPath(
|
||||
Expr check, SsaDefinition def, StackVariable v, FunctionCall use, Expr usePath, Expr checkPath
|
||||
) {
|
||||
checkUse(check, checkPath, use, usePath) and
|
||||
def.getAUse(v) = checkPath
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate isUsedPath(
|
||||
Expr check, SsaDefinition def, StackVariable v, FunctionCall use, Expr usePath, Expr checkPath
|
||||
) {
|
||||
checkUse(check, checkPath, use, usePath) and
|
||||
def.getAUse(v) = usePath
|
||||
}
|
||||
|
||||
from Expr check, Expr checkPath, FunctionCall use, Expr usePath, SsaDefinition def, StackVariable v
|
||||
from Expr check, Expr checkPath, FunctionCall use, Expr usePath
|
||||
where
|
||||
// `check` looks like a check on a filename
|
||||
(
|
||||
(
|
||||
// either:
|
||||
// an access check
|
||||
check = accessCheck(checkPath)
|
||||
or
|
||||
// a stat
|
||||
check = stat(checkPath, _)
|
||||
or
|
||||
// access to a member variable on the stat buf
|
||||
// (morally, this should be a use-use pair, but it seems unlikely
|
||||
// that this variable will get reused in practice)
|
||||
exists(Expr call, Expr e, Variable v |
|
||||
call = stat(checkPath, e) and
|
||||
e.getAChild*().(VariableAccess).getTarget() = v and
|
||||
check.(VariableAccess).getTarget() = v and
|
||||
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
|
||||
)
|
||||
) and
|
||||
// `op` looks like an operation on a filename
|
||||
use = filenameOperation(usePath)
|
||||
or
|
||||
// another filename operation (null pointers can indicate errors)
|
||||
check = filenameOperation(checkPath) and
|
||||
// `op` looks like a sensitive operation on a filename
|
||||
use = sensitiveFilenameOperation(usePath)
|
||||
) and
|
||||
// `checkPath` and `usePath` refer to the same SSA variable
|
||||
isCheckedPath(check, def, v, use, usePath, checkPath) and
|
||||
isUsedPath(check, def, v, use, usePath, checkPath)
|
||||
exists(SsaDefinition def, StackVariable v |
|
||||
def.getAUse(v) = checkPath and def.getAUse(v) = usePath
|
||||
) and
|
||||
// the return value of `check` is used (possibly with one step of
|
||||
// variable indirection) in a guard which controls `use`
|
||||
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
|
||||
guard.controls(use.(ControlFlowNode).getBasicBlock(), _)
|
||||
)
|
||||
select use,
|
||||
"The $@ being operated upon was previously $@, but the underlying file may have been changed since then.",
|
||||
usePath, "filename", check, "checked"
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added exception for GLib's gboolean to cpp/ambiguously-signed-bit-field.
|
||||
This change reduces the number of false positives in the query.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* The "Uncontrolled data in arithmetic expression" (cpp/uncontrolled-arithmetic) query has been enhanced to reduce false positive results and its @precision increased to high.
|
||||
@@ -1,16 +0,0 @@
|
||||
...
|
||||
umask(0); // BAD
|
||||
...
|
||||
maskOut = S_IRWXG | S_IRWXO;
|
||||
umask(maskOut); // GOOD
|
||||
...
|
||||
fchmod(fileno(fp), 0555 - maskOut); // BAD
|
||||
...
|
||||
fchmod(fileno(fp), 0555 & ~maskOut); // GOOD
|
||||
...
|
||||
umask(0666);
|
||||
chmod(pathname, 0666); // BAD
|
||||
...
|
||||
umask(0022);
|
||||
chmod(pathname, 0666); // GOOD
|
||||
...
|
||||
@@ -1,23 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Finding for function calls that set file permissions that may have errors in use. Incorrect arithmetic for calculating the resolution mask, using the same mask in opposite functions, using a mask that is too wide.</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<example>
|
||||
<p>The following example demonstrates erroneous and fixed ways to use functions.</p>
|
||||
<sample src="IncorrectPrivilegeAssignment.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/FIO06-C.+Create+files+with+appropriate+access+permissions">FIO06-C. Create files with appropriate access permissions</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,87 +0,0 @@
|
||||
/**
|
||||
* @name Find the wrong use of the umask function.
|
||||
* @description Incorrectly evaluated argument to the umask function may have security implications.
|
||||
* @kind problem
|
||||
* @id cpp/wrong-use-of-the-umask
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* maintainability
|
||||
* security
|
||||
* external/cwe/cwe-266
|
||||
* external/cwe/cwe-264
|
||||
* external/cwe/cwe-200
|
||||
* external/cwe/cwe-560
|
||||
* external/cwe/cwe-687
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.exprs.BitwiseOperation
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* An expression that is either a `BinaryArithmeticOperation` or the result of one or more `BinaryBitwiseOperation`s on a `BinaryArithmeticOperation`. For example `1 | (2 + 3)`.
|
||||
*/
|
||||
class ContainsArithmetic extends Expr {
|
||||
ContainsArithmetic() {
|
||||
this instanceof BinaryArithmeticOperation
|
||||
or
|
||||
// recursive search into `Operation`s
|
||||
this.(BinaryBitwiseOperation).getAnOperand() instanceof ContainsArithmetic
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds for a function `f` that has an argument at index `apos` used to set file permissions. */
|
||||
predicate numberArgumentModFunctions(Function f, int apos) {
|
||||
f.hasGlobalOrStdName("umask") and apos = 0
|
||||
or
|
||||
f.hasGlobalOrStdName("fchmod") and apos = 1
|
||||
or
|
||||
f.hasGlobalOrStdName("chmod") and apos = 1
|
||||
}
|
||||
|
||||
from FunctionCall fc, string msg, FunctionCall fcsnd
|
||||
where
|
||||
fc.getTarget().hasGlobalOrStdName("umask") and
|
||||
fc.getArgument(0).getValue() = "0" and
|
||||
not exists(FunctionCall fctmp |
|
||||
fctmp.getTarget().hasGlobalOrStdName("umask") and
|
||||
not fctmp.getArgument(0).getValue() = "0"
|
||||
) and
|
||||
exists(FunctionCall fctmp |
|
||||
(
|
||||
fctmp.getTarget().hasGlobalOrStdName("fopen") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("open")
|
||||
) and
|
||||
not fctmp.getArgument(1).getValue().matches("r%") and
|
||||
fctmp.getNumberOfArguments() = 2 and
|
||||
not fctmp.getArgument(0).getValue() = "/dev/null" and
|
||||
fcsnd = fctmp
|
||||
) and
|
||||
not exists(FunctionCall fctmp |
|
||||
fctmp.getTarget().hasGlobalOrStdName("chmod") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("fchmod")
|
||||
) and
|
||||
msg = "Using umask(0) may not be safe with call $@."
|
||||
or
|
||||
fc.getTarget().hasGlobalOrStdName("umask") and
|
||||
exists(FunctionCall fctmp |
|
||||
(
|
||||
fctmp.getTarget().hasGlobalOrStdName("chmod") or
|
||||
fctmp.getTarget().hasGlobalOrStdName("fchmod")
|
||||
) and
|
||||
(
|
||||
globalValueNumber(fc.getArgument(0)) = globalValueNumber(fctmp.getArgument(1)) and
|
||||
fc.getArgument(0).getValue() != "0"
|
||||
) and
|
||||
msg = "Not use equal argument in umask and $@ functions." and
|
||||
fcsnd = fctmp
|
||||
)
|
||||
or
|
||||
exists(ContainsArithmetic exptmp, int i |
|
||||
numberArgumentModFunctions(fc.getTarget(), i) and
|
||||
globalValueNumber(exptmp) = globalValueNumber(fc.getArgument(i)) and
|
||||
msg = "Using arithmetic to compute the mask in $@ may not be safe." and
|
||||
fcsnd = fc
|
||||
)
|
||||
select fc, msg, fcsnd, fcsnd.getTarget().getName()
|
||||
@@ -1,2 +0,0 @@
|
||||
| test.cpp:9:3:9:7 | call to umask | Not use equal argument in umask and $@ functions. | test.cpp:13:3:13:7 | call to chmod | chmod |
|
||||
| test.cpp:30:3:30:7 | call to chmod | Using arithmetic to compute the mask in $@ may not be safe. | test.cpp:30:3:30:7 | call to chmod | chmod |
|
||||
@@ -1 +0,0 @@
|
||||
experimental/Security/CWE/CWE-266/IncorrectPrivilegeAssignment.ql
|
||||
@@ -1,49 +0,0 @@
|
||||
typedef int FILE;
|
||||
FILE *fopen(const char *filename, const char *mode);
|
||||
int umask(int pmode);
|
||||
int chmod(char * filename,int pmode);
|
||||
int fclose(FILE *stream);
|
||||
|
||||
void funcTest1()
|
||||
{
|
||||
umask(0666); // BAD
|
||||
FILE *fe;
|
||||
fe = fopen("myFile.txt", "wt");
|
||||
fclose(fe);
|
||||
chmod("myFile.txt",0666);
|
||||
}
|
||||
void funcTest1g()
|
||||
{
|
||||
umask(0022);
|
||||
FILE *fe;
|
||||
fe = fopen("myFile.txt", "wt");
|
||||
fclose(fe);
|
||||
chmod("myFile.txt",0666); // GOOD
|
||||
}
|
||||
|
||||
void funcTest2(int mode)
|
||||
{
|
||||
umask(mode);
|
||||
FILE *fe;
|
||||
fe = fopen("myFile.txt", "wt");
|
||||
fclose(fe);
|
||||
chmod("myFile.txt",0555-mode); // BAD
|
||||
}
|
||||
|
||||
void funcTest2g(int mode)
|
||||
{
|
||||
umask(mode);
|
||||
FILE *fe;
|
||||
fe = fopen("myFile.txt", "wt");
|
||||
fclose(fe);
|
||||
chmod("myFile.txt",0555&~mode); // GOOD
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
funcTest1();
|
||||
funcTest2(27);
|
||||
funcTest1g();
|
||||
funcTest2g(27);
|
||||
return 0;
|
||||
}
|
||||
@@ -20,22 +20,3 @@ bool compareValues() {
|
||||
bool callCompareValues() {
|
||||
return compareValues<C1, C2> || compareValues<C1, C1>();
|
||||
}
|
||||
|
||||
template <bool C, typename T = void>
|
||||
struct enable_if {};
|
||||
|
||||
template <typename T>
|
||||
struct enable_if<true, T> { typedef T type; };
|
||||
|
||||
template<typename T1, typename T2>
|
||||
typename enable_if<T1::value <= T2::value, bool>::type constant_comparison() {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct Value0 {
|
||||
const static int value = 0;
|
||||
};
|
||||
|
||||
void instantiation_with_pointless_comparison() {
|
||||
constant_comparison<Value0, Value0>(); // GOOD
|
||||
}
|
||||
@@ -35,6 +35,8 @@ edges
|
||||
| test.cpp:190:10:190:13 | call to rand | test.cpp:205:7:205:7 | y |
|
||||
| test.cpp:190:10:190:13 | call to rand | test.cpp:208:7:208:7 | y |
|
||||
| test.cpp:215:11:215:14 | call to rand | test.cpp:219:8:219:8 | x |
|
||||
| test.cpp:223:20:223:23 | call to rand | test.cpp:227:8:227:8 | x |
|
||||
| test.cpp:223:20:223:25 | (unsigned int)... | test.cpp:227:8:227:8 | x |
|
||||
nodes
|
||||
| test.c:18:13:18:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:21:17:21:17 | r | semmle.label | r |
|
||||
@@ -90,6 +92,9 @@ nodes
|
||||
| test.cpp:208:7:208:7 | y | semmle.label | y |
|
||||
| test.cpp:215:11:215:14 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:219:8:219:8 | x | semmle.label | x |
|
||||
| test.cpp:223:20:223:23 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:223:20:223:25 | (unsigned int)... | semmle.label | (unsigned int)... |
|
||||
| test.cpp:227:8:227:8 | x | semmle.label | x |
|
||||
subpaths
|
||||
#select
|
||||
| test.c:21:17:21:17 | r | test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value |
|
||||
@@ -120,3 +125,5 @@ subpaths
|
||||
| test.cpp:205:7:205:7 | y | test.cpp:190:10:190:13 | call to rand | test.cpp:205:7:205:7 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:190:10:190:13 | call to rand | Uncontrolled value |
|
||||
| test.cpp:208:7:208:7 | y | test.cpp:190:10:190:13 | call to rand | test.cpp:208:7:208:7 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:190:10:190:13 | call to rand | Uncontrolled value |
|
||||
| test.cpp:219:8:219:8 | x | test.cpp:215:11:215:14 | call to rand | test.cpp:219:8:219:8 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:215:11:215:14 | call to rand | Uncontrolled value |
|
||||
| test.cpp:227:8:227:8 | x | test.cpp:223:20:223:23 | call to rand | test.cpp:227:8:227:8 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:223:20:223:23 | call to rand | Uncontrolled value |
|
||||
| test.cpp:227:8:227:8 | x | test.cpp:223:20:223:25 | (unsigned int)... | test.cpp:227:8:227:8 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:223:20:223:23 | call to rand | Uncontrolled value |
|
||||
|
||||
@@ -157,11 +157,3 @@ void moreTests() {
|
||||
r = r - 100; // BAD
|
||||
}
|
||||
}
|
||||
|
||||
void guarded_test(unsigned p) {
|
||||
unsigned data = (unsigned int)rand();
|
||||
if (p >= data) {
|
||||
return;
|
||||
}
|
||||
unsigned z = data - p; // GOOD
|
||||
}
|
||||
@@ -224,6 +224,6 @@ void test_mod_limit()
|
||||
unsigned int y = 100;
|
||||
unsigned int z;
|
||||
|
||||
z = (x + y) % 1000; // DUBIOUS (this could overflow but the result is controlled)
|
||||
z = (x + y) % 1000; // DUBIOUS (this could overflow but the result is controlled) [REPORTED]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,228 +1,49 @@
|
||||
edges
|
||||
| test3.cpp:17:28:17:36 | password1 | test3.cpp:22:15:22:23 | password1 |
|
||||
| test3.cpp:17:51:17:59 | password2 | test3.cpp:26:15:26:23 | password2 |
|
||||
| test3.cpp:45:8:45:15 | password | test3.cpp:47:15:47:22 | password |
|
||||
| test3.cpp:53:8:53:15 | password | test3.cpp:55:15:55:22 | password |
|
||||
| test3.cpp:71:32:71:40 | password1 | test3.cpp:76:15:76:17 | ptr |
|
||||
| test3.cpp:80:8:80:15 | password | test3.cpp:83:15:83:17 | ptr |
|
||||
| test3.cpp:98:8:98:15 | password | test3.cpp:101:12:101:19 | password |
|
||||
| test3.cpp:112:20:112:25 | buffer | test3.cpp:114:14:114:19 | buffer |
|
||||
| test3.cpp:117:28:117:33 | buffer | test3.cpp:119:9:119:14 | buffer |
|
||||
| test3.cpp:126:9:126:23 | global_password | test3.cpp:144:16:144:29 | call to get_global_str |
|
||||
| test3.cpp:129:39:129:47 | password1 | test3.cpp:138:24:138:32 | password1 |
|
||||
| test3.cpp:132:8:132:15 | password | test3.cpp:134:11:134:18 | password |
|
||||
| test3.cpp:134:11:134:18 | password | test3.cpp:112:20:112:25 | buffer |
|
||||
| test3.cpp:138:21:138:22 | call to id | test3.cpp:140:15:140:17 | ptr |
|
||||
| test3.cpp:138:24:138:32 | password1 | test3.cpp:117:28:117:33 | buffer |
|
||||
| test3.cpp:138:24:138:32 | password1 | test3.cpp:138:21:138:22 | call to id |
|
||||
| test3.cpp:144:16:144:29 | call to get_global_str | test3.cpp:146:15:146:18 | data |
|
||||
| test3.cpp:152:29:152:36 | password | test3.cpp:159:15:159:20 | buffer |
|
||||
| test3.cpp:171:8:171:15 | password | test3.cpp:173:15:173:22 | password |
|
||||
| test3.cpp:171:8:171:15 | password | test3.cpp:175:3:175:17 | call to decrypt_inplace |
|
||||
| test3.cpp:171:8:171:15 | password | test3.cpp:175:19:175:26 | password |
|
||||
| test3.cpp:179:8:179:15 | password | test3.cpp:181:15:181:22 | password |
|
||||
| test3.cpp:179:8:179:15 | password | test3.cpp:184:3:184:17 | call to decrypt_inplace |
|
||||
| test3.cpp:179:8:179:15 | password | test3.cpp:184:19:184:26 | password |
|
||||
| test3.cpp:188:8:188:15 | password | test3.cpp:191:15:191:22 | password |
|
||||
| test3.cpp:188:8:188:15 | password | test3.cpp:193:18:193:28 | call to rtn_decrypt |
|
||||
| test3.cpp:188:8:188:15 | password | test3.cpp:193:30:193:37 | password |
|
||||
| test3.cpp:197:8:197:15 | password | test3.cpp:199:3:199:17 | call to encrypt_inplace |
|
||||
| test3.cpp:197:8:197:15 | password | test3.cpp:199:19:199:26 | password |
|
||||
| test3.cpp:197:8:197:15 | password | test3.cpp:201:15:201:22 | password |
|
||||
| test3.cpp:205:8:205:15 | password | test3.cpp:207:3:207:17 | call to encrypt_inplace |
|
||||
| test3.cpp:205:8:205:15 | password | test3.cpp:207:19:207:26 | password |
|
||||
| test3.cpp:205:8:205:15 | password | test3.cpp:210:15:210:22 | password |
|
||||
| test3.cpp:214:8:214:15 | password | test3.cpp:217:18:217:28 | call to rtn_encrypt |
|
||||
| test3.cpp:214:8:214:15 | password | test3.cpp:217:18:217:28 | call to rtn_encrypt |
|
||||
| test3.cpp:214:8:214:15 | password | test3.cpp:217:30:217:37 | password |
|
||||
| test3.cpp:214:8:214:15 | password | test3.cpp:219:15:219:26 | password_ptr |
|
||||
| test3.cpp:217:18:217:28 | call to rtn_encrypt | test3.cpp:219:15:219:26 | password_ptr |
|
||||
| test3.cpp:225:34:225:41 | password | test3.cpp:227:22:227:29 | password |
|
||||
| test3.cpp:225:34:225:41 | password | test3.cpp:228:26:228:33 | password |
|
||||
| test3.cpp:239:7:239:14 | password | test3.cpp:241:8:241:15 | password |
|
||||
| test3.cpp:239:7:239:14 | password | test3.cpp:242:8:242:15 | password |
|
||||
| test3.cpp:252:8:252:16 | password1 | test3.cpp:254:15:254:23 | password1 |
|
||||
| test3.cpp:252:8:252:16 | password1 | test3.cpp:256:3:256:19 | call to decrypt_to_buffer |
|
||||
| test3.cpp:252:8:252:16 | password1 | test3.cpp:256:21:256:29 | password1 |
|
||||
| test3.cpp:252:24:252:32 | password2 | test3.cpp:256:3:256:19 | call to decrypt_to_buffer |
|
||||
| test3.cpp:252:24:252:32 | password2 | test3.cpp:256:32:256:40 | password2 |
|
||||
| test3.cpp:260:8:260:16 | password1 | test3.cpp:262:3:262:19 | call to encrypt_to_buffer |
|
||||
| test3.cpp:260:8:260:16 | password1 | test3.cpp:262:21:262:29 | password1 |
|
||||
| test3.cpp:260:24:260:32 | password2 | test3.cpp:262:3:262:19 | call to encrypt_to_buffer |
|
||||
| test3.cpp:260:24:260:32 | password2 | test3.cpp:262:32:262:40 | password2 |
|
||||
| test3.cpp:260:24:260:32 | password2 | test3.cpp:264:15:264:23 | password2 |
|
||||
| test3.cpp:268:19:268:26 | password | test3.cpp:272:15:272:18 | data |
|
||||
| test3.cpp:278:20:278:23 | data | test3.cpp:278:20:278:23 | data |
|
||||
| test3.cpp:278:20:278:23 | data | test3.cpp:280:14:280:17 | data |
|
||||
| test3.cpp:283:20:283:23 | data | test3.cpp:283:20:283:23 | data |
|
||||
| test3.cpp:283:20:283:23 | data | test3.cpp:285:14:285:17 | data |
|
||||
| test3.cpp:288:20:288:23 | data | test3.cpp:290:14:290:17 | data |
|
||||
| test3.cpp:293:20:293:23 | data | test3.cpp:293:20:293:23 | data |
|
||||
| test3.cpp:293:20:293:23 | data | test3.cpp:295:14:295:17 | data |
|
||||
| test3.cpp:298:20:298:23 | data | test3.cpp:300:14:300:17 | data |
|
||||
| test3.cpp:308:41:308:49 | password1 | test3.cpp:312:3:312:17 | call to encrypt_inplace |
|
||||
| test3.cpp:308:41:308:49 | password1 | test3.cpp:312:19:312:27 | password1 |
|
||||
| test3.cpp:308:41:308:49 | password1 | test3.cpp:313:11:313:19 | password1 |
|
||||
| test3.cpp:308:41:308:49 | password1 | test3.cpp:314:11:314:19 | password1 |
|
||||
| test3.cpp:308:41:308:49 | password1 | test3.cpp:316:11:316:19 | password1 |
|
||||
| test3.cpp:308:41:308:49 | password1 | test3.cpp:317:11:317:19 | password1 |
|
||||
| test3.cpp:308:58:308:66 | password2 | test3.cpp:324:11:324:14 | data |
|
||||
| test3.cpp:308:58:308:66 | password2 | test3.cpp:325:11:325:14 | data |
|
||||
| test3.cpp:313:11:313:19 | password1 | test3.cpp:278:20:278:23 | data |
|
||||
| test3.cpp:313:11:313:19 | password1 | test3.cpp:313:11:313:19 | ref arg password1 |
|
||||
| test3.cpp:313:11:313:19 | ref arg password1 | test3.cpp:314:11:314:19 | password1 |
|
||||
| test3.cpp:314:11:314:19 | password1 | test3.cpp:283:20:283:23 | data |
|
||||
| test3.cpp:316:11:316:19 | password1 | test3.cpp:283:20:283:23 | data |
|
||||
| test3.cpp:316:11:316:19 | password1 | test3.cpp:316:11:316:19 | ref arg password1 |
|
||||
| test3.cpp:316:11:316:19 | ref arg password1 | test3.cpp:317:11:317:19 | password1 |
|
||||
| test3.cpp:317:11:317:19 | password1 | test3.cpp:288:20:288:23 | data |
|
||||
| test3.cpp:324:11:324:14 | data | test3.cpp:293:20:293:23 | data |
|
||||
| test3.cpp:324:11:324:14 | data | test3.cpp:324:11:324:14 | ref arg data |
|
||||
| test3.cpp:324:11:324:14 | ref arg data | test3.cpp:325:11:325:14 | data |
|
||||
| test3.cpp:325:11:325:14 | data | test3.cpp:298:20:298:23 | data |
|
||||
| test3.cpp:339:9:339:16 | password | test3.cpp:341:16:341:23 | password |
|
||||
| test3.cpp:350:9:350:16 | password | test3.cpp:352:16:352:23 | password |
|
||||
| test3.cpp:350:9:350:16 | password | test3.cpp:353:4:353:18 | call to decrypt_inplace |
|
||||
| test3.cpp:350:9:350:16 | password | test3.cpp:353:20:353:27 | password |
|
||||
| test.cpp:41:23:41:43 | cleartext password! | test.cpp:48:21:48:27 | call to encrypt |
|
||||
| test.cpp:41:23:41:43 | cleartext password! | test.cpp:48:29:48:39 | thePassword |
|
||||
| test.cpp:66:23:66:43 | cleartext password! | test.cpp:76:21:76:27 | call to encrypt |
|
||||
| test.cpp:66:23:66:43 | cleartext password! | test.cpp:76:29:76:39 | thePassword |
|
||||
| test3.cpp:68:21:68:29 | password1 | test3.cpp:70:15:70:17 | ptr |
|
||||
| test3.cpp:75:15:75:22 | password | test3.cpp:77:15:77:17 | ptr |
|
||||
| test3.cpp:106:20:106:25 | buffer | test3.cpp:108:14:108:19 | buffer |
|
||||
| test3.cpp:111:28:111:33 | buffer | test3.cpp:113:9:113:14 | buffer |
|
||||
| test3.cpp:120:9:120:23 | global_password | test3.cpp:138:16:138:29 | call to get_global_str |
|
||||
| test3.cpp:128:11:128:18 | password | test3.cpp:106:20:106:25 | buffer |
|
||||
| test3.cpp:132:21:132:22 | call to id | test3.cpp:134:15:134:17 | ptr |
|
||||
| test3.cpp:132:24:132:32 | password1 | test3.cpp:111:28:111:33 | buffer |
|
||||
| test3.cpp:132:24:132:32 | password1 | test3.cpp:132:21:132:22 | call to id |
|
||||
| test3.cpp:138:16:138:29 | call to get_global_str | test3.cpp:140:15:140:18 | data |
|
||||
| test3.cpp:151:19:151:26 | password | test3.cpp:153:15:153:20 | buffer |
|
||||
nodes
|
||||
| test3.cpp:17:28:17:36 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:17:51:17:59 | password2 | semmle.label | password2 |
|
||||
| test3.cpp:22:15:22:23 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:26:15:26:23 | password2 | semmle.label | password2 |
|
||||
| test3.cpp:45:8:45:15 | password | semmle.label | password |
|
||||
| test3.cpp:47:15:47:22 | password | semmle.label | password |
|
||||
| test3.cpp:53:8:53:15 | password | semmle.label | password |
|
||||
| test3.cpp:55:15:55:22 | password | semmle.label | password |
|
||||
| test3.cpp:71:32:71:40 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:76:15:76:17 | ptr | semmle.label | ptr |
|
||||
| test3.cpp:80:8:80:15 | password | semmle.label | password |
|
||||
| test3.cpp:83:15:83:17 | ptr | semmle.label | ptr |
|
||||
| test3.cpp:98:8:98:15 | password | semmle.label | password |
|
||||
| test3.cpp:101:12:101:19 | password | semmle.label | password |
|
||||
| test3.cpp:112:20:112:25 | buffer | semmle.label | buffer |
|
||||
| test3.cpp:114:14:114:19 | buffer | semmle.label | buffer |
|
||||
| test3.cpp:117:28:117:33 | buffer | semmle.label | buffer |
|
||||
| test3.cpp:119:9:119:14 | buffer | semmle.label | buffer |
|
||||
| test3.cpp:126:9:126:23 | global_password | semmle.label | global_password |
|
||||
| test3.cpp:129:39:129:47 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:132:8:132:15 | password | semmle.label | password |
|
||||
| test3.cpp:134:11:134:18 | password | semmle.label | password |
|
||||
| test3.cpp:138:21:138:22 | call to id | semmle.label | call to id |
|
||||
| test3.cpp:138:24:138:32 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:140:15:140:17 | ptr | semmle.label | ptr |
|
||||
| test3.cpp:144:16:144:29 | call to get_global_str | semmle.label | call to get_global_str |
|
||||
| test3.cpp:146:15:146:18 | data | semmle.label | data |
|
||||
| test3.cpp:152:29:152:36 | password | semmle.label | password |
|
||||
| test3.cpp:159:15:159:20 | buffer | semmle.label | buffer |
|
||||
| test3.cpp:171:8:171:15 | password | semmle.label | password |
|
||||
| test3.cpp:173:15:173:22 | password | semmle.label | password |
|
||||
| test3.cpp:175:3:175:17 | call to decrypt_inplace | semmle.label | call to decrypt_inplace |
|
||||
| test3.cpp:175:19:175:26 | password | semmle.label | password |
|
||||
| test3.cpp:179:8:179:15 | password | semmle.label | password |
|
||||
| test3.cpp:181:15:181:22 | password | semmle.label | password |
|
||||
| test3.cpp:184:3:184:17 | call to decrypt_inplace | semmle.label | call to decrypt_inplace |
|
||||
| test3.cpp:184:19:184:26 | password | semmle.label | password |
|
||||
| test3.cpp:188:8:188:15 | password | semmle.label | password |
|
||||
| test3.cpp:191:15:191:22 | password | semmle.label | password |
|
||||
| test3.cpp:193:18:193:28 | call to rtn_decrypt | semmle.label | call to rtn_decrypt |
|
||||
| test3.cpp:193:30:193:37 | password | semmle.label | password |
|
||||
| test3.cpp:197:8:197:15 | password | semmle.label | password |
|
||||
| test3.cpp:199:3:199:17 | call to encrypt_inplace | semmle.label | call to encrypt_inplace |
|
||||
| test3.cpp:199:19:199:26 | password | semmle.label | password |
|
||||
| test3.cpp:201:15:201:22 | password | semmle.label | password |
|
||||
| test3.cpp:205:8:205:15 | password | semmle.label | password |
|
||||
| test3.cpp:207:3:207:17 | call to encrypt_inplace | semmle.label | call to encrypt_inplace |
|
||||
| test3.cpp:207:19:207:26 | password | semmle.label | password |
|
||||
| test3.cpp:210:15:210:22 | password | semmle.label | password |
|
||||
| test3.cpp:214:8:214:15 | password | semmle.label | password |
|
||||
| test3.cpp:217:18:217:28 | call to rtn_encrypt | semmle.label | call to rtn_encrypt |
|
||||
| test3.cpp:217:18:217:28 | call to rtn_encrypt | semmle.label | call to rtn_encrypt |
|
||||
| test3.cpp:217:30:217:37 | password | semmle.label | password |
|
||||
| test3.cpp:219:15:219:26 | password_ptr | semmle.label | password_ptr |
|
||||
| test3.cpp:225:34:225:41 | password | semmle.label | password |
|
||||
| test3.cpp:227:22:227:29 | password | semmle.label | password |
|
||||
| test3.cpp:228:26:228:33 | password | semmle.label | password |
|
||||
| test3.cpp:239:7:239:14 | password | semmle.label | password |
|
||||
| test3.cpp:241:8:241:15 | password | semmle.label | password |
|
||||
| test3.cpp:242:8:242:15 | password | semmle.label | password |
|
||||
| test3.cpp:252:8:252:16 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:252:24:252:32 | password2 | semmle.label | password2 |
|
||||
| test3.cpp:254:15:254:23 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:256:3:256:19 | call to decrypt_to_buffer | semmle.label | call to decrypt_to_buffer |
|
||||
| test3.cpp:256:21:256:29 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:256:32:256:40 | password2 | semmle.label | password2 |
|
||||
| test3.cpp:260:8:260:16 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:260:24:260:32 | password2 | semmle.label | password2 |
|
||||
| test3.cpp:262:3:262:19 | call to encrypt_to_buffer | semmle.label | call to encrypt_to_buffer |
|
||||
| test3.cpp:262:21:262:29 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:262:32:262:40 | password2 | semmle.label | password2 |
|
||||
| test3.cpp:264:15:264:23 | password2 | semmle.label | password2 |
|
||||
| test3.cpp:268:19:268:26 | password | semmle.label | password |
|
||||
| test3.cpp:272:15:272:18 | data | semmle.label | data |
|
||||
| test3.cpp:278:20:278:23 | data | semmle.label | data |
|
||||
| test3.cpp:278:20:278:23 | data | semmle.label | data |
|
||||
| test3.cpp:280:14:280:17 | data | semmle.label | data |
|
||||
| test3.cpp:283:20:283:23 | data | semmle.label | data |
|
||||
| test3.cpp:283:20:283:23 | data | semmle.label | data |
|
||||
| test3.cpp:285:14:285:17 | data | semmle.label | data |
|
||||
| test3.cpp:288:20:288:23 | data | semmle.label | data |
|
||||
| test3.cpp:290:14:290:17 | data | semmle.label | data |
|
||||
| test3.cpp:293:20:293:23 | data | semmle.label | data |
|
||||
| test3.cpp:293:20:293:23 | data | semmle.label | data |
|
||||
| test3.cpp:295:14:295:17 | data | semmle.label | data |
|
||||
| test3.cpp:298:20:298:23 | data | semmle.label | data |
|
||||
| test3.cpp:300:14:300:17 | data | semmle.label | data |
|
||||
| test3.cpp:308:41:308:49 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:308:58:308:66 | password2 | semmle.label | password2 |
|
||||
| test3.cpp:312:3:312:17 | call to encrypt_inplace | semmle.label | call to encrypt_inplace |
|
||||
| test3.cpp:312:19:312:27 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:313:11:313:19 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:313:11:313:19 | ref arg password1 | semmle.label | ref arg password1 |
|
||||
| test3.cpp:314:11:314:19 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:316:11:316:19 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:316:11:316:19 | ref arg password1 | semmle.label | ref arg password1 |
|
||||
| test3.cpp:317:11:317:19 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:324:11:324:14 | data | semmle.label | data |
|
||||
| test3.cpp:324:11:324:14 | ref arg data | semmle.label | ref arg data |
|
||||
| test3.cpp:325:11:325:14 | data | semmle.label | data |
|
||||
| test3.cpp:339:9:339:16 | password | semmle.label | password |
|
||||
| test3.cpp:341:16:341:23 | password | semmle.label | password |
|
||||
| test3.cpp:350:9:350:16 | password | semmle.label | password |
|
||||
| test3.cpp:352:16:352:23 | password | semmle.label | password |
|
||||
| test3.cpp:353:4:353:18 | call to decrypt_inplace | semmle.label | call to decrypt_inplace |
|
||||
| test3.cpp:353:20:353:27 | password | semmle.label | password |
|
||||
| test.cpp:41:23:41:43 | cleartext password! | semmle.label | cleartext password! |
|
||||
| test.cpp:48:21:48:27 | call to encrypt | semmle.label | call to encrypt |
|
||||
| test.cpp:48:29:48:39 | thePassword | semmle.label | thePassword |
|
||||
| test.cpp:66:23:66:43 | cleartext password! | semmle.label | cleartext password! |
|
||||
| test.cpp:76:21:76:27 | call to encrypt | semmle.label | call to encrypt |
|
||||
| test.cpp:76:29:76:39 | thePassword | semmle.label | thePassword |
|
||||
| test3.cpp:20:15:20:23 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:24:15:24:23 | password2 | semmle.label | password2 |
|
||||
| test3.cpp:41:15:41:22 | password | semmle.label | password |
|
||||
| test3.cpp:49:15:49:22 | password | semmle.label | password |
|
||||
| test3.cpp:68:21:68:29 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:70:15:70:17 | ptr | semmle.label | ptr |
|
||||
| test3.cpp:75:15:75:22 | password | semmle.label | password |
|
||||
| test3.cpp:77:15:77:17 | ptr | semmle.label | ptr |
|
||||
| test3.cpp:95:12:95:19 | password | semmle.label | password |
|
||||
| test3.cpp:106:20:106:25 | buffer | semmle.label | buffer |
|
||||
| test3.cpp:108:14:108:19 | buffer | semmle.label | buffer |
|
||||
| test3.cpp:111:28:111:33 | buffer | semmle.label | buffer |
|
||||
| test3.cpp:113:9:113:14 | buffer | semmle.label | buffer |
|
||||
| test3.cpp:120:9:120:23 | global_password | semmle.label | global_password |
|
||||
| test3.cpp:128:11:128:18 | password | semmle.label | password |
|
||||
| test3.cpp:132:21:132:22 | call to id | semmle.label | call to id |
|
||||
| test3.cpp:132:24:132:32 | password1 | semmle.label | password1 |
|
||||
| test3.cpp:134:15:134:17 | ptr | semmle.label | ptr |
|
||||
| test3.cpp:138:16:138:29 | call to get_global_str | semmle.label | call to get_global_str |
|
||||
| test3.cpp:140:15:140:18 | data | semmle.label | data |
|
||||
| test3.cpp:151:19:151:26 | password | semmle.label | password |
|
||||
| test3.cpp:153:15:153:20 | buffer | semmle.label | buffer |
|
||||
subpaths
|
||||
| test3.cpp:138:24:138:32 | password1 | test3.cpp:117:28:117:33 | buffer | test3.cpp:119:9:119:14 | buffer | test3.cpp:138:21:138:22 | call to id |
|
||||
| test3.cpp:313:11:313:19 | password1 | test3.cpp:278:20:278:23 | data | test3.cpp:278:20:278:23 | data | test3.cpp:313:11:313:19 | ref arg password1 |
|
||||
| test3.cpp:316:11:316:19 | password1 | test3.cpp:283:20:283:23 | data | test3.cpp:283:20:283:23 | data | test3.cpp:316:11:316:19 | ref arg password1 |
|
||||
| test3.cpp:324:11:324:14 | data | test3.cpp:293:20:293:23 | data | test3.cpp:293:20:293:23 | data | test3.cpp:324:11:324:14 | ref arg data |
|
||||
| test3.cpp:132:24:132:32 | password1 | test3.cpp:111:28:111:33 | buffer | test3.cpp:113:9:113:14 | buffer | test3.cpp:132:21:132:22 | call to id |
|
||||
#select
|
||||
| test3.cpp:22:3:22:6 | call to send | test3.cpp:17:28:17:36 | password1 | test3.cpp:22:15:22:23 | password1 | This operation transmits 'password1', which may contain unencrypted sensitive data from $@ | test3.cpp:17:28:17:36 | password1 | password1 |
|
||||
| test3.cpp:26:3:26:6 | call to send | test3.cpp:17:51:17:59 | password2 | test3.cpp:26:15:26:23 | password2 | This operation transmits 'password2', which may contain unencrypted sensitive data from $@ | test3.cpp:17:51:17:59 | password2 | password2 |
|
||||
| test3.cpp:47:3:47:6 | call to recv | test3.cpp:45:8:45:15 | password | test3.cpp:47:15:47:22 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:45:8:45:15 | password | password |
|
||||
| test3.cpp:55:3:55:6 | call to recv | test3.cpp:53:8:53:15 | password | test3.cpp:55:15:55:22 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:53:8:53:15 | password | password |
|
||||
| test3.cpp:76:3:76:6 | call to send | test3.cpp:71:32:71:40 | password1 | test3.cpp:76:15:76:17 | ptr | This operation transmits 'ptr', which may contain unencrypted sensitive data from $@ | test3.cpp:71:32:71:40 | password1 | password1 |
|
||||
| test3.cpp:83:3:83:6 | call to recv | test3.cpp:80:8:80:15 | password | test3.cpp:83:15:83:17 | ptr | This operation receives into 'ptr', which may put unencrypted sensitive data into $@ | test3.cpp:80:8:80:15 | password | password |
|
||||
| test3.cpp:101:3:101:6 | call to read | test3.cpp:98:8:98:15 | password | test3.cpp:101:12:101:19 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:98:8:98:15 | password | password |
|
||||
| test3.cpp:114:2:114:5 | call to recv | test3.cpp:132:8:132:15 | password | test3.cpp:114:14:114:19 | buffer | This operation receives into 'buffer', which may put unencrypted sensitive data into $@ | test3.cpp:132:8:132:15 | password | password |
|
||||
| test3.cpp:140:3:140:6 | call to send | test3.cpp:129:39:129:47 | password1 | test3.cpp:140:15:140:17 | ptr | This operation transmits 'ptr', which may contain unencrypted sensitive data from $@ | test3.cpp:129:39:129:47 | password1 | password1 |
|
||||
| test3.cpp:146:3:146:6 | call to send | test3.cpp:126:9:126:23 | global_password | test3.cpp:146:15:146:18 | data | This operation transmits 'data', which may contain unencrypted sensitive data from $@ | test3.cpp:126:9:126:23 | global_password | global_password |
|
||||
| test3.cpp:159:3:159:6 | call to send | test3.cpp:152:29:152:36 | password | test3.cpp:159:15:159:20 | buffer | This operation transmits 'buffer', which may contain unencrypted sensitive data from $@ | test3.cpp:152:29:152:36 | password | password |
|
||||
| test3.cpp:227:2:227:5 | call to send | test3.cpp:225:34:225:41 | password | test3.cpp:227:22:227:29 | password | This operation transmits 'password', which may contain unencrypted sensitive data from $@ | test3.cpp:225:34:225:41 | password | password |
|
||||
| test3.cpp:228:2:228:5 | call to send | test3.cpp:225:34:225:41 | password | test3.cpp:228:26:228:33 | password | This operation transmits 'password', which may contain unencrypted sensitive data from $@ | test3.cpp:225:34:225:41 | password | password |
|
||||
| test3.cpp:241:2:241:6 | call to fgets | test3.cpp:239:7:239:14 | password | test3.cpp:241:8:241:15 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:239:7:239:14 | password | password |
|
||||
| test3.cpp:242:2:242:6 | call to fgets | test3.cpp:239:7:239:14 | password | test3.cpp:242:8:242:15 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:239:7:239:14 | password | password |
|
||||
| test3.cpp:272:3:272:6 | call to send | test3.cpp:268:19:268:26 | password | test3.cpp:272:15:272:18 | data | This operation transmits 'data', which may contain unencrypted sensitive data from $@ | test3.cpp:268:19:268:26 | password | password |
|
||||
| test3.cpp:295:2:295:5 | call to send | test3.cpp:308:58:308:66 | password2 | test3.cpp:295:14:295:17 | data | This operation transmits 'data', which may contain unencrypted sensitive data from $@ | test3.cpp:308:58:308:66 | password2 | password2 |
|
||||
| test3.cpp:300:2:300:5 | call to send | test3.cpp:308:58:308:66 | password2 | test3.cpp:300:14:300:17 | data | This operation transmits 'data', which may contain unencrypted sensitive data from $@ | test3.cpp:308:58:308:66 | password2 | password2 |
|
||||
| test3.cpp:341:4:341:7 | call to recv | test3.cpp:339:9:339:16 | password | test3.cpp:341:16:341:23 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:339:9:339:16 | password | password |
|
||||
| test3.cpp:20:3:20:6 | call to send | test3.cpp:20:15:20:23 | password1 | test3.cpp:20:15:20:23 | password1 | This operation transmits 'password1', which may contain unencrypted sensitive data from $@ | test3.cpp:20:15:20:23 | password1 | password1 |
|
||||
| test3.cpp:24:3:24:6 | call to send | test3.cpp:24:15:24:23 | password2 | test3.cpp:24:15:24:23 | password2 | This operation transmits 'password2', which may contain unencrypted sensitive data from $@ | test3.cpp:24:15:24:23 | password2 | password2 |
|
||||
| test3.cpp:41:3:41:6 | call to recv | test3.cpp:41:15:41:22 | password | test3.cpp:41:15:41:22 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:41:15:41:22 | password | password |
|
||||
| test3.cpp:49:3:49:6 | call to recv | test3.cpp:49:15:49:22 | password | test3.cpp:49:15:49:22 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:49:15:49:22 | password | password |
|
||||
| test3.cpp:70:3:70:6 | call to send | test3.cpp:68:21:68:29 | password1 | test3.cpp:70:15:70:17 | ptr | This operation transmits 'ptr', which may contain unencrypted sensitive data from $@ | test3.cpp:68:21:68:29 | password1 | password1 |
|
||||
| test3.cpp:77:3:77:6 | call to recv | test3.cpp:75:15:75:22 | password | test3.cpp:77:15:77:17 | ptr | This operation receives into 'ptr', which may put unencrypted sensitive data into $@ | test3.cpp:75:15:75:22 | password | password |
|
||||
| test3.cpp:95:3:95:6 | call to read | test3.cpp:95:12:95:19 | password | test3.cpp:95:12:95:19 | password | This operation receives into 'password', which may put unencrypted sensitive data into $@ | test3.cpp:95:12:95:19 | password | password |
|
||||
| test3.cpp:108:2:108:5 | call to recv | test3.cpp:128:11:128:18 | password | test3.cpp:108:14:108:19 | buffer | This operation receives into 'buffer', which may put unencrypted sensitive data into $@ | test3.cpp:128:11:128:18 | password | password |
|
||||
| test3.cpp:134:3:134:6 | call to send | test3.cpp:132:24:132:32 | password1 | test3.cpp:134:15:134:17 | ptr | This operation transmits 'ptr', which may contain unencrypted sensitive data from $@ | test3.cpp:132:24:132:32 | password1 | password1 |
|
||||
| test3.cpp:140:3:140:6 | call to send | test3.cpp:120:9:120:23 | global_password | test3.cpp:140:15:140:18 | data | This operation transmits 'data', which may contain unencrypted sensitive data from $@ | test3.cpp:120:9:120:23 | global_password | global_password |
|
||||
| test3.cpp:153:3:153:6 | call to send | test3.cpp:151:19:151:26 | password | test3.cpp:153:15:153:20 | buffer | This operation transmits 'buffer', which may contain unencrypted sensitive data from $@ | test3.cpp:151:19:151:26 | password | password |
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
|
||||
typedef unsigned long size_t;
|
||||
#define STDIN_FILENO (0)
|
||||
#define STDOUT_FILENO (1)
|
||||
int stdout_fileno = STDOUT_FILENO;
|
||||
|
||||
size_t strlen(const char *s);
|
||||
|
||||
@@ -33,10 +31,6 @@ void test_send(const char *password1, const char *password2, const char *passwor
|
||||
{
|
||||
send(val(), message, strlen(message), val()); // GOOD: `message` is not a password
|
||||
}
|
||||
|
||||
{
|
||||
send(stdout_fileno, password2, strlen(password2), val()); // GOOD: `password2` is sent to stdout, not a network socket (this may be an issue but is not within the scope of the `cpp/cleartext-transmission` query)
|
||||
}
|
||||
}
|
||||
|
||||
void test_receive()
|
||||
@@ -131,7 +125,7 @@ void test_interprocedural(const char *password1)
|
||||
{
|
||||
char password[256];
|
||||
|
||||
my_recv(password, 256); // BAD: `password` is received plaintext [detected in `my_recv`]
|
||||
my_recv(password, 256); // BAD: `password` is received plaintext [detected on line 108]
|
||||
}
|
||||
|
||||
{
|
||||
@@ -159,200 +153,3 @@ void test_taint(const char *password)
|
||||
send(val(), buffer, 16, val()); // BAD: `password` is (partially) sent plaintext
|
||||
}
|
||||
}
|
||||
|
||||
void encrypt_inplace(char *buffer);
|
||||
void decrypt_inplace(char *buffer);
|
||||
char *rtn_encrypt(const char *buffer);
|
||||
char *rtn_decrypt(const char *buffer);
|
||||
|
||||
void test_decrypt()
|
||||
{
|
||||
{
|
||||
char password[256];
|
||||
|
||||
recv(val(), password, 256, val()); // GOOD: password is encrypted
|
||||
|
||||
decrypt_inplace(password); // proof that `password` was in fact encrypted
|
||||
}
|
||||
|
||||
{
|
||||
char password[256];
|
||||
|
||||
recv(val(), password, 256, val()); // GOOD: password is encrypted
|
||||
password[255] = 0;
|
||||
|
||||
decrypt_inplace(password); // proof that `password` was in fact encrypted
|
||||
}
|
||||
|
||||
{
|
||||
char password[256];
|
||||
char *password_ptr;
|
||||
|
||||
recv(val(), password, 256, val()); // GOOD: password is encrypted
|
||||
|
||||
password_ptr = rtn_decrypt(password); // proof that `password` was in fact encrypted
|
||||
}
|
||||
|
||||
{
|
||||
char password[256];
|
||||
|
||||
encrypt_inplace(password); // proof that `password` is in fact encrypted
|
||||
|
||||
send(val(), password, strlen(password), val()); // GOOD: password is encrypted
|
||||
}
|
||||
|
||||
{
|
||||
char password[256];
|
||||
|
||||
encrypt_inplace(password); // proof that `password` is in fact encrypted
|
||||
password[255] = 0;
|
||||
|
||||
send(val(), password, strlen(password), val()); // GOOD: password is encrypted
|
||||
}
|
||||
|
||||
{
|
||||
char password[256];
|
||||
char *password_ptr;
|
||||
|
||||
password_ptr = rtn_encrypt(password); // proof that `password` is in fact encrypted
|
||||
|
||||
send(val(), password_ptr, strlen(password_ptr), val()); // GOOD: password is encrypted
|
||||
}
|
||||
}
|
||||
|
||||
int get_socket(int from);
|
||||
|
||||
void test_more_stdio(const char *password)
|
||||
{
|
||||
send(get_socket(1), password, 128, val()); // GOOD: `getsocket(1)` is probably standard output [FALSE POSITIVE]
|
||||
send(get_socket(val()), password, 128, val()); // BAD
|
||||
}
|
||||
|
||||
typedef struct {} FILE;
|
||||
char *fgets(char *s, int n, FILE *stream);
|
||||
|
||||
FILE *get_stdstream(int index);
|
||||
#define STDIN_STREAM (get_stdstream(0))
|
||||
|
||||
void test_fgets(FILE *stream)
|
||||
{
|
||||
char password[128];
|
||||
|
||||
fgets(password, 128, stream); // BAD
|
||||
fgets(password, 128, STDIN_STREAM); // GOOD: `STDIN_STREAM` is probably standard input [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
void encrypt_to_buffer(const char *input, char* output);
|
||||
void decrypt_to_buffer(const char *input, char* output);
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
|
||||
void test_crypt_more()
|
||||
{
|
||||
{
|
||||
char password1[256], password2[256];
|
||||
|
||||
recv(val(), password1, 256, val()); // GOOD: password is encrypted
|
||||
|
||||
decrypt_to_buffer(password1, password2); // proof that `password1` was in fact encrypted
|
||||
}
|
||||
|
||||
{
|
||||
char password1[256], password2[256];
|
||||
|
||||
encrypt_to_buffer(password1, password2); // proof that `password2` is in fact encrypted
|
||||
|
||||
send(val(), password2, strlen(password2), val()); // GOOD: password is encrypted
|
||||
}
|
||||
|
||||
{
|
||||
char data[256], password[256];
|
||||
|
||||
strcpy(data, password); // not proof of anything
|
||||
|
||||
send(val(), data, strlen(data), val()); // BAD: password is sent plaintext
|
||||
}
|
||||
}
|
||||
|
||||
bool cond();
|
||||
|
||||
void target1(char *data)
|
||||
{
|
||||
send(val(), data, strlen(data), val()); // GOOD: encrypted
|
||||
}
|
||||
|
||||
void target2(char *data)
|
||||
{
|
||||
send(val(), data, strlen(data), val()); // BAD: from one source this is a plaintext password [NOT DETECTED]
|
||||
}
|
||||
|
||||
void target3(char *data)
|
||||
{
|
||||
send(val(), data, strlen(data), val()); // BAD: data is a plaintext password [NOT DETECTED]
|
||||
}
|
||||
|
||||
void target4(char *data)
|
||||
{
|
||||
send(val(), data, strlen(data), val()); // BAD: data is a plaintext password
|
||||
}
|
||||
|
||||
void target5(char *data)
|
||||
{
|
||||
send(val(), data, strlen(data), val()); // BAD: from one source this is a plaintext password
|
||||
}
|
||||
|
||||
void target6(char *data)
|
||||
{
|
||||
send(val(), data, strlen(data), val()); // GOOD: not a password
|
||||
}
|
||||
|
||||
void test_multiple_sources_source(char *password1, char *password2)
|
||||
{
|
||||
if (cond())
|
||||
{
|
||||
encrypt_inplace(password1);
|
||||
target1(password1);
|
||||
target2(password1);
|
||||
} else {
|
||||
target2(password1);
|
||||
target3(password1);
|
||||
}
|
||||
|
||||
if (cond())
|
||||
{
|
||||
char *data = password2;
|
||||
|
||||
target4(data);
|
||||
target5(data);
|
||||
} else {
|
||||
char *data = "harmless";
|
||||
|
||||
target5(data);
|
||||
target6(data);
|
||||
}
|
||||
}
|
||||
|
||||
void test_loops()
|
||||
{
|
||||
{
|
||||
while (cond())
|
||||
{
|
||||
char password[256];
|
||||
|
||||
recv(val(), password, 256, val()); // BAD: not encrypted
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
while (cond())
|
||||
{
|
||||
char password[256];
|
||||
|
||||
recv(val(), password, 256, val()); // GOOD: password is encrypted
|
||||
decrypt_inplace(password); // proof that `password` was in fact encrypted
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user