Compare commits

..

1 Commits

Author SHA1 Message Date
Andrew Eisenberg
bcd942270e Actions: Add workflow to publish packages 2021-11-09 15:16:48 -08:00
3580 changed files with 110326 additions and 292423 deletions

View File

@@ -1,27 +1,11 @@
{
"provide": [
"*/ql/src/qlpack.yml",
"*/ql/lib/qlpack.yml",
"*/ql/test/qlpack.yml",
"*/ql/examples/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"
],
"versionPolicies": {
"default": {
"requireChangeNotes": true,
"committedPrereleaseSuffix": "dev",
"committedVersion": "nextPatchRelease"
}
}
}
{ "provide": [ "ruby/.codeqlmanifest.json",
"*/ql/src/qlpack.yml",
"*/ql/lib/qlpack.yml",
"*/ql/test/qlpack.yml",
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
"*/ql/examples/qlpack.yml",
"*/upgrades/qlpack.yml",
"javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml",
"javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml",
"misc/legacy-support/*/qlpack.yml",
"misc/suite-helpers/qlpack.yml" ] }

View File

@@ -1,14 +1,29 @@
name: Fetch CodeQL
description: Fetches the latest version of CodeQL
inputs:
use-bundle:
description: Set to `true` to download the CodeQL CLI bundle that also includes compiled queries.
default: 'false'
required: false
runs:
using: composite
steps:
- name: Fetch CodeQL
shell: bash
run: |
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1)
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST"
unzip -q -d "${RUNNER_TEMP}" codeql-linux64.zip
LATEST=$(gh release list --repo $REPO | cut -f 3 | grep -v beta | sort --version-sort | tail -1)
gh release download --repo $REPO --pattern "$PATTERN" "$LATEST"
if [ "$USE_BUNDLE" == 'true' ]; then
tar -xzf "$PATTERN" -C "${RUNNER_TEMP}"
else
unzip -q -d "${RUNNER_TEMP}" "$PATTERN"
fi
echo "${RUNNER_TEMP}/codeql" >> "${GITHUB_PATH}"
env:
GITHUB_TOKEN: ${{ github.token }}
USE_BUNDLE: '${{ inputs.use-bundle == ''true'' }}'
REPO: '${{ inputs.use-bundle == ''true'' && ''https://github.com/dsp-testing/codeql-cli-nightlies'' || ''https://github.com/github/codeql-cli-binaries''}}'
# REPO: '${{ inputs.use-bundle == ''true'' && ''https://github.com/github/codeql-action'' || ''https://github.com/github/codeql-cli-binaries''}}'
PATTERN: '${{ inputs.use-bundle == ''true'' && ''codeql-bundle-linux64.tar.gz'' || ''codeql-linux64.zip''}}'

3
.github/labeler.yml vendored
View File

@@ -26,6 +26,3 @@ documentation:
- "**/*.qhelp"
- "**/*.md"
- docs/**/*
"QL-for-QL":
- ql/**/*

View File

@@ -7,7 +7,6 @@ on:
- "*/ql/src/**/*.ql"
- "*/ql/src/**/*.qll"
- "!**/experimental/**"
- "!ql/**"
jobs:
check-change-note:

88
.github/workflows/pack-publisher.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
# Publishes the core libraries to the CodeQL package registry.
name: Publish CodeQL core libraries
on:
pull_request:
paths:
- '.github/workflows/pack-publisher.yml' # for testing changes to this workflow
workflow_dispatch:
# the cli to use, or blank to build it again
# the pre-built packs, or blank to build again
inputs:
packages-build-number:
description: |
A CodeQL CLI workflow run number to download the packages artifacts from.
Leave blank to build packages from this repository.
default: ''
required: false
permissions:
contents: write
jobs:
codeql-package-publish:
name: CodeQL Package - Publish
runs-on: ubuntu-20.04
env:
GITHUB_TOKEN: ${{ github.token }}
steps:
- name: Dump environment
run: env
- name: Dump GitHub event context
env:
GITHUB_CONTEXT: '${{ toJson(github.event) }}'
run: echo "$GITHUB_CONTEXT"
- name: Checkout repository
uses: actions/checkout@v2
# TODO add a way to specify different versions of the CLI
- name: Download CLI
uses: ./.github/actions/fetch-codeql
with:
use-bundle: 'true'
- name: Publish packs
run: |
# do not publish go or suite-helpers
# `ls` all directories in the bundle remove suite-helpers and go
PACK_FOLDERS_TO_PUBLISH="$(ls -d $RUNNER_TEMP/codeql/qlpacks/codeql/*/* | grep -v suite | grep -v "\-go")"
ARCHIVES="$RUNNER_TEMP/archives"
mkdir -p "$ARCHIVES"
echo "Running on: $PACK_FOLDERS_TO_PUBLISH"
# tgz each folder
# then run pack publish on it
for folder in $PACK_FOLDERS_TO_PUBLISH
do
echo "Archiving $folder for publishing"
tar cfz "$ARCHIVES/archive.tgz" -C "$folder" .
echo "Publishing $ARCHIVES/archive.tgz"
echo "Would have run: 'codeql pack publish --file "$ARCHIVES/archive.tgz"'"
done
- name: Bump versions
run: |
echo "Would have run 'codeql pack release'"
- name: Update git config
run: |
git config --global user.email "github-actions@github.com"
git config --global user.name "github-actions[bot]"
- name: Create PR
run: |
set -exu
git add .
git commit -m "Post-release preparation"
NEW_BRANCH="post-release-prep-$(git show -s --format=%h)"
git checkout -b $NEW_BRANCH
git push origin "$NEW_BRANCH"
gh pr create \
--head "$NEW_BRANCH" \
--base "$GITHUB_BASE_REF" \
--fill \
--draft

