mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Merge branch 'main' into more-shell-taint
This commit is contained in:
26
.github/actions/find-latest-bundle/action.yml
vendored
26
.github/actions/find-latest-bundle/action.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: Find Latest CodeQL Bundle
|
||||
description: Finds the URL of the latest released version of the CodeQL bundle.
|
||||
outputs:
|
||||
url:
|
||||
description: The download URL of the latest CodeQL bundle release
|
||||
value: ${{ steps.find-latest.outputs.url }}
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Find Latest Release
|
||||
id: find-latest
|
||||
shell: pwsh
|
||||
run: |
|
||||
$Latest = gh release list --repo github/codeql-action --exclude-drafts --limit 1000 |
|
||||
ForEach-Object { $C = $_ -split "`t"; return @{ type = $C[1]; tag = $C[2]; } } |
|
||||
Where-Object { $_.type -eq 'Latest' }
|
||||
|
||||
$Tag = $Latest.tag
|
||||
if ($Tag -eq '') {
|
||||
throw 'Failed to find latest bundle release.'
|
||||
}
|
||||
|
||||
Write-Output "Latest bundle tag is '${Tag}'."
|
||||
"url=https://github.com/github/codeql-action/releases/download/${Tag}/codeql-bundle-linux64.tar.gz" >> $env:GITHUB_OUTPUT
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
4
.github/workflows/codeql-analysis.yml
vendored
4
.github/workflows/codeql-analysis.yml
vendored
@@ -28,9 +28,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v2
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.202
|
||||
dotnet-version: 7.0.102
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
10
.github/workflows/csharp-qltest.yml
vendored
10
.github/workflows/csharp-qltest.yml
vendored
@@ -77,10 +77,10 @@ jobs:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.202
|
||||
dotnet-version: 7.0.102
|
||||
- name: Extractor unit tests
|
||||
run: |
|
||||
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/extractor/Semmle.Util.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/extractor/Semmle.Extraction.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=6.0.4 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/extractor/Semmle.Util.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/extractor/Semmle.Extraction.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/csharp/autobuilder/Semmle.Autobuild.CSharp.Tests"
|
||||
dotnet test -p:RuntimeFrameworkVersion=7.0.2 "${{ github.workspace }}/cpp/autobuilder/Semmle.Autobuild.Cpp.Tests"
|
||||
|
||||
8
.github/workflows/go-tests-other-os.yml
vendored
8
.github/workflows/go-tests-other-os.yml
vendored
@@ -12,10 +12,10 @@ jobs:
|
||||
name: Test MacOS
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
@@ -47,10 +47,10 @@ jobs:
|
||||
name: Test Windows
|
||||
runs-on: windows-latest-xl
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
|
||||
4
.github/workflows/go-tests.yml
vendored
4
.github/workflows/go-tests.yml
vendored
@@ -20,10 +20,10 @@ jobs:
|
||||
name: Test Linux (Ubuntu)
|
||||
runs-on: ubuntu-latest-xl
|
||||
steps:
|
||||
- name: Set up Go 1.19
|
||||
- name: Set up Go 1.20
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.19
|
||||
go-version: 1.20.0
|
||||
id: go
|
||||
|
||||
- name: Check out code
|
||||
|
||||
138
.github/workflows/ql-for-ql-build.yml
vendored
138
.github/workflows/ql-for-ql-build.yml
vendored
@@ -22,66 +22,22 @@ jobs:
|
||||
steps:
|
||||
### Build the queries ###
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find latest bundle
|
||||
id: find-latest-bundle
|
||||
uses: ./.github/actions/find-latest-bundle
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
tools: ${{ steps.find-latest-bundle.outputs.url }}
|
||||
- name: Get CodeQL version
|
||||
id: get-codeql-version
|
||||
run: |
|
||||
echo "version=$("${CODEQL}" --version | head -n 1 | rev | cut -d " " -f 1 | rev)" >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- name: Cache entire pack
|
||||
id: cache-pack
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ runner.temp }}/pack
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-pack-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}-${{ hashFiles('ql/**/*.ql*') }}-${{ hashFiles('ql/**/qlpack.yml') }}-${{ hashFiles('ql/ql/src/ql.dbscheme*') }}-${{ steps.get-codeql-version.outputs.version }}--${{ hashFiles('.github/workflows/ql-for-ql-build.yml') }}
|
||||
- name: Cache queries
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
id: cache-queries
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ${{ runner.temp }}/queries
|
||||
key: queries-${{ hashFiles('ql/**/*.ql*') }}-${{ hashFiles('ql/**/qlpack.yml') }}-${{ hashFiles('ql/ql/src/ql.dbscheme*') }}-${{ steps.get-codeql-version.outputs.version }}--${{ hashFiles('.github/workflows/ql-for-ql-build.yml') }}
|
||||
- name: Build query pack
|
||||
if: steps.cache-queries.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd ql/ql/src
|
||||
"${CODEQL}" pack create -j 16
|
||||
mv .codeql/pack/codeql/ql/0.0.0 ${{ runner.temp }}/queries
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Move cache queries to pack
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cp -r ${{ runner.temp }}/queries ${{ runner.temp }}/pack
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
|
||||
### Build the extractor ###
|
||||
- name: Cache entire extractor
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
id: cache-extractor
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
ql/target/release/ql-autobuilder
|
||||
ql/target/release/ql-autobuilder.exe
|
||||
ql/target/release/ql-extractor
|
||||
ql/target/release/ql-extractor.exe
|
||||
path: ql/extractor-pack/
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
|
||||
- name: Cache cargo
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -89,77 +45,31 @@ jobs:
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-rust-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo fmt --all -- --check
|
||||
- name: Build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo build --verbose
|
||||
- name: Run tests
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo test --verbose
|
||||
- name: Release build
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: cd ql; cargo build --release
|
||||
- name: Generate dbscheme
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true' && steps.cache-pack.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
|
||||
|
||||
### Package the queries and extractor ###
|
||||
- name: Package pack
|
||||
if: steps.cache-pack.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cp -r ql/codeql-extractor.yml ql/tools ql/ql/src/ql.dbscheme.stats ${PACK}/
|
||||
mkdir -p ${PACK}/tools/linux64
|
||||
cp ql/target/release/ql-autobuilder ${PACK}/tools/linux64/autobuilder
|
||||
cp ql/target/release/ql-extractor ${PACK}/tools/linux64/extractor
|
||||
chmod +x ${PACK}/tools/linux64/autobuilder
|
||||
chmod +x ${PACK}/tools/linux64/extractor
|
||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
||||
run: cd ql; ./scripts/create-extractor-pack.sh
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
### Run the analysis ###
|
||||
- name: Hack codeql-action options
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: run-ql-for-ql
|
||||
- name: Make database and analyze
|
||||
run: |
|
||||
JSON=$(jq -nc --arg pack "${PACK}" '.database."run-queries"=["--search-path", $pack] | .resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .resolve.languages=["--search-path", $pack] | .database.init=["--search-path", $pack]')
|
||||
echo "CODEQL_ACTION_EXTRA_OPTIONS=${JSON}" >> ${GITHUB_ENV}
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
- name: Create CodeQL config file
|
||||
run: |
|
||||
echo "paths-ignore:" >> ${CONF}
|
||||
echo " - ql/ql/test" >> ${CONF}
|
||||
echo " - \"*/ql/lib/upgrades/\"" >> ${CONF}
|
||||
echo "disable-default-queries: true" >> ${CONF}
|
||||
echo "queries:" >> ${CONF}
|
||||
echo " - uses: ./ql/ql/src/codeql-suites/ql-code-scanning.qls" >> ${CONF}
|
||||
echo "Config file: "
|
||||
cat ${CONF}
|
||||
env:
|
||||
CONF: ./ql-for-ql-config.yml
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
${CODEQL} database create -l=ql --search-path ql/extractor-pack ${DB}
|
||||
${CODEQL} database analyze -j0 --format=sarif-latest --output=ql-for-ql.sarif ${DB} ql/ql/src/codeql-suites/ql-code-scanning.qls --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}"
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
DB: ${{ runner.temp }}/DB
|
||||
LGTM_INDEX_FILTERS: |
|
||||
exclude:ql/ql/test
|
||||
exclude:*/ql/lib/upgrades/
|
||||
- name: Upload sarif to code-scanning
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
languages: ql
|
||||
db-location: ${{ runner.temp }}/db
|
||||
config-file: ./ql-for-ql-config.yml
|
||||
tools: ${{ steps.find-latest-bundle.outputs.url }}
|
||||
- name: Move pack queries
|
||||
run: |
|
||||
cp -r ${PACK}/queries ql/ql/src
|
||||
env:
|
||||
PACK: ${{ runner.temp }}/pack
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
with:
|
||||
category: "ql-for-ql"
|
||||
- name: Copy sarif file to CWD
|
||||
run: cp ../results/ql.sarif ./ql-for-ql.sarif
|
||||
- name: Fixup the $scema in sarif # Until https://github.com/microsoft/sarif-vscode-extension/pull/436/ is part in a stable release
|
||||
run: |
|
||||
sed -i 's/\$schema.*/\$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0",/' ql-for-ql.sarif
|
||||
sarif_file: ql-for-ql.sarif
|
||||
category: ql-for-ql
|
||||
- name: Sarif as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
|
||||
8
.github/workflows/ql-for-ql-tests.yml
vendored
8
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -6,11 +6,13 @@ on:
|
||||
paths:
|
||||
- "ql/**"
|
||||
- codeql-workspace.yml
|
||||
- .github/workflows/ql-for-ql-tests.yml
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "ql/**"
|
||||
- codeql-workspace.yml
|
||||
- .github/workflows/ql-for-ql-tests.yml
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -22,7 +24,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@45955cb1830b640e2c1603ad72ad542a49d47b96
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
@@ -34,6 +36,8 @@ jobs:
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/rust-toolchain.toml', 'ql/**/Cargo.lock') }}
|
||||
- name: Check formatting
|
||||
run: cd ql; cargo fmt --all -- --check
|
||||
- name: Build extractor
|
||||
run: |
|
||||
cd ql;
|
||||
@@ -65,7 +69,7 @@ jobs:
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplForRegExp.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForHttpClientLibraries.qll",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Autobuild.Cpp</AssemblyName>
|
||||
<RootNamespace>Semmle.Autobuild.Cpp</RootNamespace>
|
||||
<ApplicationIcon />
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.5.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
cpp/ql/lib/change-notes/released/0.5.2.md
Normal file
3
cpp/ql/lib/change-notes/released/0.5.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.1
|
||||
lastReleaseVersion: 0.5.2
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -707,8 +707,8 @@ private module Cached {
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
cached
|
||||
DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
result = viableCallable(call)
|
||||
or
|
||||
@@ -1391,6 +1391,9 @@ class TypedContentApprox extends MkTypedContentApprox {
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the content. */
|
||||
ContentApprox getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
@@ -1408,6 +1411,8 @@ abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
private import cpp
|
||||
private import experimental.semmle.code.cpp.models.interfaces.SimpleRangeAnalysisExpr
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
float evaluateConstantExpr(Expr e) {
|
||||
result = e.getValue().toFloat()
|
||||
or
|
||||
// This handles when a constant value is put into a variable
|
||||
// and the variable is used later
|
||||
exists(SsaDefinition defn, StackVariable sv |
|
||||
defn.getAUse(sv) = e and
|
||||
result = defn.getDefiningValue(sv).getValue().toFloat()
|
||||
)
|
||||
}
|
||||
|
||||
// If the constant right operand is negative or is greater than or equal to the number of
|
||||
// bits in the left operands type, then the result is undefined (except on the IA-32
|
||||
// architecture where the shift value is masked with 0b00011111, but we can't
|
||||
// assume the architecture).
|
||||
bindingset[val]
|
||||
private predicate isValidShiftExprShift(float val, Expr l) {
|
||||
val >= 0 and
|
||||
// We use getFullyConverted because the spec says to use the *promoted* left operand
|
||||
val < (l.getFullyConverted().getUnderlyingType().getSize() * 8)
|
||||
}
|
||||
|
||||
bindingset[val, shift, max_val]
|
||||
private predicate canLShiftOverflow(int val, int shift, int max_val) {
|
||||
// val << shift = val * 2^shift > max_val => val > max_val/2^shift = max_val >> b
|
||||
val > max_val.bitShiftRight(shift)
|
||||
}
|
||||
|
||||
/**
|
||||
* A range analysis expression consisting of the `>>` or `>>=` operator when at least
|
||||
* one operand is a constant (and if the right operand is a constant, it must be "valid"
|
||||
* (see `isValidShiftExprShift`)). When handling any undefined behavior, it leaves the
|
||||
* values unconstrained. From the C++ standard: "The behavior is undefined if the right
|
||||
* operand is negative, or greater than or equal to the length in bits of the promoted
|
||||
* left operand. The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an
|
||||
* unsigned type or if E1 has a signed type and a non-negative value, the value of the
|
||||
* result is the integral part of the quotient of E1/2^E2. If E1 has a signed type and a
|
||||
* negative value, the resulting value is implementation-defined."
|
||||
*/
|
||||
class ConstantRShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
/**
|
||||
* Holds for `a >> b` or `a >>= b` in one of the following two cases:
|
||||
* 1. `a` is a constant and `b` is not
|
||||
* 2. `b` is constant
|
||||
*
|
||||
* We don't handle the case where `a` and `b` are both non-constant values.
|
||||
*/
|
||||
ConstantRShiftExprRange() {
|
||||
getUnspecifiedType() instanceof IntegralType and
|
||||
exists(Expr l, Expr r |
|
||||
l = this.(RShiftExpr).getLeftOperand() and
|
||||
r = this.(RShiftExpr).getRightOperand()
|
||||
or
|
||||
l = this.(AssignRShiftExpr).getLValue() and
|
||||
r = this.(AssignRShiftExpr).getRValue()
|
||||
|
|
||||
l.getUnspecifiedType() instanceof IntegralType and
|
||||
r.getUnspecifiedType() instanceof IntegralType and
|
||||
(
|
||||
// If the left operand is a constant, verify that the right operand is not a constant
|
||||
exists(evaluateConstantExpr(l)) and not exists(evaluateConstantExpr(r))
|
||||
or
|
||||
// If the right operand is a constant, check if it is a valid shift expression
|
||||
exists(float constROp |
|
||||
constROp = evaluateConstantExpr(r) and isValidShiftExprShift(constROp, l)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Expr getLeftOperand() {
|
||||
result = this.(RShiftExpr).getLeftOperand() or
|
||||
result = this.(AssignRShiftExpr).getLValue()
|
||||
}
|
||||
|
||||
Expr getRightOperand() {
|
||||
result = this.(RShiftExpr).getRightOperand() or
|
||||
result = this.(AssignRShiftExpr).getRValue()
|
||||
}
|
||||
|
||||
override float getLowerBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
if
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
// and a negative shift is implementation defined, so we set the result
|
||||
// to the minimum value
|
||||
result = exprMinVal(this)
|
||||
else
|
||||
// We can get the smallest value by shifting the smallest bound by the largest bound
|
||||
result = lLower.bitShiftRight(rUpper)
|
||||
)
|
||||
}
|
||||
|
||||
override float getUpperBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
if
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
// and a negative shift is implementation defined, so we set the result
|
||||
// to the maximum value
|
||||
result = exprMaxVal(this)
|
||||
else
|
||||
// We can get the largest value by shifting the largest bound by the smallest bound
|
||||
result = lUpper.bitShiftRight(rLower)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate dependsOnChild(Expr child) {
|
||||
child = getLeftOperand() or child = getRightOperand()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A range analysis expression consisting of the `<<` or `<<=` operator when at least
|
||||
* one operand is a constant (and if the right operand is a constant, it must be "valid"
|
||||
* (see `isValidShiftExprShift`)). When handling any undefined behavior, it leaves the
|
||||
* values unconstrained. From the C++ standard: "The behavior is undefined if the right
|
||||
* operand is negative, or greater than or equal to the length in bits of the promoted left operand.
|
||||
* The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1
|
||||
* has an unsigned type, the value of the result is E1 x 2 E2, reduced modulo one more than the
|
||||
* maximum value representable in the result type. Otherwise, if E1 has a signed type and
|
||||
* non-negative value, and E1 x 2 E2 is representable in the corresponding unsigned type of the
|
||||
* result type, then that value, converted to the result type, is the resulting value; otherwise,
|
||||
* the behavior is undefined."
|
||||
*/
|
||||
class ConstantLShiftExprRange extends SimpleRangeAnalysisExpr {
|
||||
/**
|
||||
* Holds for `a << b` or `a <<= b` in one of the following two cases:
|
||||
* 1. `a` is a constant and `b` is not
|
||||
* 2. `b` is constant
|
||||
*
|
||||
* We don't handle the case where `a` and `b` are both non-constant values.
|
||||
*/
|
||||
ConstantLShiftExprRange() {
|
||||
getUnspecifiedType() instanceof IntegralType and
|
||||
exists(Expr l, Expr r |
|
||||
l = this.(LShiftExpr).getLeftOperand() and
|
||||
r = this.(LShiftExpr).getRightOperand()
|
||||
or
|
||||
l = this.(AssignLShiftExpr).getLValue() and
|
||||
r = this.(AssignLShiftExpr).getRValue()
|
||||
|
|
||||
l.getUnspecifiedType() instanceof IntegralType and
|
||||
r.getUnspecifiedType() instanceof IntegralType and
|
||||
(
|
||||
// If the left operand is a constant, verify that the right operand is not a constant
|
||||
exists(evaluateConstantExpr(l)) and not exists(evaluateConstantExpr(r))
|
||||
or
|
||||
// If the right operand is a constant, check if it is a valid shift expression
|
||||
exists(float constROp |
|
||||
constROp = evaluateConstantExpr(r) and isValidShiftExprShift(constROp, l)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Expr getLeftOperand() {
|
||||
result = this.(LShiftExpr).getLeftOperand() or
|
||||
result = this.(AssignLShiftExpr).getLValue()
|
||||
}
|
||||
|
||||
Expr getRightOperand() {
|
||||
result = this.(LShiftExpr).getRightOperand() or
|
||||
result = this.(AssignLShiftExpr).getRValue()
|
||||
}
|
||||
|
||||
override float getLowerBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
if
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
// and a negative shift is undefined, so we set to the minimum value
|
||||
result = exprMinVal(this)
|
||||
else
|
||||
// If we have `0b01010000 << [0, 2]`, the max value for 8 bits is 0b10100000
|
||||
// (a shift of 1) but doing a shift by the upper bound would give 0b01000000.
|
||||
// So if the left shift operation causes an overflow, we just assume the max value
|
||||
// If necessary, we may be able to improve this bound in the future
|
||||
if canLShiftOverflow(lUpper, rUpper, exprMaxVal(this))
|
||||
then result = exprMinVal(this)
|
||||
else result = lLower.bitShiftLeft(rLower)
|
||||
)
|
||||
}
|
||||
|
||||
override float getUpperBounds() {
|
||||
exists(int lLower, int lUpper, int rLower, int rUpper |
|
||||
lLower = getFullyConvertedLowerBounds(getLeftOperand()) and
|
||||
lUpper = getFullyConvertedUpperBounds(getLeftOperand()) and
|
||||
rLower = getFullyConvertedLowerBounds(getRightOperand()) and
|
||||
rUpper = getFullyConvertedUpperBounds(getRightOperand()) and
|
||||
lLower <= lUpper and
|
||||
rLower <= rUpper
|
||||
|
|
||||
if
|
||||
lLower < 0
|
||||
or
|
||||
not (
|
||||
isValidShiftExprShift(rLower, getLeftOperand()) and
|
||||
isValidShiftExprShift(rUpper, getLeftOperand())
|
||||
)
|
||||
then
|
||||
// We don't want to deal with shifting negative numbers at the moment,
|
||||
// and a negative shift is undefined, so we set it to the maximum value
|
||||
result = exprMaxVal(this)
|
||||
else
|
||||
// If we have `0b01010000 << [0, 2]`, the max value for 8 bits is 0b10100000
|
||||
// (a shift of 1) but doing a shift by the upper bound would give 0b01000000.
|
||||
// So if the left shift operation causes an overflow, we just assume the max value
|
||||
// If necessary, we may be able to improve this bound in the future
|
||||
if canLShiftOverflow(lUpper, rUpper, exprMaxVal(this))
|
||||
then result = exprMaxVal(this)
|
||||
else result = lUpper.bitShiftLeft(rUpper)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate dependsOnChild(Expr child) {
|
||||
child = getLeftOperand() or child = getRightOperand()
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.5.2-dev
|
||||
version: 0.5.3-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -707,8 +707,8 @@ private module Cached {
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
cached
|
||||
DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
result = viableCallable(call)
|
||||
or
|
||||
@@ -1391,6 +1391,9 @@ class TypedContentApprox extends MkTypedContentApprox {
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the content. */
|
||||
ContentApprox getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
@@ -1408,6 +1411,8 @@ abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -667,23 +667,78 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(NodeEx arg |
|
||||
fwdFlow(arg, _, config) and
|
||||
viableParamArgEx(_, node, arg) and
|
||||
cc = true and
|
||||
not fullBarrier(node, config)
|
||||
)
|
||||
fwdFlowIn(_, _, _, node, config) and
|
||||
cc = true
|
||||
or
|
||||
// flow out of a callable
|
||||
fwdFlowOut(_, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
fwdFlowOut(call, node, false, config) and
|
||||
cc = false
|
||||
or
|
||||
fwdFlowOutFromArg(call, node, config) and
|
||||
fwdFlowIsEntered(call, cc, config)
|
||||
)
|
||||
}
|
||||
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowIn(
|
||||
DataFlowCall call, NodeEx arg, Cc cc, ParamNodeEx p, Configuration config
|
||||
) {
|
||||
// call context cannot help reduce virtual dispatch
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
not fullBarrier(p, config) and
|
||||
(
|
||||
cc = false
|
||||
or
|
||||
cc = true and
|
||||
not reducedViableImplInCallContext(call, _, _)
|
||||
)
|
||||
or
|
||||
// call context may help reduce virtual dispatch
|
||||
exists(DataFlowCallable target |
|
||||
fwdFlowInReducedViableImplInSomeCallContext(call, arg, p, target, config) and
|
||||
target = viableImplInSomeFwdFlowCallContextExt(call, config) and
|
||||
cc = true
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
fwdFlowIn(call, _, cc, _, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowInReducedViableImplInSomeCallContext(
|
||||
DataFlowCall call, NodeEx arg, ParamNodeEx p, DataFlowCallable target, Configuration config
|
||||
) {
|
||||
fwdFlow(arg, true, config) and
|
||||
viableParamArgEx(call, p, arg) and
|
||||
reducedViableImplInCallContext(call, _, _) and
|
||||
target = p.getEnclosingCallable() and
|
||||
not fullBarrier(p, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference,
|
||||
* and to `ctx`s that are reachable in `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInSomeFwdFlowCallContextExt(
|
||||
DataFlowCall call, Configuration config
|
||||
) {
|
||||
exists(DataFlowCall ctx |
|
||||
fwdFlowIsEntered(ctx, _, config) and
|
||||
result = viableImplInCallContextExt(call, ctx)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -726,7 +781,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||
exists(ReturnPosition pos |
|
||||
fwdFlowReturnPosition(pos, cc, config) and
|
||||
@@ -740,17 +796,6 @@ private module Stage1 implements StageSig {
|
||||
fwdFlowOut(call, out, true, config)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an argument to `call` is reached in the flow covered by `fwdFlow`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) {
|
||||
exists(ArgNodeEx arg |
|
||||
fwdFlow(arg, cc, config) and
|
||||
viableParamArgEx(call, _, arg)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) {
|
||||
exists(NodeEx node1 |
|
||||
additionalLocalStateStep(node1, state1, _, state2, config) or
|
||||
@@ -817,13 +862,8 @@ private module Stage1 implements StageSig {
|
||||
)
|
||||
or
|
||||
// flow into a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowIn(call, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
revFlowIn(_, node, false, config) and
|
||||
toReturn = false
|
||||
or
|
||||
// flow out of a callable
|
||||
exists(ReturnPosition pos |
|
||||
@@ -831,6 +871,12 @@ private module Stage1 implements StageSig {
|
||||
node.(RetNodeEx).getReturnPosition() = pos and
|
||||
toReturn = true
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call |
|
||||
revFlowInToReturn(call, node, config) and
|
||||
revFlowIsReturned(call, toReturn, config)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -886,11 +932,11 @@ private module Stage1 implements StageSig {
|
||||
additional predicate viableParamArgNodeCandFwd1(
|
||||
DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config
|
||||
) {
|
||||
viableParamArgEx(call, p, arg) and
|
||||
fwdFlow(arg, config)
|
||||
fwdFlowIn(call, arg, _, p, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
// inline to reduce the number of iterations
|
||||
pragma[inline]
|
||||
private predicate revFlowIn(
|
||||
DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config
|
||||
) {
|
||||
@@ -1223,7 +1269,16 @@ private module MkStage<StageSig PrevStage> {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail);
|
||||
|
||||
Content getHeadContent(Ap ap);
|
||||
/**
|
||||
* An approximation of `Content` that corresponds to the precision level of
|
||||
* `Ap`, such that the mappings from both `Ap` and `Content` to this type
|
||||
* are functional.
|
||||
*/
|
||||
class ApHeadContent;
|
||||
|
||||
ApHeadContent getHeadContent(Ap ap);
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c);
|
||||
|
||||
class ApOption;
|
||||
|
||||
@@ -1471,34 +1526,32 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
}
|
||||
|
||||
private class ApNonNil instanceof Ap {
|
||||
pragma[nomagic]
|
||||
ApNonNil() { not this instanceof ApNil }
|
||||
|
||||
string toString() { result = "" }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead0(
|
||||
NodeEx node1, FlowState state, Cc cc, ParamNodeOption summaryCtx, ApOption argAp, ApNonNil ap,
|
||||
Configuration config
|
||||
private predicate readStepCand(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, _, _, config)
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
apc = projectToHeadContent(c)
|
||||
}
|
||||
|
||||
bindingset[ap, c]
|
||||
bindingset[node1, apc]
|
||||
pragma[inline_late]
|
||||
private predicate hasHeadContent(Ap ap, Content c) { getHeadContent(ap) = c }
|
||||
private predicate readStepCand0(
|
||||
NodeEx node1, ApHeadContent apc, Content c, NodeEx node2, Configuration config
|
||||
) {
|
||||
readStepCand(node1, apc, c, node2, config)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlowRead(
|
||||
Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc,
|
||||
ParamNodeOption summaryCtx, ApOption argAp, Configuration config
|
||||
) {
|
||||
fwdFlowRead0(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
PrevStage::readStepCand(node1, c, node2, config) and
|
||||
hasHeadContent(ap, c)
|
||||
exists(ApHeadContent apc |
|
||||
fwdFlow(node1, state, cc, summaryCtx, argAp, ap, config) and
|
||||
apc = getHeadContent(ap) and
|
||||
readStepCand0(node1, apc, c, node2, config)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -2072,8 +2125,12 @@ private module Stage2Param implements MkStage<Stage1>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
|
||||
|
||||
class ApHeadContent = Unit;
|
||||
|
||||
pragma[inline]
|
||||
Content getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
ApHeadContent getHeadContent(Ap ap) { exists(result) and ap = true }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { any() }
|
||||
|
||||
class ApOption = BooleanOption;
|
||||
|
||||
@@ -2337,8 +2394,12 @@ private module Stage3Param implements MkStage<Stage2>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getAHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = ContentApprox;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getAHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
predicate projectToHeadContent = getContentApprox/1;
|
||||
|
||||
class ApOption = ApproxAccessPathFrontOption;
|
||||
|
||||
@@ -2413,8 +2474,12 @@ private module Stage4Param implements MkStage<Stage3>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathFrontOption;
|
||||
|
||||
@@ -2743,8 +2808,12 @@ private module Stage5Param implements MkStage<Stage4>::StageParam {
|
||||
bindingset[tc, tail]
|
||||
Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
|
||||
|
||||
class ApHeadContent = Content;
|
||||
|
||||
pragma[noinline]
|
||||
Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
ApHeadContent getHeadContent(Ap ap) { result = ap.getHead().getContent() }
|
||||
|
||||
ApHeadContent projectToHeadContent(Content c) { result = c }
|
||||
|
||||
class ApOption = AccessPathApproxOption;
|
||||
|
||||
|
||||
@@ -707,8 +707,8 @@ private module Cached {
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
cached
|
||||
DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) {
|
||||
result = viableImplInCallContext(call, ctx) and
|
||||
result = viableCallable(call)
|
||||
or
|
||||
@@ -1391,6 +1391,9 @@ class TypedContentApprox extends MkTypedContentApprox {
|
||||
/** Gets a typed content approximated by this value. */
|
||||
TypedContent getATypedContent() { result = getATypedContent(this) }
|
||||
|
||||
/** Gets the content. */
|
||||
ContentApprox getContent() { result = c }
|
||||
|
||||
/** Gets the container type. */
|
||||
DataFlowType getContainerType() { result = t }
|
||||
|
||||
@@ -1408,6 +1411,8 @@ abstract class ApproxAccessPathFront extends TApproxAccessPathFront {
|
||||
|
||||
abstract boolean toBoolNonEmpty();
|
||||
|
||||
TypedContentApprox getHead() { this = TApproxFrontHead(result) }
|
||||
|
||||
pragma[nomagic]
|
||||
TypedContent getAHead() {
|
||||
exists(TypedContentApprox cont |
|
||||
|
||||
@@ -412,6 +412,9 @@ class CallTargetOperand extends RegisterOperand {
|
||||
*/
|
||||
class ArgumentOperand extends RegisterOperand {
|
||||
override ArgumentOperandTag tag;
|
||||
|
||||
/** Gets the `CallInstruction` for which this is an argument. */
|
||||
CallInstruction getCall() { result.getAnArgumentOperand() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -412,6 +412,9 @@ class CallTargetOperand extends RegisterOperand {
|
||||
*/
|
||||
class ArgumentOperand extends RegisterOperand {
|
||||
override ArgumentOperandTag tag;
|
||||
|
||||
/** Gets the `CallInstruction` for which this is an argument. */
|
||||
CallInstruction getCall() { result.getAnArgumentOperand() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -412,6 +412,9 @@ class CallTargetOperand extends RegisterOperand {
|
||||
*/
|
||||
class ArgumentOperand extends RegisterOperand {
|
||||
override ArgumentOperandTag tag;
|
||||
|
||||
/** Gets the `CallInstruction` for which this is an argument. */
|
||||
CallInstruction getCall() { result.getAnArgumentOperand() = this }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -107,130 +107,34 @@ private FunctionInput getIteratorArgumentInput(Operator op, int index) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member prefix `operator*` function for an iterator type.
|
||||
*/
|
||||
private class IteratorPointerDereferenceOperator extends Operator, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorPointerDereferenceOperator() {
|
||||
this.hasName("operator*") and
|
||||
iteratorInput = getIteratorArgumentInput(this, 0)
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = iteratorInput and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator++` or `operator--` function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and versions, use `IteratorCrementOperator`.
|
||||
*/
|
||||
private class IteratorCrementOperator extends Operator, DataFlowFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorCrementOperator() {
|
||||
class IteratorCrementNonMemberOperator extends Operator {
|
||||
IteratorCrementNonMemberOperator() {
|
||||
this.hasName(["operator++", "operator--"]) and
|
||||
iteratorInput = getIteratorArgumentInput(this, 0)
|
||||
}
|
||||
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = iteratorInput and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator+` function for an iterator type.
|
||||
*/
|
||||
private class IteratorAddOperator extends Operator, TaintFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorAddOperator() {
|
||||
this.hasName("operator+") and
|
||||
iteratorInput = getIteratorArgumentInput(this, [0, 1])
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = iteratorInput and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator-` function that takes a pointer difference type as its second argument.
|
||||
*/
|
||||
private class IteratorSubOperator extends Operator, TaintFunction {
|
||||
FunctionInput iteratorInput;
|
||||
|
||||
IteratorSubOperator() {
|
||||
this.hasName("operator-") and
|
||||
iteratorInput = getIteratorArgumentInput(this, 0) and
|
||||
this.getParameter(1).getUnspecifiedType() instanceof IntegralType // not an iterator difference
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = iteratorInput and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator+=` or `operator-=` function for an iterator type.
|
||||
*/
|
||||
class IteratorAssignArithmeticOperator extends Operator {
|
||||
IteratorAssignArithmeticOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
exists(getIteratorArgumentInput(this, 0))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorAssignArithmeticOperatorModel extends IteratorAssignArithmeticOperator,
|
||||
DataFlowFunction, TaintFunction {
|
||||
private class IteratorCrementNonMemberOperatorModel extends IteratorCrementNonMemberOperator,
|
||||
DataFlowFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
input = getIteratorArgumentInput(this, 0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
or
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
or
|
||||
// reverse flow from returned reference to the object referenced by the first parameter
|
||||
input.isReturnValueDeref() and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
(input.isParameter(1) or input.isParameterDeref(1)) and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A prefix `operator*` member function for an iterator type.
|
||||
*/
|
||||
class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
IteratorPointerDereferenceMemberOperator() {
|
||||
this.getClassAndName("operator*") instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator++` or `operator--` member function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches member functions. To find both
|
||||
* non-member and member versions, use `IteratorCrementOperator`.
|
||||
*/
|
||||
class IteratorCrementMemberOperator extends MemberFunction {
|
||||
IteratorCrementMemberOperator() {
|
||||
@@ -258,25 +162,49 @@ private class IteratorCrementMemberOperatorModel extends IteratorCrementMemberOp
|
||||
}
|
||||
|
||||
/**
|
||||
* A member `operator->` function for an iterator type.
|
||||
* A (member or non-member) `operator++` or `operator--` function for an iterator type.
|
||||
*/
|
||||
private class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
IteratorFieldMemberOperator() { this.getClassAndName("operator->") instanceof Iterator }
|
||||
class IteratorCrementOperator extends Function {
|
||||
IteratorCrementOperator() {
|
||||
this instanceof IteratorCrementNonMemberOperator or
|
||||
this instanceof IteratorCrementMemberOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator+` function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and member versions, use `IteratorBinaryAddOperator`.
|
||||
*/
|
||||
class IteratorAddNonMemberOperator extends Operator {
|
||||
IteratorAddNonMemberOperator() {
|
||||
this.hasName("operator+") and
|
||||
exists(getIteratorArgumentInput(this, [0, 1]))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorAddNonMemberOperatorModel extends IteratorAddNonMemberOperator, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
input = getIteratorArgumentInput(this, [0, 1]) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator+` or `operator-` member function of an iterator class.
|
||||
*
|
||||
* Note that this class _only_ matches member functions. To find both
|
||||
* non-member and member versions, use `IteratorBinaryAddOperator`.
|
||||
*/
|
||||
private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, TaintFunction {
|
||||
class IteratorBinaryArithmeticMemberOperator extends MemberFunction {
|
||||
IteratorBinaryArithmeticMemberOperator() {
|
||||
this.getClassAndName(["operator+", "operator-"]) instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorBinaryArithmeticMemberOperatorModel extends IteratorBinaryArithmeticMemberOperator,
|
||||
TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
@@ -284,14 +212,84 @@ private class IteratorBinaryArithmeticMemberOperator extends MemberFunction, Tai
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator+=` or `operator-=` member function of an iterator class.
|
||||
* A (member or non-member) `operator+` or `operator-` function for an iterator type.
|
||||
*/
|
||||
private class IteratorAssignArithmeticMemberOperator extends MemberFunction, DataFlowFunction,
|
||||
TaintFunction {
|
||||
class IteratorBinaryArithmeticOperator extends Function {
|
||||
IteratorBinaryArithmeticOperator() {
|
||||
this instanceof IteratorAddNonMemberOperator or
|
||||
this instanceof IteratorSubNonMemberOperator or
|
||||
this instanceof IteratorBinaryArithmeticMemberOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator-` function that takes a pointer difference type as its second argument.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and member versions, use `IteratorBinaryArithmeticOperator` (which also
|
||||
* includes `operator+` versions).
|
||||
*/
|
||||
class IteratorSubNonMemberOperator extends Operator {
|
||||
IteratorSubNonMemberOperator() {
|
||||
this.hasName("operator-") and
|
||||
exists(getIteratorArgumentInput(this, 0)) and
|
||||
this.getParameter(1).getUnspecifiedType() instanceof IntegralType // not an iterator difference
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorSubOperatorModel extends IteratorSubNonMemberOperator, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = getIteratorArgumentInput(this, 0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member `operator+=` or `operator-=` function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and member versions, use `IteratorAssignArithmeticOperator`.
|
||||
*/
|
||||
class IteratorAssignArithmeticNonMemberOperator extends Operator {
|
||||
IteratorAssignArithmeticNonMemberOperator() {
|
||||
this.hasName(["operator+=", "operator-="]) and
|
||||
exists(getIteratorArgumentInput(this, 0))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorAssignArithmeticNonMemberOperatorModel extends IteratorAssignArithmeticNonMemberOperator,
|
||||
DataFlowFunction, TaintFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameter(0) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and output.isReturnValueDeref()
|
||||
or
|
||||
// reverse flow from returned reference to the object referenced by the first parameter
|
||||
input.isReturnValueDeref() and
|
||||
output.isParameterDeref(0)
|
||||
or
|
||||
(input.isParameter(1) or input.isParameterDeref(1)) and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator+=` or `operator-=` member function of an iterator class.
|
||||
*
|
||||
* Note that this class _only_ matches member functions. To find both
|
||||
* non-member and member versions, use `IteratorAssignArithmeticOperator`.
|
||||
*/
|
||||
class IteratorAssignArithmeticMemberOperator extends MemberFunction {
|
||||
IteratorAssignArithmeticMemberOperator() {
|
||||
this.getClassAndName(["operator+=", "operator-="]) instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorAssignArithmeticMemberOperatorModel extends IteratorAssignArithmeticMemberOperator,
|
||||
DataFlowFunction, TaintFunction {
|
||||
override predicate hasDataFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierAddress() and
|
||||
output.isReturnValue()
|
||||
@@ -310,6 +308,83 @@ private class IteratorAssignArithmeticMemberOperator extends MemberFunction, Dat
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A (member or non-member) `operator+=` or `operator-=` function for an iterator type.
|
||||
*/
|
||||
class IteratorAssignArithmeticOperator extends Function {
|
||||
IteratorAssignArithmeticOperator() {
|
||||
this instanceof IteratorAssignArithmeticNonMemberOperator or
|
||||
this instanceof IteratorAssignArithmeticMemberOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A prefix `operator*` member function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches member functions. To find both
|
||||
* non-member and member versions, use `IteratorPointerDereferenceOperator`.
|
||||
*/
|
||||
class IteratorPointerDereferenceMemberOperator extends MemberFunction, TaintFunction,
|
||||
IteratorReferenceFunction {
|
||||
IteratorPointerDereferenceMemberOperator() {
|
||||
this.getClassAndName("operator*") instanceof Iterator
|
||||
}
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A non-member prefix `operator*` function for an iterator type.
|
||||
*
|
||||
* Note that this class _only_ matches non-member functions. To find both
|
||||
* non-member and member versions, use `IteratorPointerDereferenceOperator`.
|
||||
*/
|
||||
class IteratorPointerDereferenceNonMemberOperator extends Operator, IteratorReferenceFunction {
|
||||
IteratorPointerDereferenceNonMemberOperator() {
|
||||
this.hasName("operator*") and
|
||||
exists(getIteratorArgumentInput(this, 0))
|
||||
}
|
||||
}
|
||||
|
||||
private class IteratorPointerDereferenceNonMemberOperatorModel extends IteratorPointerDereferenceNonMemberOperator,
|
||||
TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input = getIteratorArgumentInput(this, 0) and
|
||||
output.isReturnValue()
|
||||
or
|
||||
input.isReturnValueDeref() and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A (member or non-member) prefix `operator*` function for an iterator type.
|
||||
*/
|
||||
class IteratorPointerDereferenceOperator extends Function {
|
||||
IteratorPointerDereferenceOperator() {
|
||||
this instanceof IteratorPointerDereferenceNonMemberOperator or
|
||||
this instanceof IteratorPointerDereferenceMemberOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A member `operator->` function for an iterator type.
|
||||
*/
|
||||
private class IteratorFieldMemberOperator extends Operator, TaintFunction {
|
||||
IteratorFieldMemberOperator() { this.getClassAndName("operator->") instanceof Iterator }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator[]` member function of an iterator class.
|
||||
*/
|
||||
@@ -326,17 +401,24 @@ private class IteratorArrayMemberOperator extends MemberFunction, TaintFunction,
|
||||
/**
|
||||
* An `operator=` member function of an iterator class that is not a copy or move assignment
|
||||
* operator.
|
||||
*
|
||||
* The `hasTaintFlow` override provides flow through output iterators that return themselves with
|
||||
* `operator*` and use their own `operator=` to assign to the container.
|
||||
*/
|
||||
private class IteratorAssignmentMemberOperator extends MemberFunction, TaintFunction {
|
||||
class IteratorAssignmentMemberOperator extends MemberFunction {
|
||||
IteratorAssignmentMemberOperator() {
|
||||
this.getClassAndName("operator=") instanceof Iterator and
|
||||
not this instanceof CopyAssignmentOperator and
|
||||
not this instanceof MoveAssignmentOperator
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An `operator=` member function of an iterator class that is not a copy or move assignment
|
||||
* operator.
|
||||
*
|
||||
* The `hasTaintFlow` override provides flow through output iterators that return themselves with
|
||||
* `operator*` and use their own `operator=` to assign to the container.
|
||||
*/
|
||||
private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMemberOperator,
|
||||
TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(0) and
|
||||
output.isQualifierObject()
|
||||
|
||||
@@ -64,7 +64,7 @@ predicate isInsecureEncryption(string name) { name.regexpMatch(getInsecureAlgori
|
||||
*/
|
||||
bindingset[name]
|
||||
predicate isEncryptionAdditionalEvidence(string name) {
|
||||
name.toUpperCase().matches("%" + ["CRYPT", "CODE", "CODING", "CBC", "KEY", "CIPHER", "MAC"] + "%")
|
||||
name.regexpMatch("(?i).*(crypt|code|coding|cbc|key|cipher|mac).*")
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,8 +14,8 @@ import cpp
|
||||
*/
|
||||
bindingset[s]
|
||||
private predicate suspicious(string s) {
|
||||
s.regexpMatch(".*(password|passwd|accountid|account.?key|accnt.?key|license.?key|trusted).*") and
|
||||
not s.matches(["%hash%", "%crypt%", "%file%", "%path%", "%invalid%"])
|
||||
s.regexpMatch("(?i).*(password|passwd|accountid|account.?key|accnt.?key|license.?key|trusted).*") and
|
||||
not s.regexpMatch("(?i).*(hash|crypt|file|path|invalid).*")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -23,7 +23,7 @@ private predicate suspicious(string s) {
|
||||
*/
|
||||
class SensitiveVariable extends Variable {
|
||||
SensitiveVariable() {
|
||||
suspicious(this.getName().toLowerCase()) and
|
||||
suspicious(this.getName()) and
|
||||
not this.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class SensitiveVariable extends Variable {
|
||||
*/
|
||||
class SensitiveFunction extends Function {
|
||||
SensitiveFunction() {
|
||||
suspicious(this.getName().toLowerCase()) and
|
||||
suspicious(this.getName()) and
|
||||
not this.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.5.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -100,7 +100,7 @@ private predicate fwdFlow(Instruction instr, ValueNumber vn) {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate revFlow(Instruction instr, ValueNumber vn) {
|
||||
fwdFlow(instr, vn) and
|
||||
fwdFlow(instr, pragma[only_bind_out](vn)) and
|
||||
(
|
||||
isSink(instr, _, vn)
|
||||
or
|
||||
@@ -126,7 +126,7 @@ class Node extends MkNode {
|
||||
|
||||
final string toString() { result = instr.toString() }
|
||||
|
||||
final Node getASuccessor() { result = MkNode(instr.getASuccessor(), vn) }
|
||||
final Node getASuccessor() { result = MkNode(pragma[only_bind_out](instr.getASuccessor()), vn) }
|
||||
|
||||
final Location getLocation() { result = instr.getLocation() }
|
||||
}
|
||||
@@ -167,7 +167,7 @@ predicate hasFlow(
|
||||
) {
|
||||
exists(ValueNumber vn |
|
||||
isSource(call, index, source, vn, _) and
|
||||
hasFlow(getNode(source, vn), getNode(sink, vn)) and
|
||||
hasFlow(getNode(source, pragma[only_bind_into](vn)), getNode(sink, pragma[only_bind_into](vn))) and
|
||||
isSink(sink, access, vn)
|
||||
)
|
||||
}
|
||||
|
||||
3
cpp/ql/src/change-notes/released/0.5.2.md
Normal file
3
cpp/ql/src/change-notes/released/0.5.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.1
|
||||
lastReleaseVersion: 0.5.2
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
...
|
||||
char buf[256];
|
||||
X509_NAME_oneline(X509_get_subject_name(peer),buf,sizeof(buf)); // GOOD
|
||||
...
|
||||
char buf[256];
|
||||
X509_NAME_oneline(X509_get_subject_name(peer),buf,1024); // BAD
|
||||
...
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Using a size argument that is larger than the buffer size will result in an out-of-bounds memory access and possibly overflow. You need to limit the value of the length argument.</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<example>
|
||||
<p>The following example shows the use of a function with and without an error in the size argument.</p>
|
||||
<sample src="BufferAccessWithIncorrectLengthValue.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/ARR38-C.+Guarantee+that+library+functions+do+not+form+invalid+pointers">ARR38-C. Guarantee that library functions do not form invalid pointers - SEI CERT C Coding Standard - Confluence</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* @name Buffer access with incorrect length value
|
||||
* @description Incorrect use of the length argument in some functions will result in out-of-memory accesses.
|
||||
* @kind problem
|
||||
* @id cpp/buffer-access-with-incorrect-length-value
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* experimental
|
||||
* external/cwe/cwe-805
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
/** Holds for a function `f`, which has an argument at index `bpos` that points to a buffer and an argument at index `spos` that points to a size. */
|
||||
predicate numberArgument(Function f, int bpos, int spos) {
|
||||
f.hasGlobalOrStdName([
|
||||
"X509_NAME_oneline", "SSL_CIPHER_description", "SSL_get_shared_ciphers",
|
||||
"SSL_export_keying_material_early", "SSL_export_keying_material", "SSL_set_alpn_protos",
|
||||
"SSL_CTX_set_alpn_protos", "SSL_read", "SSL_read_ex", "SSL_read_early_data",
|
||||
"SSL_bytes_to_cipher_list", "SSL_write", "SSL_SESSION_set1_master_key",
|
||||
"SSL_CTX_set_session_id_context", "BIO_gets", "BIO_read", "BIO_read_ex", "BIO_write",
|
||||
"BIO_write_ex", "BIO_ctrl", "BN_bn2binpad", "BN_signed_bn2bin", "BN_signed_bn2lebin",
|
||||
"EVP_PKEY_get_default_digest_name", "EVP_DigestUpdate", "EVP_PKEY_CTX_set1_tls1_prf_secret",
|
||||
"EVP_KDF_derive", "EVP_CIPHER_CTX_get_updated_iv", "EVP_PKEY_get_group_name", "EVP_MAC_init",
|
||||
"write", "read", "send", "sendto", "recv", "recvfrom", "strerror_r"
|
||||
]) and
|
||||
bpos = 1 and
|
||||
spos = 2
|
||||
or
|
||||
f.hasGlobalOrStdName(["X509_NAME_get_text_by_NID", "EVP_PKEY_get_utf8_string_param"]) and
|
||||
bpos = 2 and
|
||||
spos = 3
|
||||
or
|
||||
f.hasGlobalOrStdName([
|
||||
"BIO_snprintf", "BN_signed_lebin2bn", "BIO_new_mem_buf", "BN_lebin2bn", "BN_bin2bn",
|
||||
"EVP_read_pw_string", "EVP_read_pw_string", "strftime", "strnlen", "fgets", "snprintf",
|
||||
"vsnprintf"
|
||||
]) and
|
||||
bpos = 0 and
|
||||
spos = 1
|
||||
or
|
||||
f.hasGlobalOrStdName(["AES_ige_encrypt", "memchr"]) and bpos = 0 and spos = 2
|
||||
or
|
||||
f.hasGlobalOrStdName("EVP_MAC_final") and bpos = 1 and spos = 3
|
||||
or
|
||||
f.hasGlobalOrStdName("OBJ_obj2txt") and bpos = 2 and spos = 1
|
||||
or
|
||||
f.hasGlobalOrStdName("EVP_CIPHER_CTX_ctrl") and bpos = 3 and spos = 2
|
||||
or
|
||||
f.hasGlobalOrStdName(["EVP_PKEY_get_octet_string_param", "getnameinfo"]) and bpos = 2 and spos = 3
|
||||
or
|
||||
f.hasGlobalOrStdName([
|
||||
"EVP_DecryptUpdate", "EVP_EncryptUpdate", "EVP_PKEY_encrypt", "EVP_PKEY_sign",
|
||||
"EVP_CipherUpdate"
|
||||
]) and
|
||||
bpos = 3 and
|
||||
spos = 4
|
||||
or
|
||||
f.hasGlobalOrStdName("getnameinfo") and bpos = 4 and spos = 5
|
||||
}
|
||||
|
||||
from FunctionCall fc
|
||||
where
|
||||
exists(ArrayType array, int bufArgPos, int sizeArgPos |
|
||||
numberArgument(fc.getTarget(), bufArgPos, sizeArgPos) and
|
||||
fc.getArgument(pragma[only_bind_into](sizeArgPos)).getValue().toInt() > array.getByteSize() and
|
||||
fc.getArgument(pragma[only_bind_into](bufArgPos))
|
||||
.(VariableAccess)
|
||||
.getTarget()
|
||||
.getADeclarationEntry()
|
||||
.getType() = array
|
||||
)
|
||||
select fc,
|
||||
"Access beyond the bounds of the allocated memory is possible, the size argument used is greater than the size of the buffer."
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.5.2-dev
|
||||
version: 0.5.3-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
| bitshift.cpp:23:3:23:9 | ... <<= ... | 0.0 | 255.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:25:5:25:11 | ... <<= ... | 0.0 | 240.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:29:3:29:8 | ... << ... | 0.0 | 1020.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:32:3:32:9 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:35:3:35:9 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:38:3:38:22 | ... << ... | 0.0 | 32640.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:39:3:39:22 | ... << ... | 0.0 | 32640.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:40:3:40:22 | ... << ... | 0.0 | 32640.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:43:3:43:19 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:46:3:46:22 | ... << ... | 128.0 | 128.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:49:3:49:8 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:52:5:52:10 | ... << ... | 1.0 | 128.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:57:3:57:8 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:58:3:58:9 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:59:3:59:9 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:60:3:60:22 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:61:3:61:19 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:64:3:64:19 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:67:3:67:8 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:70:5:70:10 | ... << ... | 1.0 | 128.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:75:5:75:10 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:76:5:76:10 | ... << ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:90:3:90:9 | ... >>= ... | 0.0 | 63.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:92:5:92:11 | ... >>= ... | 0.0 | 15.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:96:3:96:8 | ... >> ... | 0.0 | 63.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:99:3:99:9 | ... >> ... | 0.0 | 0.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:103:3:103:9 | ... >> ... | 0.0 | 0.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:106:3:106:22 | ... >> ... | 0.0 | 63.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:107:3:107:22 | ... >> ... | 0.0 | 63.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:108:3:108:22 | ... >> ... | 0.0 | 63.0 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:111:3:111:19 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:114:3:114:24 | ... >> ... | 32.0 | 32.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:117:3:117:10 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:120:5:120:12 | ... >> ... | 32.0 | 128.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:126:3:126:8 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:127:3:127:9 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:128:3:128:9 | ... >> ... | -1.0 | 0.0 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:129:3:129:22 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:130:3:130:19 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:133:3:133:21 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:136:3:136:10 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:139:5:139:12 | ... >> ... | 32.0 | 128.0 | file://:0:0:0:0 | int | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:144:5:144:10 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | signed char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
| bitshift.cpp:145:5:145:10 | ... >> ... | -2.147483648E9 | 2.147483647E9 | file://:0:0:0:0 | signed char | file://:0:0:0:0 | unsigned char | file://:0:0:0:0 | int | file://:0:0:0:0 | int |
|
||||
@@ -0,0 +1,24 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import experimental.semmle.code.cpp.rangeanalysis.extensions.ConstantShiftExprRange
|
||||
|
||||
Expr getLOp(Operation o) {
|
||||
result = o.(BinaryOperation).getLeftOperand() or
|
||||
result = o.(Assignment).getLValue()
|
||||
}
|
||||
|
||||
Expr getROp(Operation o) {
|
||||
result = o.(BinaryOperation).getRightOperand() or
|
||||
result = o.(Assignment).getRValue()
|
||||
}
|
||||
|
||||
from Operation o
|
||||
where
|
||||
(
|
||||
o instanceof BinaryBitwiseOperation
|
||||
or
|
||||
o instanceof AssignBitwiseOperation
|
||||
)
|
||||
select o, lowerBound(o), upperBound(o), getLOp(o).getUnderlyingType(),
|
||||
getROp(o).getUnderlyingType(), getLOp(o).getFullyConverted().getUnderlyingType(),
|
||||
getROp(o).getFullyConverted().getUnderlyingType()
|
||||
@@ -0,0 +1,147 @@
|
||||
typedef signed char int8_t;
|
||||
typedef short int16_t;
|
||||
typedef int int32_t;
|
||||
typedef long int64_t;
|
||||
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef unsigned long uint64_t;
|
||||
|
||||
extern uint8_t value_known_at_runtime8();
|
||||
|
||||
void testLShiftOperator() {
|
||||
uint8_t unsigned_const1 = 7;
|
||||
uint8_t unsigned_const2(7);
|
||||
uint8_t unsigned_const3{7};
|
||||
int8_t signed_const = -7;
|
||||
uint8_t x = value_known_at_runtime8();
|
||||
int8_t y = (int8_t)value_known_at_runtime8();
|
||||
uint8_t z = value_known_at_runtime8();
|
||||
|
||||
// An assign left shift operator. Note that no promotion occurs here
|
||||
z <<= 2; // [0, 255]
|
||||
if (z <= 60) {
|
||||
z <<= 2; // [0, 240]
|
||||
}
|
||||
|
||||
// A normal shift
|
||||
x << 2; // [0, 1020]
|
||||
|
||||
// Possible to exceed the maximum size
|
||||
x << 25; // [-2147483648, 2147483648]
|
||||
|
||||
// Undefined behavior
|
||||
x << 34; // [-2147483648, 2147483648]
|
||||
|
||||
// A normal shift by a constant in a variable
|
||||
x << unsigned_const1; // [0, 32640]
|
||||
x << unsigned_const2; // [0, 32640]
|
||||
x << unsigned_const3; // [0, 32640]
|
||||
|
||||
// Negative shifts are undefined
|
||||
x << signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// Now the left operand is a constant
|
||||
1 << unsigned_const1; // [128, 128]
|
||||
|
||||
// x could be large enough to cause undefined behavior
|
||||
1 << x; // [-2147483648, 2147483647]
|
||||
if (x < 8) {
|
||||
// x is now constrained so the shift is defined
|
||||
1 << x; // [1, 128]
|
||||
}
|
||||
|
||||
// We don't support shifting negative values (and some of these are undefined
|
||||
// anyway)
|
||||
y << 2; // [-2147483648, 2147483647]
|
||||
y << 25; // [-2147483648, 2147483648]
|
||||
y << 34; // [-2147483648, 2147483648]
|
||||
y << unsigned_const1; // [-2147483648, 2147483647]
|
||||
y << signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// Negative shifts are undefined
|
||||
1 << signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// We don't handle cases where the shift range could be negative
|
||||
1 << y; // [-2147483648, 2147483648]
|
||||
if (y >= 0 && y < 8) {
|
||||
// The shift range is now positive
|
||||
1 << y; // [1, 128]
|
||||
}
|
||||
|
||||
if (x > 0 and x < 2 and y > 0 and x < 2) {
|
||||
// We don't support shifts where neither operand is a constant at the moment
|
||||
x << y; // [-2147483648, 2147483648]
|
||||
y << x; // [-2147483648, 2147483648]
|
||||
}
|
||||
}
|
||||
|
||||
void testRShiftOperator() {
|
||||
uint8_t unsigned_const1 = 2;
|
||||
uint8_t unsigned_const2(2);
|
||||
uint8_t unsigned_const3{2};
|
||||
int8_t signed_const = -2;
|
||||
uint8_t x = value_known_at_runtime8();
|
||||
int8_t y = (int8_t)value_known_at_runtime8();
|
||||
uint8_t z = value_known_at_runtime8();
|
||||
|
||||
// An assign right shift operator. Note that no promotion occurs here
|
||||
z >>= 2; // [0, 63]
|
||||
if (z <= 60) {
|
||||
z >>= 2; // [0, 15]
|
||||
}
|
||||
|
||||
// A normal shift
|
||||
x >> 2; // [0, 63]
|
||||
|
||||
// Possible to exceed the maximum size
|
||||
x >> 25; // [0, 0]
|
||||
|
||||
// Undefined behavior, but this case is handled by the SimpleRangeAnalysis
|
||||
// library and sets the the bounds to [0, 0], which is fine
|
||||
x >> 34; // [0, 0]
|
||||
|
||||
// A normal shift by a constant in a variable
|
||||
x >> unsigned_const1; // [0, 63]
|
||||
x >> unsigned_const2; // [0, 63]
|
||||
x >> unsigned_const3; // [0, 63]
|
||||
|
||||
// Negative shifts are undefined
|
||||
x >> signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// Now the left operand is a constant
|
||||
128 >> unsigned_const1; // [32, 32]
|
||||
|
||||
// x could be large enough to cause undefined behavior
|
||||
128 >> x; // [-2147483648, 2147483647]
|
||||
if (x < 3) {
|
||||
// x is now constrained so the shift is defined
|
||||
128 >> x; // [32, 128]
|
||||
}
|
||||
|
||||
// We don't support shifting negative values, but the SimpleRangeAnalysis
|
||||
// library handles the first three cases even though they're implementation
|
||||
// defined or undefined behavior (TODO: Check ideone)
|
||||
y >> 2; // [-2147483648, 2147483647] (Default is [-32, 31])
|
||||
y >> 25; // -2147483648, 2147483647] (Default is [-1, 0])
|
||||
y >> 34; // [-1, 0] (My code doesn't touch this, so default code is used)
|
||||
y >> unsigned_const1; // [-2147483648, 2147483647]
|
||||
y >> signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// Negative shifts are undefined
|
||||
128 >> signed_const; // [-2147483648, 2147483648]
|
||||
|
||||
// We don't handle cases where the shift range could be negative
|
||||
128 >> y; // [-2147483648, 2147483648]
|
||||
if (y >= 0 && y < 3) {
|
||||
// The shift range is now positive
|
||||
128 >> y; // [32, 128]
|
||||
}
|
||||
|
||||
if (x > 0 and x < 2 and y > 0 and x < 2) {
|
||||
// We don't support shifts where neither operand is a constant at the moment
|
||||
x >> y; // [-2147483648, 2147483648]
|
||||
y >> x; // [-2147483648, 2147483648]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
| test.cpp:27:5:27:21 | call to X509_NAME_oneline | Access beyond the bounds of the allocated memory is possible, the size argument used is greater than the size of the buffer. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-805/BufferAccessWithIncorrectLengthValue.ql
|
||||
@@ -0,0 +1,31 @@
|
||||
struct X509_NAME {};
|
||||
struct SSL {};
|
||||
struct X509 {};
|
||||
|
||||
char * X509_NAME_oneline(X509_NAME *a,char *buf,int size);
|
||||
X509 *SSL_get_peer_certificate(const SSL *ssl);
|
||||
X509_NAME *X509_get_subject_name(const X509 *x);
|
||||
char *strcasestr(char *a, char *b);
|
||||
|
||||
bool goodTest1(SSL *ssl,char *text)
|
||||
{
|
||||
X509 *peer;
|
||||
char buf[256];
|
||||
if( peer = SSL_get_peer_certificate(ssl))
|
||||
{
|
||||
X509_NAME_oneline(X509_get_subject_name(peer),buf,sizeof(buf)); // GOOD
|
||||
if((char*)strcasestr(buf,text)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool badTest1(SSL *ssl,char *text)
|
||||
{
|
||||
X509 *peer;
|
||||
char buf[256];
|
||||
if( peer = SSL_get_peer_certificate(ssl))
|
||||
{
|
||||
X509_NAME_oneline(X509_get_subject_name(peer),buf,1024); // BAD
|
||||
if((char*)strcasestr(buf,text)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -6,7 +6,7 @@ runs:
|
||||
- name: Setup dotnet
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 6.0.202
|
||||
dotnet-version: 7.0.102
|
||||
- name: Build Extractor
|
||||
shell: bash
|
||||
run: scripts/create-extractor-pack.sh
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Autobuild.CSharp</AssemblyName>
|
||||
<RootNamespace>Semmle.Autobuild.CSharp</RootNamespace>
|
||||
<ApplicationIcon/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Autobuild.Shared</AssemblyName>
|
||||
<RootNamespace>Semmle.Autobuild.Shared</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
class AnnotatedElement extends @cil_has_type_annotation {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Field extends @cil_field {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from AnnotatedElement element, int annotation
|
||||
where cil_type_annotation(element, annotation) and not element instanceof Field
|
||||
select element, annotation
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
class AnnotatedElement extends @has_type_annotation {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Field extends @field {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from AnnotatedElement element, int annotation
|
||||
where type_annotation(element, annotation) and not element instanceof Field
|
||||
select element, annotation
|
||||
@@ -0,0 +1,4 @@
|
||||
description: Remove CIL fields as entities that can have type annotations and remove field type annotations.
|
||||
compatibility: backwards
|
||||
cil_type_annotation.rel: run cil_type_annotation.qlo
|
||||
type_annotation.rel: run type_annotation.qlo
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: Remove a relation for scoped annotations.
|
||||
compatibility: backwards
|
||||
scoped_annotation.rel: delete
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Extraction.CIL.Driver</AssemblyName>
|
||||
<RootNamespace>Semmle.Extraction.CIL.Driver</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
@@ -38,6 +35,11 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
t = mt.Unmodified;
|
||||
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
|
||||
}
|
||||
if (t is ByRefType brt)
|
||||
{
|
||||
t = brt.ElementType;
|
||||
yield return Tuples.cil_type_annotation(this, TypeAnnotation.Ref);
|
||||
}
|
||||
yield return Tuples.cil_field(this, DeclaringType, Name, t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Extraction.CIL</AssemblyName>
|
||||
<RootNamespace>Semmle.Extraction.CIL</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Extraction.CSharp.Driver</AssemblyName>
|
||||
<RootNamespace>Semmle.Extraction.CSharp.Driver</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
||||
@@ -106,36 +106,6 @@ namespace Semmle.BuildAnalyser
|
||||
return result;
|
||||
}
|
||||
|
||||
// Attempt to load the reference from the GAC.
|
||||
try
|
||||
{
|
||||
var loadedAssembly = System.Reflection.Assembly.ReflectionOnlyLoad(id);
|
||||
|
||||
if (loadedAssembly is not null)
|
||||
{
|
||||
// The assembly was somewhere we haven't indexed before.
|
||||
// Add this assembly to our index so that subsequent lookups are faster.
|
||||
|
||||
result = AssemblyInfo.MakeFromAssembly(loadedAssembly);
|
||||
assemblyInfoById[id] = result;
|
||||
assemblyInfoByFileName[loadedAssembly.Location] = result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
// A suitable assembly could not be found
|
||||
}
|
||||
catch (FileLoadException)
|
||||
{
|
||||
// The assembly cannot be loaded for some reason
|
||||
// e.g. The name is malformed.
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
// .NET Core does not have a GAC.
|
||||
}
|
||||
|
||||
// Fallback position - locate the assembly by its lower-case name only.
|
||||
var asmName = assemblyName.ToLowerInvariant();
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Extraction.CSharp.Standalone</AssemblyName>
|
||||
<RootNamespace>Semmle.Extraction.CSharp.Standalone</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
||||
@@ -59,6 +59,19 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
}
|
||||
}
|
||||
|
||||
protected void PopulateScopedKind(TextWriter trapFile, ScopedKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ScopedKind.ScopedRef:
|
||||
trapFile.scoped_annotation(this, Kinds.ScopedAnnotation.ScopedRef);
|
||||
break;
|
||||
case ScopedKind.ScopedValue:
|
||||
trapFile.scoped_annotation(this, Kinds.ScopedAnnotation.ScopedValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected void ExtractCompilerGenerated(TextWriter trapFile)
|
||||
{
|
||||
if (Symbol.IsImplicitlyDeclared)
|
||||
|
||||
@@ -30,9 +30,11 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
PopulateAttributes();
|
||||
ContainingType!.PopulateGenerics();
|
||||
PopulateNullability(trapFile, Symbol.GetAnnotatedType());
|
||||
PopulateRefKind(trapFile, Symbol.RefKind);
|
||||
|
||||
var unboundFieldKey = Field.Create(Context, Symbol.OriginalDefinition);
|
||||
trapFile.fields(this, (Symbol.IsConst ? 2 : 1), Symbol.Name, ContainingType, Type.TypeRef, unboundFieldKey);
|
||||
var kind = Symbol.IsConst ? VariableKind.Const : VariableKind.None;
|
||||
trapFile.fields(this, kind, Symbol.Name, ContainingType, Type.TypeRef, unboundFieldKey);
|
||||
|
||||
PopulateModifiers(trapFile);
|
||||
|
||||
|
||||
@@ -23,17 +23,23 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
public void PopulateManual(Expression parent, bool isVar)
|
||||
{
|
||||
var trapFile = Context.TrapWriter.Writer;
|
||||
var (kind, type) = Symbol is ILocalSymbol l
|
||||
? (l.IsRef ? 3 : l.IsConst ? 2 : 1, l.GetAnnotatedType())
|
||||
: (1, parent.Type);
|
||||
trapFile.localvars(this, kind, Symbol.Name, isVar ? 1 : 0, Type.Create(Context, type).TypeRef, parent);
|
||||
var @var = isVar ? 1 : 0;
|
||||
|
||||
if (Symbol is ILocalSymbol local)
|
||||
{
|
||||
var kind = local.IsRef ? Kinds.VariableKind.Ref : local.IsConst ? Kinds.VariableKind.Const : Kinds.VariableKind.None;
|
||||
var type = local.GetAnnotatedType();
|
||||
trapFile.localvars(this, kind, Symbol.Name, @var, Type.Create(Context, type).TypeRef, parent);
|
||||
|
||||
PopulateNullability(trapFile, local.GetAnnotatedType());
|
||||
PopulateScopedKind(trapFile, local.ScopedKind);
|
||||
if (local.IsRef)
|
||||
trapFile.type_annotation(this, Kinds.TypeAnnotation.Ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
trapFile.localvars(this, Kinds.VariableKind.None, Symbol.Name, @var, Type.Create(Context, parent.Type).TypeRef, parent);
|
||||
}
|
||||
|
||||
trapFile.localvar_location(this, Location);
|
||||
|
||||
|
||||
@@ -25,19 +25,14 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
|
||||
public static string AccessibilityModifier(Accessibility access)
|
||||
{
|
||||
switch (access)
|
||||
return access switch
|
||||
{
|
||||
case Accessibility.Private:
|
||||
return "private";
|
||||
case Accessibility.Protected:
|
||||
return "protected";
|
||||
case Accessibility.Public:
|
||||
return "public";
|
||||
case Accessibility.Internal:
|
||||
return "internal";
|
||||
default:
|
||||
throw new InternalError("Unavailable modifier combination");
|
||||
}
|
||||
Accessibility.Private => Modifiers.Private,
|
||||
Accessibility.Protected => Modifiers.Protected,
|
||||
Accessibility.Public => Modifiers.Public,
|
||||
Accessibility.Internal => Modifiers.Internal,
|
||||
_ => throw new InternalError("Unavailable modifier combination"),
|
||||
};
|
||||
}
|
||||
|
||||
public static void HasAccessibility(Context cx, TextWriter trapFile, IEntity type, Accessibility access)
|
||||
@@ -48,17 +43,17 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
case Accessibility.Public:
|
||||
case Accessibility.Protected:
|
||||
case Accessibility.Internal:
|
||||
HasModifier(cx, trapFile, type, Modifier.AccessibilityModifier(access));
|
||||
HasModifier(cx, trapFile, type, AccessibilityModifier(access));
|
||||
break;
|
||||
case Accessibility.NotApplicable:
|
||||
break;
|
||||
case Accessibility.ProtectedOrInternal:
|
||||
HasModifier(cx, trapFile, type, "protected");
|
||||
HasModifier(cx, trapFile, type, "internal");
|
||||
HasModifier(cx, trapFile, type, Modifiers.Protected);
|
||||
HasModifier(cx, trapFile, type, Modifiers.Internal);
|
||||
break;
|
||||
case Accessibility.ProtectedAndInternal:
|
||||
HasModifier(cx, trapFile, type, "protected");
|
||||
HasModifier(cx, trapFile, type, "private");
|
||||
HasModifier(cx, trapFile, type, Modifiers.Protected);
|
||||
HasModifier(cx, trapFile, type, Modifiers.Private);
|
||||
break;
|
||||
default:
|
||||
throw new InternalError($"Unhandled Microsoft.CodeAnalysis.Accessibility value: {access}");
|
||||
@@ -70,6 +65,27 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
trapFile.has_modifiers(target, Modifier.Create(cx, modifier));
|
||||
}
|
||||
|
||||
private static void ExtractNamedTypeModifiers(Context cx, TextWriter trapFile, IEntity key, ISymbol symbol)
|
||||
{
|
||||
if (symbol.Kind != SymbolKind.NamedType)
|
||||
return;
|
||||
|
||||
if (symbol is not INamedTypeSymbol nt)
|
||||
throw new InternalError(symbol, "Symbol kind is inconsistent with its type");
|
||||
|
||||
if (nt.IsRecord)
|
||||
HasModifier(cx, trapFile, key, Modifiers.Record);
|
||||
|
||||
if (nt.TypeKind == TypeKind.Struct)
|
||||
{
|
||||
if (nt.IsReadOnly)
|
||||
HasModifier(cx, trapFile, key, Modifiers.Readonly);
|
||||
|
||||
if (nt.IsRefLikeType)
|
||||
HasModifier(cx, trapFile, key, Modifiers.Ref);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key, ISymbol symbol)
|
||||
{
|
||||
HasAccessibility(cx, trapFile, key, symbol.DeclaredAccessibility);
|
||||
@@ -77,51 +93,35 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
trapFile.has_modifiers(key, Modifier.Create(cx, Accessibility.Public));
|
||||
|
||||
if (symbol.IsAbstract && (symbol.Kind != SymbolKind.NamedType || ((INamedTypeSymbol)symbol).TypeKind != TypeKind.Interface))
|
||||
HasModifier(cx, trapFile, key, "abstract");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Abstract);
|
||||
|
||||
if (symbol.IsSealed)
|
||||
HasModifier(cx, trapFile, key, "sealed");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Sealed);
|
||||
|
||||
var fromSource = symbol.DeclaringSyntaxReferences.Length > 0;
|
||||
|
||||
if (symbol.IsStatic && !(symbol.Kind == SymbolKind.Field && ((IFieldSymbol)symbol).IsConst && !fromSource))
|
||||
HasModifier(cx, trapFile, key, "static");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Static);
|
||||
|
||||
if (symbol.IsVirtual)
|
||||
HasModifier(cx, trapFile, key, "virtual");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Virtual);
|
||||
|
||||
if (symbol.Kind == SymbolKind.Field && ((IFieldSymbol)symbol).IsReadOnly)
|
||||
HasModifier(cx, trapFile, key, "readonly");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Readonly);
|
||||
|
||||
if (symbol.IsOverride)
|
||||
HasModifier(cx, trapFile, key, "override");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Override);
|
||||
|
||||
if (symbol.Kind == SymbolKind.Method && ((IMethodSymbol)symbol).IsAsync)
|
||||
HasModifier(cx, trapFile, key, "async");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Async);
|
||||
|
||||
if (symbol.IsExtern)
|
||||
HasModifier(cx, trapFile, key, "extern");
|
||||
HasModifier(cx, trapFile, key, Modifiers.Extern);
|
||||
|
||||
foreach (var modifier in symbol.GetSourceLevelModifiers())
|
||||
HasModifier(cx, trapFile, key, modifier);
|
||||
|
||||
if (symbol.Kind == SymbolKind.NamedType)
|
||||
{
|
||||
var nt = symbol as INamedTypeSymbol;
|
||||
if (nt is null)
|
||||
throw new InternalError(symbol, "Symbol kind is inconsistent with its type");
|
||||
|
||||
if (nt.IsRecord)
|
||||
HasModifier(cx, trapFile, key, "record");
|
||||
|
||||
if (nt.TypeKind == TypeKind.Struct)
|
||||
{
|
||||
if (nt.IsReadOnly)
|
||||
HasModifier(cx, trapFile, key, "readonly");
|
||||
if (nt.IsRefLikeType)
|
||||
HasModifier(cx, trapFile, key, "ref");
|
||||
}
|
||||
}
|
||||
ExtractNamedTypeModifiers(cx, trapFile, key, symbol);
|
||||
}
|
||||
|
||||
public static Modifier Create(Context cx, string modifier)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
internal static class Modifiers
|
||||
{
|
||||
public const string Abstract = "abstract";
|
||||
public const string Async = "async";
|
||||
public const string Const = "const";
|
||||
public const string Extern = "extern";
|
||||
public const string Internal = "internal";
|
||||
public const string New = "new";
|
||||
public const string Override = "override";
|
||||
public const string Partial = "partial";
|
||||
public const string Private = "private";
|
||||
public const string Protected = "protected";
|
||||
public const string Public = "public";
|
||||
public const string Readonly = "readonly";
|
||||
public const string Record = "record";
|
||||
public const string Ref = "ref";
|
||||
public const string Sealed = "sealed";
|
||||
public const string Static = "static";
|
||||
public const string Virtual = "virtual";
|
||||
}
|
||||
@@ -100,6 +100,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
PopulateAttributes();
|
||||
PopulateNullability(trapFile, Symbol.GetAnnotatedType());
|
||||
PopulateRefKind(trapFile, Symbol.RefKind);
|
||||
PopulateScopedKind(trapFile, Symbol.ScopedKind);
|
||||
|
||||
if (Symbol.Name != Original.Symbol.Name)
|
||||
Context.ModelError(Symbol, "Inconsistent parameter declaration");
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
{
|
||||
var info = Context.GetModel(node).GetSymbolInfo(node.Name);
|
||||
|
||||
if (node.StaticKeyword.Kind() == SyntaxKind.None)
|
||||
if (node.StaticKeyword.IsKind(SyntaxKind.None))
|
||||
{
|
||||
// A normal using
|
||||
if (info.Symbol is INamespaceSymbol namespaceSymbol)
|
||||
@@ -47,7 +47,7 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
trapFile.using_directive_location(this, Context.CreateLocation(ReportingLocation));
|
||||
}
|
||||
|
||||
if (node.GlobalKeyword.Kind() == SyntaxKind.GlobalKeyword)
|
||||
if (node.GlobalKeyword.IsKind(SyntaxKind.GlobalKeyword))
|
||||
{
|
||||
trapFile.using_global(this);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Semmle.Extraction.Kinds;
|
||||
|
||||
public enum ScopedAnnotation
|
||||
{
|
||||
None = 0,
|
||||
ScopedRef = 1,
|
||||
ScopedValue = 2
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Semmle.Extraction.Kinds;
|
||||
|
||||
public enum VariableKind
|
||||
{
|
||||
None = 1,
|
||||
Const = 2,
|
||||
Ref = 3
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Extraction.CSharp</AssemblyName>
|
||||
<RootNamespace>Semmle.Extraction.CSharp</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
||||
@@ -191,8 +191,8 @@ namespace Semmle.Extraction.CSharp
|
||||
internal static void field_location(this TextWriter trapFile, Field field, Location location) =>
|
||||
trapFile.WriteTuple("field_location", field, location);
|
||||
|
||||
internal static void fields(this TextWriter trapFile, Field field, int @const, string name, Type declaringType, Type fieldType, Field unboundKey) =>
|
||||
trapFile.WriteTuple("fields", field, @const, name, declaringType, fieldType, unboundKey);
|
||||
internal static void fields(this TextWriter trapFile, Field field, VariableKind kind, string name, Type declaringType, Type fieldType, Field unboundKey) =>
|
||||
trapFile.WriteTuple("fields", field, (int)kind, name, declaringType, fieldType, unboundKey);
|
||||
|
||||
internal static void general_type_parameter_constraints(this TextWriter trapFile, TypeParameterConstraints constraints, int hasKind) =>
|
||||
trapFile.WriteTuple("general_type_parameter_constraints", constraints, hasKind);
|
||||
@@ -227,8 +227,8 @@ namespace Semmle.Extraction.CSharp
|
||||
internal static void localvar_location(this TextWriter trapFile, LocalVariable var, Location location) =>
|
||||
trapFile.WriteTuple("localvar_location", var, location);
|
||||
|
||||
internal static void localvars(this TextWriter trapFile, LocalVariable key, int @const, string name, int @var, Type type, Expression expr) =>
|
||||
trapFile.WriteTuple("localvars", key, @const, name, @var, type, expr);
|
||||
internal static void localvars(this TextWriter trapFile, LocalVariable key, VariableKind kind, string name, int @var, Type type, Expression expr) =>
|
||||
trapFile.WriteTuple("localvars", key, (int)kind, name, @var, type, expr);
|
||||
|
||||
public static void metadata_handle(this TextWriter trapFile, IEntity entity, Location assembly, int handleValue) =>
|
||||
trapFile.WriteTuple("metadata_handle", entity, assembly, handleValue);
|
||||
@@ -462,5 +462,8 @@ namespace Semmle.Extraction.CSharp
|
||||
|
||||
internal static void file_extraction_mode(this System.IO.TextWriter trapFile, Entities.File file, ExtractorMode mode) =>
|
||||
trapFile.WriteTuple("file_extraction_mode", file, mode);
|
||||
|
||||
internal static void scoped_annotation(this TextWriter trapFile, IEntity element, ScopedAnnotation @scoped) =>
|
||||
trapFile.WriteTuple("scoped_annotation", element, (int)@scoped);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Extraction</AssemblyName>
|
||||
<RootNamespace>Semmle.Extraction</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace Semmle.Extraction
|
||||
{
|
||||
FileUtils.MoveOrReplace(tmpSrcFile, dest);
|
||||
}
|
||||
catch (IOException ex)
|
||||
catch (Exception ex)
|
||||
{
|
||||
// If this happened, it was probably because the same file was compiled multiple times.
|
||||
// In any case, this is not a fatal error.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Util</AssemblyName>
|
||||
<RootNamespace>Semmle.Util</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.4.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.4.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.4.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.4.1
|
||||
lastReleaseVersion: 1.4.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.4.2-dev
|
||||
version: 1.4.3-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.4.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.4.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.4.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.4.1
|
||||
lastReleaseVersion: 1.4.2
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.4.2-dev
|
||||
version: 1.4.3-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import os
|
||||
from create_database_utils import *
|
||||
|
||||
run_codeql_database_create(['dotnet publish'], test_db="default-db", db=None, lang="csharp")
|
||||
artifacts = 'bin/Temp'
|
||||
run_codeql_database_create([f"dotnet publish -o {artifacts}"], test_db="default-db", db=None, lang="csharp")
|
||||
|
||||
## Check that the publish folder is created.
|
||||
if not os.path.isdir("bin/Debug/net6.0/publish/"):
|
||||
if not os.path.isdir(artifacts):
|
||||
raise Exception("The publish artifact folder was not created.")
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user