View File

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

View File

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

View File

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

View File

@@ -102,6 +102,16 @@ jobs:
PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*)
codeql/codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
(cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;)
- name: Compile with previous CodeQL versions
run: |
for version in $(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | tail -3 | head -2); do
rm -f codeql-linux64.zip
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$version"
rm -rf codeql; unzip -q codeql-linux64.zip
codeql/codeql query compile target/packs/*
done
env:
GITHUB_TOKEN: ${{ github.token }}
- uses: actions/upload-artifact@v2
with:
name: codeql-ruby-queries

View File

@@ -24,7 +24,7 @@ jobs:
strategy:
fail-fast: false
matrix:
repo: [rails/rails, discourse/discourse, spree/spree, ruby/ruby]
repo: [rails/rails, discourse/discourse, spree/spree]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
@@ -41,7 +41,7 @@ jobs:
- name: Create database
run: |
codeql database create \
--search-path "${{ github.workspace }}/ruby/extractor-pack" \
--search-path "${{ github.workspace }}/ruby" \
--threads 4 \
--language ruby --source-root "${{ github.workspace }}/repo" \
"${{ runner.temp }}/database"

View File

@@ -32,14 +32,14 @@ jobs:
- uses: ./ruby/actions/create-extractor-pack
- name: Run QL tests
run: |
codeql test run --threads=0 --ram 5000 --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test
codeql test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}" --consistency-queries ql/consistency-queries ql/test
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Check QL formatting
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
- name: Check QL compilation
run: |
codeql query compile --check-only --threads=0 --ram 5000 --warnings=error "ql/src" "ql/examples"
codeql query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}" "ql/src" "ql/examples"
env:
GITHUB_TOKEN: ${{ github.token }}
- name: Check DB upgrade scripts

3
.gitignore vendored
View File

@@ -27,6 +27,3 @@ csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
# Avoid committing cached package components
.codeql
# Compiled class file
*.class

View File

@@ -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/ @github/codeql-ql-for-ql-reviewers

View File

@@ -4,9 +4,6 @@ We welcome contributions to our CodeQL libraries and queries. Got an idea for a
There is lots of useful documentation to help you write queries, ranging from information about query file structure to tutorials for specific target languages. For more information on the documentation available, see [CodeQL queries](https://help.semmle.com/QL/learn-ql/writing-queries/writing-queries.html) on [help.semmle.com](https://help.semmle.com).
## Change notes
Any nontrivial user-visible change to a query pack or library pack should have a change note. For details on how to add a change note for your change, see [this guide](docs/change-notes.md).
## Submitting a new experimental query

View File

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

View File

@@ -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 `"\$.*\$"`.

View File

@@ -7,7 +7,6 @@
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll",
"java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
"cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
@@ -453,24 +452,17 @@
"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",
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll"
],
"ReDoS Util Python/JS/Ruby": [
"ReDoS Util Python/JS": [
"javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll",
"python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll",
"ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll"
"python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll"
],
"ReDoS Exponential Python/JS": [
"javascript/ql/lib/semmle/javascript/security/performance/ExponentialBackTracking.qll",
@@ -479,12 +471,7 @@
"ReDoS Polynomial Python/JS": [
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
"python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll",
"ruby/ql/lib/codeql/ruby/security/performance/SuperlinearBackTracking.qll"
],
"BadTagFilterQuery Python/JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll",
"python/ql/lib/semmle/python/security/BadTagFilterQuery.qll",
"ruby/ql/lib/codeql/ruby/security/BadTagFilterQuery.qll"
"ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll"
],
"CFG": [
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",

View File

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

View File

@@ -1,7 +1,4 @@
## 0.0.4
### New Features
lgtm,codescanning
* The QL library `semmle.code.cpp.commons.Exclusions` now contains a predicate
`isFromSystemMacroDefinition` for identifying code that originates from a
macro outside the project being analyzed.

View File

@@ -5,11 +5,9 @@
@name Badly bounded write (CWE-120)
+ semmlecode-cpp-queries/Security/CWE/CWE-120/OverrunWrite.ql: /CWE/CWE-120
@name Potentially overrunning write (CWE-120)
+ semmlecode-cpp-queries/Security/CWE/CWE-120/VeryLikelyOverrunWrite.ql: /CWE/CWE-120
@name Likely overrunning write
+ semmlecode-cpp-queries/Security/CWE/CWE-120/OverrunWriteFloat.ql: /CWE/CWE-120
@name Potentially overrunning write with float to string conversion (CWE-120)
+ semmlecode-cpp-queries/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.ql: /CWE/CWE-120
@name Array offset used before range check (CWE-120)
+ semmlecode-cpp-queries/Likely Bugs/Memory Management/UnsafeUseOfStrcat.ql: /CWE/CWE-120
@name Potentially unsafe use of strcat (CWE-120)
@name Potentially unsafe use of strcat (CWE-120)

View File

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

View File

@@ -1,6 +1,4 @@
name: codeql/cpp-examples
groups:
- cpp
- examples
version: 0.0.2
dependencies:
codeql/cpp-all: "*"

View File

@@ -1,13 +0,0 @@
## 0.0.7
## 0.0.6
## 0.0.5
## 0.0.4
### New Features
* The QL library `semmle.code.cpp.commons.Exclusions` now contains a predicate
`isFromSystemMacroDefinition` for identifying code that originates from a
macro outside the project being analyzed.

View File

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

View File

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

View File

@@ -1,4 +0,0 @@
---
category: deprecated
---
* The `codeql/cpp-upgrades` CodeQL pack has been removed. All upgrades scripts have been merged into the `codeql/cpp-all` CodeQL pack.

View File

@@ -1,5 +0,0 @@
---
category: minorAnalysis
---
* `FormatLiteral::getMaxConvertedLength` now uses range analysis to provide a
more accurate length for integers formatted with `%x`

View File

@@ -1 +0,0 @@
## 0.0.5

View File

@@ -1 +0,0 @@
## 0.0.6

View File

@@ -1 +0,0 @@
## 0.0.7

View File

@@ -1,2 +0,0 @@
---
lastReleaseVersion: 0.0.7

View File

@@ -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`.
*/

View File

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

View File

@@ -1,7 +1,7 @@
name: codeql/cpp-all
version: 0.0.8-dev
groups: cpp
version: 0.0.2
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
library: true
upgrades: upgrades
dependencies:
codeql/cpp-upgrades: 0.0.2

View File

@@ -206,7 +206,9 @@ class Class extends UserType {
* it is callable by a particular caller. For C++11, there's also a question
* of whether to include members that are defaulted or deleted.
*/
deprecated predicate hasCopyConstructor() { this.getAMemberFunction() instanceof CopyConstructor }
deprecated predicate hasCopyConstructor() {
exists(CopyConstructor cc | cc = this.getAMemberFunction())
}
/**
* Holds if this class has a copy assignment operator that is either
@@ -222,7 +224,7 @@ class Class extends UserType {
* or deleted.
*/
deprecated predicate hasCopyAssignmentOperator() {
this.getAMemberFunction() instanceof CopyAssignmentOperator
exists(CopyAssignmentOperator coa | coa = this.getAMemberFunction())
}
/**
@@ -885,7 +887,7 @@ class NestedClass extends Class {
* pure virtual function.
*/
class AbstractClass extends Class {
AbstractClass() { this.getAMemberFunction() instanceof PureVirtualFunction }
AbstractClass() { exists(PureVirtualFunction f | this.getAMemberFunction() = f) }
override string getAPrimaryQlClass() { result = "AbstractClass" }
}

View File

@@ -286,13 +286,13 @@ class AttributeArgument extends Element, @attribute_arg {
override Location getLocation() { attribute_args(underlyingElement(this), _, _, _, result) }
override string toString() {
if underlyingElement(this) instanceof @attribute_arg_empty
if exists(@attribute_arg_empty self | self = underlyingElement(this))
then result = "empty argument"
else
exists(string prefix, string tail |
(if exists(this.getName()) then prefix = this.getName() + "=" else prefix = "") and
(
if underlyingElement(this) instanceof @attribute_arg_type
if exists(@attribute_arg_type self | self = underlyingElement(this))
then tail = this.getValueType().getName()
else tail = this.getValueText()
) and

View File

@@ -233,7 +233,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable {
XMLAttribute getAttribute(string name) { result.getElement() = this and result.getName() = name }
/** Holds if this XML element has an attribute with the specified `name`. */
predicate hasAttribute(string name) { exists(this.getAttribute(name)) }
predicate hasAttribute(string name) { exists(XMLAttribute a | a = this.getAttribute(name)) }
/** Gets the value of the attribute with the specified `name`, if any. */
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }

View File

@@ -101,21 +101,6 @@ predicate functionArgumentMustBeNullTerminated(Function f, int i) {
f instanceof StrcatFunction and i = 0
}
/**
* Holds if `arg` is a string format argument to a formatting function call
* `ffc`.
*/
predicate formatArgumentMustBeNullTerminated(FormattingFunctionCall ffc, Expr arg) {
// String argument to a formatting function (such as `printf`)
exists(int n, FormatLiteral fl |
ffc.getConversionArgument(n) = arg and
fl = ffc.getFormat() and
fl.getConversionType(n) instanceof PointerType and // `%s`, `%ws` etc
not fl.getConversionType(n) instanceof VoidPointerType and // exclude: `%p`
not fl.hasPrecision(n) // exclude: `%.*s`
)
}
/**
* Holds if `va` is a variable access where the contents must be null terminated.
*/
@@ -128,7 +113,13 @@ predicate variableMustBeNullTerminated(VariableAccess va) {
)
or
// String argument to a formatting function (such as `printf`)
formatArgumentMustBeNullTerminated(fc, va)
exists(int n, FormatLiteral fl |
fc.(FormattingFunctionCall).getConversionArgument(n) = va and
fl = fc.(FormattingFunctionCall).getFormat() and
fl.getConversionType(n) instanceof PointerType and // `%s`, `%ws` etc
not fl.getConversionType(n) instanceof VoidPointerType and // exclude: `%p`
not fl.hasPrecision(n) // exclude: `%.*s`
)
or
// Call to a wrapper function that requires null termination
// (not itself adding a null terminator)

View File

@@ -6,108 +6,6 @@ import semmle.code.cpp.Type
import semmle.code.cpp.commons.CommonType
import semmle.code.cpp.commons.StringAnalysis
import semmle.code.cpp.models.interfaces.FormattingFunction
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
private newtype TBufferWriteEstimationReason =
TUnspecifiedEstimateReason() or
TTypeBoundsAnalysis() or
TWidenedValueFlowAnalysis() or
TValueFlowAnalysis()
private predicate gradeToReason(int grade, TBufferWriteEstimationReason reason) {
// when combining reasons, lower grade takes precedence
grade = 0 and reason = TUnspecifiedEstimateReason()
or
grade = 1 and reason = TTypeBoundsAnalysis()
or
grade = 2 and reason = TWidenedValueFlowAnalysis()
or
grade = 3 and reason = TValueFlowAnalysis()
}
/**
* A reason for a specific buffer write size estimate.
*/
abstract class BufferWriteEstimationReason extends TBufferWriteEstimationReason {
/**
* Returns the name of the concrete class.
*/
abstract string toString();
/**
* Returns a human readable representation of this reason.
*/
abstract string getDescription();
/**
* Combine estimate reasons. Used to give a reason for the size of a format string
* conversion given reasons coming from its individual specifiers.
*/
BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
exists(int grade, int otherGrade |
gradeToReason(grade, this) and gradeToReason(otherGrade, other)
|
if otherGrade < grade then result = other else result = this
)
}
}
/**
* No particular reason given. This is currently used for backward compatibility so that
* classes derived from BufferWrite and overriding `getMaxData/0` still work with the
* queries as intended.
*/
class UnspecifiedEstimateReason extends BufferWriteEstimationReason, TUnspecifiedEstimateReason {
override string toString() { result = "UnspecifiedEstimateReason" }
override string getDescription() { result = "no reason specified" }
}
/**
* The estimation comes from rough bounds just based on the type (e.g.
* `0 <= x < 2^32` for an unsigned 32 bit integer).
*/
class TypeBoundsAnalysis extends BufferWriteEstimationReason, TTypeBoundsAnalysis {
override string toString() { result = "TypeBoundsAnalysis" }
override string getDescription() { result = "based on type bounds" }
}
/**
* The estimation comes from non trivial bounds found via actual flow analysis,
* but a widening aproximation might have been used for variables in loops.
* For example
* ```
* for (int i = 0; i < 10; ++i) {
* int j = i + i;
* //... <- estimation done here based on j
* }
* ```
*/
class WidenedValueFlowAnalysis extends BufferWriteEstimationReason, TWidenedValueFlowAnalysis {
override string toString() { result = "WidenedValueFlowAnalysis" }
override string getDescription() {
result = "based on flow analysis of value bounds with a widening approximation"
}
}
/**
* The estimation comes from non trivial bounds found via actual flow analysis.
* For example
* ```
* unsigned u = x;
* if (u < 1000) {
* //... <- estimation done here based on u
* }
* ```
*/
class ValueFlowAnalysis extends BufferWriteEstimationReason, TValueFlowAnalysis {
override string toString() { result = "ValueFlowAnalysis" }
override string getDescription() { result = "based on flow analysis of value bounds" }
}
class PrintfFormatAttribute extends FormatAttribute {
PrintfFormatAttribute() { this.getArchetype() = ["printf", "__printf__"] }
@@ -370,50 +268,6 @@ class FormattingFunctionCall extends Expr {
}
}
/**
* Gets the number of digits required to represent the integer represented by `f`.
*
* `f` is assumed to be nonnegative.
*/
bindingset[f]
private int lengthInBase10(float f) {
f = 0 and result = 1
or
result = f.log10().floor() + 1
}
pragma[nomagic]
private predicate isPointerTypeWithBase(Type base, PointerType pt) { base = pt.getBaseType() }
bindingset[expr]
private BufferWriteEstimationReason getEstimationReasonForIntegralExpression(Expr expr) {
// we consider the range analysis non trivial if it
// * constrained non-trivially both sides of a signed value, or
// * constrained non-trivially the positive side of an unsigned value
// expr should already be given as getFullyConverted
if
upperBound(expr) < exprMaxVal(expr) and
(exprMinVal(expr) >= 0 or lowerBound(expr) > exprMinVal(expr))
then
// next we check whether the estimate may have been widened
if upperBoundMayBeWidened(expr)
then result = TWidenedValueFlowAnalysis()
else result = TValueFlowAnalysis()
else result = TTypeBoundsAnalysis()
}
/**
* Gets the number of hex digits required to represent the integer represented by `f`.
*
* `f` is assumed to be nonnegative.
*/
bindingset[f]
private int lengthInBase16(float f) {
f = 0 and result = 1
or
result = (f.log2() / 4.0).floor() + 1
}
/**
* A class to represent format strings that occur as arguments to invocations of formatting functions.
*/
@@ -965,19 +819,19 @@ class FormatLiteral extends Literal {
(
conv = ["s", "S"] and
len = "h" and
isPointerTypeWithBase(any(PlainCharType plainCharType), result)
result.(PointerType).getBaseType() instanceof PlainCharType
or
conv = ["s", "S"] and
len = ["l", "w"] and
isPointerTypeWithBase(this.getWideCharType(), result)
result.(PointerType).getBaseType() = this.getWideCharType()
or
conv = "s" and
(len != "l" and len != "w" and len != "h") and
isPointerTypeWithBase(this.getDefaultCharType(), result)
result.(PointerType).getBaseType() = this.getDefaultCharType()
or
conv = "S" and
(len != "l" and len != "w" and len != "h") and
isPointerTypeWithBase(this.getNonDefaultCharType(), result)
result.(PointerType).getBaseType() = this.getNonDefaultCharType()
)
)
}
@@ -1122,14 +976,7 @@ class FormatLiteral extends Literal {
* conversion specifier of this format string; has no result if this cannot
* be determined.
*/
int getMaxConvertedLength(int n) { result = max(this.getMaxConvertedLength(n, _)) }
/**
* Gets the maximum length of the string that can be produced by the nth
* conversion specifier of this format string, specifying the estimation reason;
* has no result if this cannot be determined.
*/
int getMaxConvertedLength(int n, BufferWriteEstimationReason reason) {
int getMaxConvertedLength(int n) {
exists(int len |
(
(
@@ -1141,12 +988,10 @@ class FormatLiteral extends Literal {
) and
(
this.getConversionChar(n) = "%" and
len = 1 and
reason = TValueFlowAnalysis()
len = 1
or
this.getConversionChar(n).toLowerCase() = "c" and
len = 1 and
reason = TValueFlowAnalysis() // e.g. 'a'
len = 1 // e.g. 'a'
or
this.getConversionChar(n).toLowerCase() = "f" and
exists(int dot, int afterdot |
@@ -1160,8 +1005,7 @@ class FormatLiteral extends Literal {
afterdot = 6
) and
len = 1 + 309 + dot + afterdot
) and
reason = TTypeBoundsAnalysis() // e.g. -1e308="-100000"...
) // e.g. -1e308="-100000"...
or
this.getConversionChar(n).toLowerCase() = "e" and
exists(int dot, int afterdot |
@@ -1175,8 +1019,7 @@ class FormatLiteral extends Literal {
afterdot = 6
) and
len = 1 + 1 + dot + afterdot + 1 + 1 + 3
) and
reason = TTypeBoundsAnalysis() // -1e308="-1.000000e+308"
) // -1e308="-1.000000e+308"
or
this.getConversionChar(n).toLowerCase() = "g" and
exists(int dot, int afterdot |
@@ -1199,110 +1042,61 @@ class FormatLiteral extends Literal {
// (e.g. 123456, 0.000123456 are just OK)
// so case %f can be at most P characters + 4 zeroes, sign, dot = P + 6
len = (afterdot.maximum(1) + 6).maximum(1 + 1 + dot + afterdot + 1 + 1 + 3)
) and
reason = TTypeBoundsAnalysis() // (e.g. "-1.59203e-319")
) // (e.g. "-1.59203e-319")
or
this.getConversionChar(n).toLowerCase() = ["d", "i"] and
// e.g. -2^31 = "-2147483648"
exists(float typeBasedBound, float valueBasedBound |
// The first case handles length sub-specifiers
// Subtract one in the exponent because one bit is for the sign.
// Add 1 to account for the possible sign in the output.
typeBasedBound =
1 + lengthInBase10(2.pow(this.getIntegralDisplayType(n).getSize() * 8 - 1)) and
// The second case uses range analysis to deduce a length that's shorter than the length
// of the number -2^31.
exists(Expr arg, float lower, float upper |
arg = this.getUse().getConversionArgument(n) and
lower = lowerBound(arg.getFullyConverted()) and
upper = upperBound(arg.getFullyConverted())
|
valueBasedBound =
max(int cand |
// Include the sign bit in the length if it can be negative
(
if lower < 0
then cand = 1 + lengthInBase10(lower.abs())
else cand = lengthInBase10(lower)
)
or
(
if upper < 0
then cand = 1 + lengthInBase10(upper.abs())
else cand = lengthInBase10(upper)
)
) and
// we don't want to call this on `arg.getFullyConverted()` as we want
// to detect non-trivial range analysis without taking into account up-casting
reason = getEstimationReasonForIntegralExpression(arg)
) and
len = valueBasedBound.minimum(typeBasedBound)
)
or
this.getConversionChar(n).toLowerCase() = "u" and
// e.g. 2^32 - 1 = "4294967295"
exists(float typeBasedBound, float valueBasedBound |
// The first case handles length sub-specifiers
typeBasedBound = lengthInBase10(2.pow(this.getIntegralDisplayType(n).getSize() * 8) - 1) and
// The second case uses range analysis to deduce a length that's shorter than
// the length of the number 2^31 - 1.
exists(Expr arg, float lower, float upper |
arg = this.getUse().getConversionArgument(n) and
lower = lowerBound(arg.getFullyConverted()) and
upper = upperBound(arg.getFullyConverted())
|
valueBasedBound =
lengthInBase10(max(float cand |
// If lower can be negative we use `(unsigned)-1` as the candidate value.
lower < 0 and
cand = 2.pow(any(IntType t | t.isUnsigned()).getSize() * 8)
or
cand = upper
)) and
// we don't want to call this on `arg.getFullyConverted()` as we want
// to detect non-trivial range analysis without taking into account up-casting
reason = getEstimationReasonForIntegralExpression(arg)
) and
len = valueBasedBound.minimum(typeBasedBound)
)
or
this.getConversionChar(n).toLowerCase() = "x" and
// e.g. "12345678"
exists(int baseLen, int typeBasedBound, int valueBasedBound |
typeBasedBound =
min(int digits |
digits = 2 * this.getIntegralDisplayType(n).getSize()
exists(int sizeBits |
sizeBits =
min(int bits |
bits = this.getIntegralDisplayType(n).getSize() * 8
or
exists(IntegralType t |
t = this.getUse().getConversionArgument(n).getType().getUnderlyingType()
|
t.isUnsigned() and
digits = 2 * t.getSize()
t.isSigned() and bits = t.getSize() * 8
)
) and
exists(Expr arg, float lower, float upper, float typeLower, float typeUpper |
arg = this.getUse().getConversionArgument(n) and
lower = lowerBound(arg.getFullyConverted()) and
upper = upperBound(arg.getFullyConverted()) and
typeLower = exprMinVal(arg.getFullyConverted()) and
typeUpper = exprMaxVal(arg.getFullyConverted())
|
valueBasedBound =
lengthInBase16(max(float cand |
// If lower can be negative we use `(unsigned)-1` as the candidate value.
lower < 0 and
cand = 2.pow(any(IntType t | t.isUnsigned()).getSize() * 8)
or
cand = upper
)) and
(
if lower > typeLower or upper < typeUpper
then reason = TValueFlowAnalysis()
else reason = TTypeBoundsAnalysis()
)
) and
baseLen = valueBasedBound.minimum(typeBasedBound) and
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
len = 1 + ((sizeBits - 1) / 10.0.log2()).ceil()
// this calculation is as %u (below) only we take out the sign bit (- 1) and allow a whole
// character for it to be expressed as '-'.
)
or
this.getConversionChar(n).toLowerCase() = "u" and
// e.g. 2^32 - 1 = "4294967295"
exists(int sizeBits |
sizeBits =
min(int bits |
bits = this.getIntegralDisplayType(n).getSize() * 8
or
exists(IntegralType t |
t = this.getUse().getConversionArgument(n).getType().getUnderlyingType()
|
t.isUnsigned() and bits = t.getSize() * 8
)
) and
len = (sizeBits / 10.0.log2()).ceil()
// convert the size from bits to decimal characters, and round up as you can't have
// fractional characters (10.0.log2() is the number of bits expressed per decimal character)
)
or
this.getConversionChar(n).toLowerCase() = "x" and
// e.g. "12345678"
exists(int sizeBytes, int baseLen |
sizeBytes =
min(int bytes |
bytes = this.getIntegralDisplayType(n).getSize()
or
exists(IntegralType t |
t = this.getUse().getConversionArgument(n).getType().getUnderlyingType()
|
t.isUnsigned() and bytes = t.getSize()
)
) and
baseLen = sizeBytes * 2 and
(
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
)
)
or
this.getConversionChar(n).toLowerCase() = "p" and
@@ -1312,8 +1106,7 @@ class FormatLiteral extends Literal {
(
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
)
) and
reason = TValueFlowAnalysis()
)
or
this.getConversionChar(n).toLowerCase() = "o" and
// e.g. 2^32 - 1 = "37777777777"
@@ -1332,16 +1125,14 @@ class FormatLiteral extends Literal {
(
if this.hasAlternateFlag(n) then len = 1 + baseLen else len = baseLen // "0"
)
) and
reason = TTypeBoundsAnalysis()
)
or
this.getConversionChar(n).toLowerCase() = "s" and
len =
min(int v |
v = this.getPrecision(n) or
v = this.getUse().getFormatArgument(n).(AnalysedString).getMaxLength() - 1 // (don't count null terminator)
) and
reason = TValueFlowAnalysis()
)
)
)
}
@@ -1353,19 +1144,10 @@ class FormatLiteral extends Literal {
* determining whether a buffer overflow is caused by long float to string
* conversions.
*/
int getMaxConvertedLengthLimited(int n) { result = max(this.getMaxConvertedLengthLimited(n, _)) }
/**
* Gets the maximum length of the string that can be produced by the nth
* conversion specifier of this format string, specifying the reason for the
* estimation, except that float to string conversions are assumed to be 8
* characters. This is helpful for determining whether a buffer overflow is
* caused by long float to string conversions.
*/
int getMaxConvertedLengthLimited(int n, BufferWriteEstimationReason reason) {
int getMaxConvertedLengthLimited(int n) {
if this.getConversionChar(n).toLowerCase() = "f"
then result = this.getMaxConvertedLength(n, reason).minimum(8)
else result = this.getMaxConvertedLength(n, reason)
then result = this.getMaxConvertedLength(n).minimum(8)
else result = this.getMaxConvertedLength(n)
}
/**
@@ -1405,35 +1187,29 @@ class FormatLiteral extends Literal {
)
}
private int getMaxConvertedLengthAfter(int n, BufferWriteEstimationReason reason) {
private int getMaxConvertedLengthAfter(int n) {
if n = this.getNumConvSpec()
then result = this.getConstantSuffix().length() + 1 and reason = TValueFlowAnalysis()
then result = this.getConstantSuffix().length() + 1
else
exists(BufferWriteEstimationReason headReason, BufferWriteEstimationReason tailReason |
result =
this.getConstantPart(n).length() + this.getMaxConvertedLength(n, headReason) +
this.getMaxConvertedLengthAfter(n + 1, tailReason) and
reason = headReason.combineWith(tailReason)
)
result =
this.getConstantPart(n).length() + this.getMaxConvertedLength(n) +
this.getMaxConvertedLengthAfter(n + 1)
}
private int getMaxConvertedLengthAfterLimited(int n, BufferWriteEstimationReason reason) {
private int getMaxConvertedLengthAfterLimited(int n) {
if n = this.getNumConvSpec()
then result = this.getConstantSuffix().length() + 1 and reason = TValueFlowAnalysis()
then result = this.getConstantSuffix().length() + 1
else
exists(BufferWriteEstimationReason headReason, BufferWriteEstimationReason tailReason |
result =
this.getConstantPart(n).length() + this.getMaxConvertedLengthLimited(n, headReason) +
this.getMaxConvertedLengthAfterLimited(n + 1, tailReason) and
reason = headReason.combineWith(tailReason)
)
result =
this.getConstantPart(n).length() + this.getMaxConvertedLengthLimited(n) +
this.getMaxConvertedLengthAfterLimited(n + 1)
}
/**
* Gets the maximum length of the string that can be produced by this format
* string. Has no result if this cannot be determined.
*/
int getMaxConvertedLength() { result = this.getMaxConvertedLengthAfter(0, _) }
int getMaxConvertedLength() { result = this.getMaxConvertedLengthAfter(0) }
/**
* Gets the maximum length of the string that can be produced by this format
@@ -1441,24 +1217,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) }
}

View File

@@ -29,7 +29,7 @@ class GuardCondition extends Expr {
exists(IRGuardCondition ir | this = ir.getUnconvertedResultExpression())
or
// no binary operators in the IR
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
exists(GuardCondition gc | this.(BinaryLogicalOperation).getAnOperand() = gc)
or
// the IR short-circuits if(!x)
// don't produce a guard condition for `y = !x` and other non-short-circuited cases
@@ -98,7 +98,7 @@ class GuardCondition extends Expr {
*/
private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
GuardConditionFromBinaryLogicalOperator() {
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
exists(GuardCondition gc | this.(BinaryLogicalOperation).getAnOperand() = gc)
}
override predicate controls(BasicBlock controlled, boolean testIsTrue) {

View File

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

View File

@@ -626,9 +626,9 @@ library class ExprEvaluator extends int {
// All assignments must have the same int value
result =
unique(Expr value |
value = v.getAnAssignedValue() and not this.ignoreVariableAssignment(e, v, value)
value = v.getAnAssignedValue() and not ignoreVariableAssignment(e, v, value)
|
this.getValueInternalNonSubExpr(value)
getValueInternalNonSubExpr(value)
)
)
}

View File

@@ -1,6 +1,4 @@
private import cpp
private import semmle.code.cpp.dataflow.internal.DataFlowPrivate
private import semmle.code.cpp.dataflow.internal.DataFlowUtil
/**
* Gets a function that might be called by `call`.
@@ -65,17 +63,3 @@ predicate mayBenefitFromCallContext(Call call, Function f) { none() }
* restricted to those `call`s for which a context might make a difference.
*/
Function viableImplInCallContext(Call call, Call ctx) { none() }
/** A parameter position represented by an integer. */
class ParameterPosition extends int {
ParameterPosition() { any(ParameterNode p).isParameterOf(_, this) }
}
/** An argument position represented by an integer. */
class ArgumentPosition extends int {
ArgumentPosition() { any(ArgumentNode a).argumentOf(_, this) }
}
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
pragma[inline]
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -3,17 +3,6 @@ private import DataFlowImplSpecific::Public
import Cached
module DataFlowImplCommonPublic {
/** A state value to track during data flow. */
class FlowState = string;
/**
* The default state, which is used when the state is unspecified for a source
* or a sink.
*/
class FlowStateEmpty extends FlowState {
FlowStateEmpty() { this = "" }
}
private newtype TFlowFeature =
TFeatureHasSourceCallContext() or
TFeatureHasSinkCallContext() or
@@ -73,18 +62,6 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
tupleLimit = 1000
}
/**
* Holds if `arg` is an argument of `call` with an argument position that matches
* parameter position `ppos`.
*/
pragma[noinline]
predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) {
exists(ArgumentPosition apos |
arg.argumentOf(call, apos) and
parameterMatch(ppos, apos)
)
}
/**
* Provides a simple data-flow analysis for resolving lambda calls. The analysis
* currently excludes read-steps, store-steps, and flow-through.
@@ -94,27 +71,25 @@ predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPositio
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
*/
private module LambdaFlow {
pragma[noinline]
private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) {
p.isParameterOf(viableCallable(call), ppos)
private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) {
p.isParameterOf(viableCallable(call), i)
}
pragma[noinline]
private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) {
p.isParameterOf(viableCallableLambda(call, _), ppos)
private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) {
p.isParameterOf(viableCallableLambda(call, _), i)
}
private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
exists(ParameterPosition ppos |
viableParamNonLambda(call, ppos, p) and
argumentPositionMatch(call, arg, ppos)
exists(int i |
viableParamNonLambda(call, i, p) and
arg.argumentOf(call, i)
)
}
private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
exists(ParameterPosition ppos |
viableParamLambda(call, ppos, p) and
argumentPositionMatch(call, arg, ppos)
exists(int i |
viableParamLambda(call, i, p) and
arg.argumentOf(call, i)
)
}
@@ -347,7 +322,7 @@ private module Cached {
or
exists(ArgNode arg |
result.(PostUpdateNode).getPreUpdateNode() = arg and
arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition())
arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition())
)
}
@@ -355,7 +330,7 @@ private module Cached {
predicate returnNodeExt(Node n, ReturnKindExt k) {
k = TValueReturn(n.(ReturnNode).getKind())
or
exists(ParamNode p, ParameterPosition pos |
exists(ParamNode p, int pos |
parameterValueFlowsToPreUpdate(p, n) and
p.isParameterOf(_, pos) and
k = TParamUpdate(pos)
@@ -377,13 +352,11 @@ private module Cached {
}
cached
predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) {
isParameterNode(p, c, pos)
}
predicate parameterNode(Node p, DataFlowCallable c, int pos) { isParameterNode(p, c, pos) }
cached
predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) {
isArgumentNode(n, call, pos)
predicate argumentNode(Node n, DataFlowCall call, int pos) {
n.(ArgumentNode).argumentOf(call, pos)
}
/**
@@ -401,12 +374,12 @@ private module Cached {
}
/**
* Holds if `p` is the parameter of a viable dispatch target of `call`,
* and `p` has position `ppos`.
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
*/
pragma[nomagic]
private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) {
p.isParameterOf(viableCallableExt(call), ppos)
private predicate viableParam(DataFlowCall call, int i, ParamNode p) {
p.isParameterOf(viableCallableExt(call), i)
}
/**
@@ -415,9 +388,9 @@ private module Cached {
*/
cached
predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) {
exists(ParameterPosition ppos |
viableParam(call, ppos, p) and
argumentPositionMatch(call, arg, ppos) and
exists(int i |
viableParam(call, i, p) and
arg.argumentOf(call, i) and
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p))
)
}
@@ -889,7 +862,7 @@ private module Cached {
cached
newtype TReturnKindExt =
TValueReturn(ReturnKind kind) or
TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) }
TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) }
cached
newtype TBooleanOption =
@@ -1081,9 +1054,9 @@ class ParamNode extends Node {
/**
* Holds if this node is the parameter of callable `c` at the specified
* position.
* (zero-based) position.
*/
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) }
predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) }
}
/** A data-flow node that represents a call argument. */
@@ -1091,9 +1064,7 @@ class ArgNode extends Node {
ArgNode() { argumentNode(this, _, _) }
/** Holds if this argument occurs at the given position in the given call. */
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
argumentNode(this, call, pos)
}
final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) }
}
/**
@@ -1139,14 +1110,11 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
}
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
private ParameterPosition pos;
private int pos;
ParamUpdateReturnKind() { this = TParamUpdate(pos) }
ParameterPosition getPosition() { result = pos }
pragma[nomagic]
ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) }
int getPosition() { result = pos }
override string toString() { result = "param update " + pos }
}

View File

@@ -9,31 +9,6 @@ private import tainttracking1.TaintTrackingParameter::Private
private import tainttracking1.TaintTrackingParameter::Public
module Consistency {
private newtype TConsistencyConfiguration = MkConsistencyConfiguration()
/** A class for configuring the consistency queries. */
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 {
RelevantNode() {
this instanceof ArgumentNode or
@@ -58,7 +33,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 +53,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 +63,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,13 +159,12 @@ 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."
}
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
not hasPost(n) and
not any(ConsistencyConfiguration c).argHasPostUpdateExclude(n) and
not isImmutableOrUnobservable(n) and
msg = "ArgumentNode is missing PostUpdateNode."
}
@@ -206,7 +177,6 @@ module Consistency {
isPostUpdateNode(n) and
not clearsContent(n, _) and
simpleLocalFlowStep(_, n) and
not any(ConsistencyConfiguration c).postWithInFlowExclude(n) and
msg = "PostUpdateNode should not be the target of local flow."
}
}

View File

@@ -2,20 +2,12 @@ private import cpp
private import DataFlowUtil
private import DataFlowDispatch
private import FlowVar
private import DataFlowImplConsistency
/** Gets the callable in which this node occurs. */
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
p.isParameterOf(c, pos)
}
/** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */
predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos) {
arg.argumentOf(c, pos)
}
predicate isParameterNode(ParameterNode p, DataFlowCallable c, int pos) { p.isParameterOf(c, pos) }
/** Gets the instance argument of a non-static call. */
private Node getInstanceArgument(Call call) {
@@ -48,7 +40,7 @@ private class Argument extends Expr {
*/
class ArgumentNode extends Node {
ArgumentNode() {
this.asExpr() instanceof Argument or
exists(Argument arg | this.asExpr() = arg) or
this = getInstanceArgument(_)
}
@@ -267,6 +259,27 @@ class Unit extends TUnit {
string toString() { result = "unit" }
}
/**
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
* modified or its modification cannot be observed, for example if it is a
* freshly created object that is not saved in a variable.
*
* This predicate is only used for consistency checks.
*/
predicate isImmutableOrUnobservable(Node n) {
// Is the null pointer (or something that's not really a pointer)
exists(n.asExpr().getValue())
or
// Isn't a pointer or is a pointer to const
forall(DerivedType dt | dt = n.asExpr().getActualType() |
dt.getBaseType().isConst()
or
dt.getBaseType() instanceof RoutineType
)
// The above list of cases isn't exhaustive, but it narrows down the
// consistency alerts enough that most of them are interesting.
}
/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { none() }
@@ -289,19 +302,3 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
* by default as a heuristic.
*/
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
override predicate argHasPostUpdateExclude(ArgumentNode n) {
// Is the null pointer (or something that's not really a pointer)
exists(n.asExpr().getValue())
or
// Isn't a pointer or is a pointer to const
forall(DerivedType dt | dt = n.asExpr().getActualType() |
dt.getBaseType().isConst()
or
dt.getBaseType() instanceof RoutineType
)
// The above list of cases isn't exhaustive, but it narrows down the
// consistency alerts enough that most of them are interesting.
}
}

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