mirror of
https://github.com/github/codeql.git
synced 2026-05-02 20:25:13 +02:00
Merge branch 'main' into call-graph-code
This commit is contained in:
2
.github/actions/fetch-codeql/action.yml
vendored
2
.github/actions/fetch-codeql/action.yml
vendored
@@ -19,4 +19,6 @@ runs:
|
||||
gh extension install github/gh-codeql
|
||||
gh codeql set-channel "$CHANNEL"
|
||||
gh codeql version
|
||||
printf "CODEQL_FETCHED_CODEQL_PATH=" >> "${GITHUB_ENV}"
|
||||
gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_ENV}"
|
||||
gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}"
|
||||
|
||||
62
.github/workflows/ql-for-ql-tests.yml
vendored
62
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -33,19 +33,73 @@ jobs:
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/rust-toolchain.toml', 'ql/**/Cargo.lock') }}
|
||||
- name: Build extractor
|
||||
run: |
|
||||
cd ql;
|
||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
||||
env "PATH=$PATH:$codeqlpath" ./scripts/create-extractor-pack.sh
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: ql-for-ql-tests
|
||||
- name: Run QL tests
|
||||
run: |
|
||||
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries ql/ql/test
|
||||
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" ql/ql/test
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Check QL formatting
|
||||
|
||||
other-os:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest]
|
||||
needs: [qltest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install GNU tar
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
find ql/ql/src "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 "${CODEQL}" query format --check-only
|
||||
brew install gnu-tar
|
||||
echo "/usr/local/opt/gnu-tar/libexec/gnubin" >> $GITHUB_PATH
|
||||
- name: Find codeql
|
||||
id: find-codeql
|
||||
uses: github/codeql-action/init@77a8d2d10c0b403a8b4aadbd223dc489ecd22683
|
||||
with:
|
||||
languages: javascript # does not matter
|
||||
- uses: ./.github/actions/os-version
|
||||
id: os_version
|
||||
- uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
ql/target
|
||||
key: ${{ runner.os }}-${{ steps.os_version.outputs.version }}-qltest-cargo-${{ hashFiles('ql/rust-toolchain.toml', 'ql/**/Cargo.lock') }}
|
||||
- name: Build extractor
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
cd ql;
|
||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
||||
env "PATH=$PATH:$codeqlpath" ./scripts/create-extractor-pack.sh
|
||||
- name: Build extractor (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd ql;
|
||||
$Env:PATH += ";$(dirname ${{ steps.find-codeql.outputs.codeql-path }})"
|
||||
pwsh ./scripts/create-extractor-pack.ps1
|
||||
- name: Run a single QL tests - Unix
|
||||
if: runner.os != 'Windows'
|
||||
run: |
|
||||
"${CODEQL}" test run --check-databases --search-path "${{ github.workspace }}/ql/extractor-pack" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- name: Run a single QL tests - Windows
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$Env:PATH += ";$(dirname ${{ steps.find-codeql.outputs.codeql-path }})"
|
||||
codeql test run --check-databases --search-path "${{ github.workspace }}/ql/extractor-pack" ql/ql/test/queries/style/DeadCode/DeadCode.qlref
|
||||
|
||||
22
.github/workflows/ruby-build.yml
vendored
22
.github/workflows/ruby-build.yml
vendored
@@ -205,11 +205,6 @@ jobs:
|
||||
- name: Fetch CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Shopify/example-ruby-app
|
||||
ref: 67a0decc5eb550f3a9228eda53925c3afd40dfe9
|
||||
|
||||
- name: Download Ruby bundle
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
@@ -218,26 +213,15 @@ jobs:
|
||||
- name: Unzip Ruby bundle
|
||||
shell: bash
|
||||
run: unzip -q -d "${{ runner.temp }}/ruby-bundle" "${{ runner.temp }}/codeql-ruby-bundle.zip"
|
||||
- name: Prepare test files
|
||||
shell: bash
|
||||
run: |
|
||||
echo "import codeql.ruby.AST select count(File f)" > "test.ql"
|
||||
echo "| 4 |" > "test.expected"
|
||||
echo 'name: sample-tests
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
codeql/ruby-all: "*"
|
||||
extractor: ruby
|
||||
tests: .
|
||||
' > qlpack.yml
|
||||
|
||||
- name: Run QL test
|
||||
shell: bash
|
||||
run: |
|
||||
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" .
|
||||
codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" ruby/ql/test/library-tests/ast/constants/
|
||||
- name: Create database
|
||||
shell: bash
|
||||
run: |
|
||||
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root . ../database
|
||||
codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root ruby/ql/test/library-tests/ast/constants/ ../database
|
||||
- name: Analyze database
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll",
|
||||
"go/ql/lib/semmle/go/dataflow/internal/DataFlowImplForStringsNewReplacer.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll",
|
||||
"python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll",
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
private import RangeAnalysisStage
|
||||
|
||||
module FloatDelta implements DeltaSig {
|
||||
class Delta = float;
|
||||
|
||||
bindingset[d]
|
||||
bindingset[result]
|
||||
float toFloat(Delta d) { result = d }
|
||||
|
||||
bindingset[d]
|
||||
bindingset[result]
|
||||
int toInt(Delta d) { result = d }
|
||||
|
||||
bindingset[n]
|
||||
bindingset[result]
|
||||
Delta fromInt(int n) { result = n }
|
||||
|
||||
bindingset[f]
|
||||
Delta fromFloat(float f) {
|
||||
result =
|
||||
min(float diff, float res |
|
||||
diff = (res - f) and res = f.ceil()
|
||||
or
|
||||
diff = (f - res) and res = f.floor()
|
||||
|
|
||||
res order by diff
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -14,321 +14,328 @@ private import ModulusAnalysisSpecific::Private
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import RangeUtils
|
||||
private import RangeAnalysisStage
|
||||
|
||||
/**
|
||||
* Holds if `e + delta` equals `v` at `pos`.
|
||||
*/
|
||||
private predicate valueFlowStepSsa(SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta) {
|
||||
semSsaUpdateStep(v, e, delta) and pos.hasReadOfVar(v)
|
||||
or
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = semEqFlowCond(v, e, delta, true, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `add` is the addition of `larg` and `rarg`, neither of which are
|
||||
* `ConstantIntegerExpr`s.
|
||||
*/
|
||||
private predicate nonConstAddition(SemExpr add, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemAddExpr a | a = add |
|
||||
larg = a.getLeftOperand() and
|
||||
rarg = a.getRightOperand()
|
||||
) and
|
||||
not larg instanceof SemConstantIntegerExpr and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not
|
||||
* a `ConstantIntegerExpr`.
|
||||
*/
|
||||
private predicate nonConstSubtraction(SemExpr sub, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemSubExpr s | s = sub |
|
||||
larg = s.getLeftOperand() and
|
||||
rarg = s.getRightOperand()
|
||||
) and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/** Gets an expression that is the remainder modulo `mod` of `arg`. */
|
||||
private SemExpr modExpr(SemExpr arg, int mod) {
|
||||
exists(SemRemExpr rem |
|
||||
result = rem and
|
||||
arg = rem.getLeftOperand() and
|
||||
rem.getRightOperand().(SemConstantIntegerExpr).getIntValue() = mod and
|
||||
mod >= 2
|
||||
)
|
||||
or
|
||||
exists(SemConstantIntegerExpr c |
|
||||
mod = 2.pow([1 .. 30]) and
|
||||
c.getIntValue() = mod - 1 and
|
||||
result.(SemBitAndExpr).hasOperands(arg, c)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on
|
||||
* its `testIsTrue` branch.
|
||||
*/
|
||||
private SemGuard moduloCheck(SemSsaVariable v, int val, int mod, boolean testIsTrue) {
|
||||
exists(SemExpr rem, SemConstantIntegerExpr c, int r, boolean polarity |
|
||||
result.isEquality(rem, c, polarity) and
|
||||
c.getIntValue() = r and
|
||||
rem = modExpr(v.getAUse(), mod) and
|
||||
(
|
||||
testIsTrue = polarity and val = r
|
||||
or
|
||||
testIsTrue = polarity.booleanNot() and
|
||||
mod = 2 and
|
||||
val = 1 - r and
|
||||
(r = 0 or r = 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
|
||||
*/
|
||||
private predicate moduloGuardedRead(SemSsaVariable v, SemSsaReadPosition pos, int val, int mod) {
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = moduloCheck(v, val, mod, testIsTrue) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `factor` is a power of 2 that divides `mask`. */
|
||||
bindingset[mask]
|
||||
private predicate andmaskFactor(int mask, int factor) {
|
||||
mask % factor = 0 and
|
||||
factor = 2.pow([1 .. 30])
|
||||
}
|
||||
|
||||
/** Holds if `e` is evenly divisible by `factor`. */
|
||||
private predicate evenlyDivisibleExpr(SemExpr e, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() |
|
||||
e.(SemMulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2
|
||||
or
|
||||
e.(SemShiftLeftExpr).getRightOperand() = c and factor = 2.pow(k) and k > 0
|
||||
or
|
||||
e.(SemBitAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rix` is the number of input edges to `phi`.
|
||||
*/
|
||||
private predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
|
||||
rix = max(int r | rankedPhiInput(phi, _, _, r))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remainder of `val` modulo `mod`.
|
||||
*
|
||||
* For `mod = 0` the result equals `val` and for `mod > 1` the result is within
|
||||
* the range `[0 .. mod-1]`.
|
||||
*/
|
||||
bindingset[val, mod]
|
||||
private int remainder(int val, int mod) {
|
||||
mod = 0 and result = val
|
||||
or
|
||||
mod > 1 and result = ((val % mod) + mod) % mod
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
|
||||
*/
|
||||
private predicate phiSelfModulus(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod
|
||||
) {
|
||||
exists(SemSsaBound phibound, int v, int m |
|
||||
edge.phiInput(phi, inp) and
|
||||
phibound.getAVariable() = phi and
|
||||
ssaModulus(inp, edge, phibound, v, m) and
|
||||
mod = m.gcd(v) and
|
||||
mod != 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
|
||||
*/
|
||||
private predicate phiModulusInit(SemSsaPhiNode phi, SemBound b, int val, int mod) {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(phi, inp) and
|
||||
ssaModulus(inp, edge, b, val, mod)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate phiModulusRankStep(SemSsaPhiNode phi, SemBound b, int val, int mod, int rix) {
|
||||
/*
|
||||
* base case. If any phi input is equal to `b + val` modulo `mod`, that's a potential congruence
|
||||
* class for the phi node.
|
||||
module ModulusAnalysis<DeltaSig D, BoundSig<D> Bounds, UtilSig<D> U> {
|
||||
/**
|
||||
* Holds if `e + delta` equals `v` at `pos`.
|
||||
*/
|
||||
private predicate valueFlowStepSsa(SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta) {
|
||||
U::semSsaUpdateStep(v, e, D::fromInt(delta)) and pos.hasReadOfVar(v)
|
||||
or
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = U::semEqFlowCond(v, e, D::fromInt(delta), true, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
rix = 0 and
|
||||
phiModulusInit(phi, b, val, mod)
|
||||
or
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int v1, int m1 |
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
|
|
||||
/**
|
||||
* Holds if `add` is the addition of `larg` and `rarg`, neither of which are
|
||||
* `ConstantIntegerExpr`s.
|
||||
*/
|
||||
private predicate nonConstAddition(SemExpr add, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemAddExpr a | a = add |
|
||||
larg = a.getLeftOperand() and
|
||||
rarg = a.getRightOperand()
|
||||
) and
|
||||
not larg instanceof SemConstantIntegerExpr and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not
|
||||
* a `ConstantIntegerExpr`.
|
||||
*/
|
||||
private predicate nonConstSubtraction(SemExpr sub, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemSubExpr s | s = sub |
|
||||
larg = s.getLeftOperand() and
|
||||
rarg = s.getRightOperand()
|
||||
) and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/** Gets an expression that is the remainder modulo `mod` of `arg`. */
|
||||
private SemExpr modExpr(SemExpr arg, int mod) {
|
||||
exists(SemRemExpr rem |
|
||||
result = rem and
|
||||
arg = rem.getLeftOperand() and
|
||||
rem.getRightOperand().(SemConstantIntegerExpr).getIntValue() = mod and
|
||||
mod >= 2
|
||||
)
|
||||
or
|
||||
exists(SemConstantIntegerExpr c |
|
||||
mod = 2.pow([1 .. 30]) and
|
||||
c.getIntValue() = mod - 1 and
|
||||
result.(SemBitAndExpr).hasOperands(arg, c)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on
|
||||
* its `testIsTrue` branch.
|
||||
*/
|
||||
private SemGuard moduloCheck(SemSsaVariable v, int val, int mod, boolean testIsTrue) {
|
||||
exists(SemExpr rem, SemConstantIntegerExpr c, int r, boolean polarity |
|
||||
result.isEquality(rem, c, polarity) and
|
||||
c.getIntValue() = r and
|
||||
rem = modExpr(v.getAUse(), mod) and
|
||||
(
|
||||
testIsTrue = polarity and val = r
|
||||
or
|
||||
testIsTrue = polarity.booleanNot() and
|
||||
mod = 2 and
|
||||
val = 1 - r and
|
||||
(r = 0 or r = 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
|
||||
*/
|
||||
private predicate moduloGuardedRead(SemSsaVariable v, SemSsaReadPosition pos, int val, int mod) {
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = moduloCheck(v, val, mod, testIsTrue) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `factor` is a power of 2 that divides `mask`. */
|
||||
bindingset[mask]
|
||||
private predicate andmaskFactor(int mask, int factor) {
|
||||
mask % factor = 0 and
|
||||
factor = 2.pow([1 .. 30])
|
||||
}
|
||||
|
||||
/** Holds if `e` is evenly divisible by `factor`. */
|
||||
private predicate evenlyDivisibleExpr(SemExpr e, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() |
|
||||
e.(SemMulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2
|
||||
or
|
||||
e.(SemShiftLeftExpr).getRightOperand() = c and factor = 2.pow(k) and k > 0
|
||||
or
|
||||
e.(SemBitAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rix` is the number of input edges to `phi`.
|
||||
*/
|
||||
private predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
|
||||
rix = max(int r | rankedPhiInput(phi, _, _, r))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remainder of `val` modulo `mod`.
|
||||
*
|
||||
* For `mod = 0` the result equals `val` and for `mod > 1` the result is within
|
||||
* the range `[0 .. mod-1]`.
|
||||
*/
|
||||
bindingset[val, mod]
|
||||
private int remainder(int val, int mod) {
|
||||
mod = 0 and result = val
|
||||
or
|
||||
mod > 1 and result = ((val % mod) + mod) % mod
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
|
||||
*/
|
||||
private predicate phiSelfModulus(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod
|
||||
) {
|
||||
exists(Bounds::SemSsaBound phibound, int v, int m |
|
||||
edge.phiInput(phi, inp) and
|
||||
phibound.getAVariable() = phi and
|
||||
ssaModulus(inp, edge, phibound, v, m) and
|
||||
mod = m.gcd(v) and
|
||||
mod != 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
|
||||
*/
|
||||
private predicate phiModulusInit(SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(phi, inp) and
|
||||
ssaModulus(inp, edge, b, val, mod)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate phiModulusRankStep(
|
||||
SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod, int rix
|
||||
) {
|
||||
/*
|
||||
* Recursive case. If `inp` = `b + v2` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be the congruence class of `v1` modulo
|
||||
* the greatest common denominator of `m1`, `m2`, and `v1 - v2`.
|
||||
* base case. If any phi input is equal to `b + val` modulo `mod`, that's a potential congruence
|
||||
* class for the phi node.
|
||||
*/
|
||||
|
||||
exists(int v2, int m2 |
|
||||
rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
ssaModulus(inp, edge, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2)
|
||||
rix = 0 and
|
||||
phiModulusInit(phi, b, val, mod)
|
||||
or
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int v1, int m1 |
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
|
|
||||
/*
|
||||
* Recursive case. If `inp` = `b + v2` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be the congruence class of `v1` modulo
|
||||
* the greatest common denominator of `m1`, `m2`, and `v1 - v2`.
|
||||
*/
|
||||
|
||||
exists(int v2, int m2 |
|
||||
rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
ssaModulus(inp, edge, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2)
|
||||
)
|
||||
or
|
||||
/*
|
||||
* Recursive case. If `inp` = `phi` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be a congruence class modulo the greatest
|
||||
* common denominator of `m1` and `m2`.
|
||||
*/
|
||||
|
||||
exists(int m2 |
|
||||
rankedPhiInput(phi, inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
phiSelfModulus(phi, inp, edge, m2) and
|
||||
mod = m1.gcd(m2)
|
||||
)
|
||||
)
|
||||
or
|
||||
/*
|
||||
* Recursive case. If `inp` = `phi` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be a congruence class modulo the greatest
|
||||
* common denominator of `m1` and `m2`.
|
||||
*/
|
||||
}
|
||||
|
||||
exists(int m2 |
|
||||
rankedPhiInput(phi, inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
phiSelfModulus(phi, inp, edge, m2) and
|
||||
mod = m1.gcd(m2)
|
||||
/**
|
||||
* Holds if `phi` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate phiModulus(SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
|
||||
exists(int r |
|
||||
maxPhiInputRank(phi, r) and
|
||||
phiModulusRankStep(phi, b, val, mod, r)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `phi` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate phiModulus(SemSsaPhiNode phi, SemBound b, int val, int mod) {
|
||||
exists(int r |
|
||||
maxPhiInputRank(phi, r) and
|
||||
phiModulusRankStep(phi, b, val, mod, r)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate ssaModulus(SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int val, int mod) {
|
||||
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
|
||||
or
|
||||
b.(SemSsaBound).getAVariable() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0
|
||||
or
|
||||
exists(SemExpr e, int val0, int delta |
|
||||
semExprModulus(e, b, val0, mod) and
|
||||
valueFlowStepSsa(v, pos, e, delta) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
moduloGuardedRead(v, pos, val, mod) and b instanceof SemZeroBound
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is equal to `b + val` modulo `mod`.
|
||||
*
|
||||
* There are two cases for the modulus:
|
||||
* - `mod = 0`: The equality `e = b + val` is an ordinary equality.
|
||||
* - `mod > 1`: `val` lies within the range `[0 .. mod-1]`.
|
||||
*/
|
||||
cached
|
||||
predicate semExprModulus(SemExpr e, SemBound b, int val, int mod) {
|
||||
not ignoreExprModulus(e) and
|
||||
(
|
||||
e = b.getExpr(val) and mod = 0
|
||||
/**
|
||||
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate ssaModulus(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, Bounds::SemBound b, int val, int mod
|
||||
) {
|
||||
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
|
||||
or
|
||||
evenlyDivisibleExpr(e, mod) and
|
||||
val = 0 and
|
||||
b instanceof SemZeroBound
|
||||
b.(Bounds::SemSsaBound).getAVariable() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
ssaModulus(v, bb, b, val, mod) and
|
||||
e = v.getAUse() and
|
||||
bb.getAnExpr() = e
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int val0, int delta |
|
||||
semExprModulus(mid, b, val0, mod) and
|
||||
semValueFlowStep(e, mid, delta) and
|
||||
exists(SemExpr e, int val0, int delta |
|
||||
semExprModulus(e, b, val0, mod) and
|
||||
valueFlowStepSsa(v, pos, e, delta) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
exists(SemConditionalExpr cond, int v1, int v2, int m1, int m2 |
|
||||
cond = e and
|
||||
condExprBranchModulus(cond, true, b, v1, m1) and
|
||||
condExprBranchModulus(cond, false, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
)
|
||||
or
|
||||
exists(SemBound b1, SemBound b2, int v1, int v2, int m1, int m2 |
|
||||
addModulus(e, true, b1, v1, m1) and
|
||||
addModulus(e, false, b2, v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 + v2, mod)
|
||||
|
|
||||
b = b1 and b2 instanceof SemZeroBound
|
||||
moduloGuardedRead(v, pos, val, mod) and b instanceof Bounds::SemZeroBound
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is equal to `b + val` modulo `mod`.
|
||||
*
|
||||
* There are two cases for the modulus:
|
||||
* - `mod = 0`: The equality `e = b + val` is an ordinary equality.
|
||||
* - `mod > 1`: `val` lies within the range `[0 .. mod-1]`.
|
||||
*/
|
||||
cached
|
||||
predicate semExprModulus(SemExpr e, Bounds::SemBound b, int val, int mod) {
|
||||
not ignoreExprModulus(e) and
|
||||
(
|
||||
e = b.getExpr(D::fromInt(val)) and mod = 0
|
||||
or
|
||||
b = b2 and b1 instanceof SemZeroBound
|
||||
evenlyDivisibleExpr(e, mod) and
|
||||
val = 0 and
|
||||
b instanceof Bounds::SemZeroBound
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
ssaModulus(v, bb, b, val, mod) and
|
||||
e = v.getAUse() and
|
||||
bb.getAnExpr() = e
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int val0, int delta |
|
||||
semExprModulus(mid, b, val0, mod) and
|
||||
U::semValueFlowStep(e, mid, D::fromInt(delta)) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
exists(SemConditionalExpr cond, int v1, int v2, int m1, int m2 |
|
||||
cond = e and
|
||||
condExprBranchModulus(cond, true, b, v1, m1) and
|
||||
condExprBranchModulus(cond, false, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
)
|
||||
or
|
||||
exists(Bounds::SemBound b1, Bounds::SemBound b2, int v1, int v2, int m1, int m2 |
|
||||
addModulus(e, true, b1, v1, m1) and
|
||||
addModulus(e, false, b2, v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 + v2, mod)
|
||||
|
|
||||
b = b1 and b2 instanceof Bounds::SemZeroBound
|
||||
or
|
||||
b = b2 and b1 instanceof Bounds::SemZeroBound
|
||||
)
|
||||
or
|
||||
exists(int v1, int v2, int m1, int m2 |
|
||||
subModulus(e, true, b, v1, m1) and
|
||||
subModulus(e, false, any(Bounds::SemZeroBound zb), v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 - v2, mod)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(int v1, int v2, int m1, int m2 |
|
||||
subModulus(e, true, b, v1, m1) and
|
||||
subModulus(e, false, any(SemZeroBound zb), v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 - v2, mod)
|
||||
}
|
||||
|
||||
private predicate condExprBranchModulus(
|
||||
SemConditionalExpr cond, boolean branch, Bounds::SemBound b, int val, int mod
|
||||
) {
|
||||
semExprModulus(cond.getBranchExpr(branch), b, val, mod)
|
||||
}
|
||||
|
||||
private predicate addModulus(SemExpr add, boolean isLeft, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstAddition(add, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate condExprBranchModulus(
|
||||
SemConditionalExpr cond, boolean branch, SemBound b, int val, int mod
|
||||
) {
|
||||
semExprModulus(cond.getBranchExpr(branch), b, val, mod)
|
||||
}
|
||||
|
||||
private predicate addModulus(SemExpr add, boolean isLeft, SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstAddition(add, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
private predicate subModulus(SemExpr sub, boolean isLeft, SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstSubtraction(sub, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
|
||||
* in an arbitrary 1-based numbering of the input edges to `phi`.
|
||||
*/
|
||||
private predicate rankedPhiInput(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
edge =
|
||||
rank[r](SemSsaReadPositionPhiInputEdge e |
|
||||
e.phiInput(phi, _)
|
||||
|
|
||||
e order by e.getOrigBlock().getUniqueId()
|
||||
private predicate subModulus(SemExpr sub, boolean isLeft, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstSubtraction(sub, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
|
||||
* in an arbitrary 1-based numbering of the input edges to `phi`.
|
||||
*/
|
||||
private predicate rankedPhiInput(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
edge =
|
||||
rank[r](SemSsaReadPositionPhiInputEdge e |
|
||||
e.phiInput(phi, _)
|
||||
|
|
||||
e order by e.getOrigBlock().getUniqueId()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,830 +1,24 @@
|
||||
/**
|
||||
* Provides classes and predicates for range analysis.
|
||||
*
|
||||
* An inferred bound can either be a specific integer, the abstract value of an
|
||||
* SSA variable, or the abstract value of an interesting expression. The latter
|
||||
* category includes array lengths that are not SSA variables.
|
||||
*
|
||||
* If an inferred bound relies directly on a condition, then this condition is
|
||||
* reported as the reason for the bound.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This library tackles range analysis as a flow problem. Consider e.g.:
|
||||
* ```
|
||||
* len = arr.length;
|
||||
* if (x < len) { ... y = x-1; ... y ... }
|
||||
* ```
|
||||
* In this case we would like to infer `y <= arr.length - 2`, and this is
|
||||
* accomplished by tracking the bound through a sequence of steps:
|
||||
* ```
|
||||
* arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y
|
||||
* ```
|
||||
*
|
||||
* In its simplest form the step relation `E1 --> E2` relates two expressions
|
||||
* such that `E1 <= B` implies `E2 <= B` for any `B` (with a second separate
|
||||
* step relation handling lower bounds). Examples of such steps include
|
||||
* assignments `E2 = E1` and conditions `x <= E1` where `E2` is a use of `x`
|
||||
* guarded by the condition.
|
||||
*
|
||||
* In order to handle subtractions and additions with constants, and strict
|
||||
* comparisons, the step relation is augmented with an integer delta. With this
|
||||
* generalization `E1 --(delta)--> E2` relates two expressions and an integer
|
||||
* such that `E1 <= B` implies `E2 <= B + delta` for any `B`. This corresponds
|
||||
* to the predicate `boundFlowStep`.
|
||||
*
|
||||
* The complete range analysis is then implemented as the transitive closure of
|
||||
* the step relation summing the deltas along the way. If `E1` transitively
|
||||
* steps to `E2`, `delta` is the sum of deltas along the path, and `B` is an
|
||||
* interesting bound equal to the value of `E1` then `E2 <= B + delta`. This
|
||||
* corresponds to the predicate `bounded`.
|
||||
*
|
||||
* Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`.
|
||||
* There are essentially two cases:
|
||||
* - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`.
|
||||
* - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`.
|
||||
* The first case is for whenever a bound can be proven without taking looping
|
||||
* into account. The second case is relevant when `x2` comes from a back-edge
|
||||
* where we can prove that the variable has been non-increasing through the
|
||||
* loop-iteration as this means that any upper bound that holds prior to the
|
||||
* loop also holds for the variable during the loop.
|
||||
* This generalizes to a phi node with `n` inputs, so if
|
||||
* `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we
|
||||
* also have `x0 <= B + delta` if we can prove either:
|
||||
* - `xj <= B + d` with `d <= delta` or
|
||||
* - `xj <= x0 + d` with `d <= 0`
|
||||
* for each input `xj`.
|
||||
*
|
||||
* As all inferred bounds can be related directly to a path in the source code
|
||||
* the only source of non-termination is if successive redundant (and thereby
|
||||
* increasingly worse) bounds are calculated along a loop in the source code.
|
||||
* We prevent this by weakening the bound to a small finite set of bounds when
|
||||
* a path follows a second back-edge (we postpone weakening till the second
|
||||
* back-edge as a precise bound might require traversing a loop once).
|
||||
*/
|
||||
|
||||
private import RangeAnalysisSpecific as Specific
|
||||
private import RangeAnalysisStage
|
||||
private import RangeAnalysisSpecific
|
||||
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
private import RangeUtils
|
||||
private import SignAnalysisCommon
|
||||
private import ModulusAnalysis
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import experimental.semmle.code.cpp.semantic.SemanticBound as SemanticBound
|
||||
|
||||
cached
|
||||
private module RangeAnalysisCache {
|
||||
cached
|
||||
module RangeAnalysisPublic {
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e`.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*
|
||||
* The reason for the bound is given by `reason` and may be either a condition
|
||||
* or `NoReason` if the bound was proven directly without the use of a bounding
|
||||
* condition.
|
||||
*/
|
||||
cached
|
||||
predicate semBounded(SemExpr e, SemBound b, int delta, boolean upper, SemReason reason) {
|
||||
bounded(e, b, delta, upper, _, _, reason) and
|
||||
bestBound(e, b, delta, upper)
|
||||
}
|
||||
module Bounds implements BoundSig<FloatDelta> {
|
||||
class SemBound instanceof SemanticBound::SemBound {
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
SemExpr getExpr(float delta) { result = super.getExpr(delta) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`.
|
||||
*/
|
||||
cached
|
||||
predicate possibleReason(SemGuard guard) {
|
||||
guard = boundFlowCond(_, _, _, _, _) or guard = semEqFlowCond(_, _, _, _, _)
|
||||
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
|
||||
|
||||
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
|
||||
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
}
|
||||
}
|
||||
|
||||
private import RangeAnalysisCache
|
||||
import RangeAnalysisPublic
|
||||
private module CppRangeAnalysis =
|
||||
RangeStage<FloatDelta, Bounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e` and this is the best such delta.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*/
|
||||
private predicate bestBound(SemExpr e, SemBound b, int delta, boolean upper) {
|
||||
delta = min(int d | bounded(e, b, d, upper, _, _, _)) and upper = true
|
||||
or
|
||||
delta = max(int d | bounded(e, b, d, upper, _, _, _)) and upper = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `comp` corresponds to:
|
||||
* - `upper = true` : `v <= e + delta` or `v < e + delta`
|
||||
* - `upper = false` : `v >= e + delta` or `v > e + delta`
|
||||
*/
|
||||
private predicate boundCondition(
|
||||
SemRelationalExpr comp, SemSsaVariable v, SemExpr e, int delta, boolean upper
|
||||
) {
|
||||
comp.getLesserOperand() = semSsaRead(v, delta) and e = comp.getGreaterOperand() and upper = true
|
||||
or
|
||||
comp.getGreaterOperand() = semSsaRead(v, delta) and e = comp.getLesserOperand() and upper = false
|
||||
or
|
||||
exists(SemSubExpr sub, SemConstantIntegerExpr c, int d |
|
||||
// (v - d) - e < c
|
||||
comp.getLesserOperand() = sub and
|
||||
comp.getGreaterOperand() = c and
|
||||
sub.getLeftOperand() = semSsaRead(v, d) and
|
||||
sub.getRightOperand() = e and
|
||||
upper = true and
|
||||
delta = d + c.getIntValue()
|
||||
or
|
||||
// (v - d) - e > c
|
||||
comp.getGreaterOperand() = sub and
|
||||
comp.getLesserOperand() = c and
|
||||
sub.getLeftOperand() = semSsaRead(v, d) and
|
||||
sub.getRightOperand() = e and
|
||||
upper = false and
|
||||
delta = d + c.getIntValue()
|
||||
or
|
||||
// e - (v - d) < c
|
||||
comp.getLesserOperand() = sub and
|
||||
comp.getGreaterOperand() = c and
|
||||
sub.getLeftOperand() = e and
|
||||
sub.getRightOperand() = semSsaRead(v, d) and
|
||||
upper = false and
|
||||
delta = d - c.getIntValue()
|
||||
or
|
||||
// e - (v - d) > c
|
||||
comp.getGreaterOperand() = sub and
|
||||
comp.getLesserOperand() = c and
|
||||
sub.getLeftOperand() = e and
|
||||
sub.getRightOperand() = semSsaRead(v, d) and
|
||||
upper = true and
|
||||
delta = d - c.getIntValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `comp` is a comparison between `x` and `y` for which `y - x` has a
|
||||
* fixed value modulo some `mod > 1`, such that the comparison can be
|
||||
* strengthened by `strengthen` when evaluating to `testIsTrue`.
|
||||
*/
|
||||
private predicate modulusComparison(SemRelationalExpr comp, boolean testIsTrue, int strengthen) {
|
||||
exists(
|
||||
SemBound b, int v1, int v2, int mod1, int mod2, int mod, boolean resultIsStrict, int d, int k
|
||||
|
|
||||
// If `x <= y` and `x =(mod) b + v1` and `y =(mod) b + v2` then
|
||||
// `0 <= y - x =(mod) v2 - v1`. By choosing `k =(mod) v2 - v1` with
|
||||
// `0 <= k < mod` we get `k <= y - x`. If the resulting comparison is
|
||||
// strict then the strengthening amount is instead `k - 1` modulo `mod`:
|
||||
// `x < y` means `0 <= y - x - 1 =(mod) k - 1` so `k - 1 <= y - x - 1` and
|
||||
// thus `k - 1 < y - x` with `0 <= k - 1 < mod`.
|
||||
semExprModulus(comp.getLesserOperand(), b, v1, mod1) and
|
||||
semExprModulus(comp.getGreaterOperand(), b, v2, mod2) and
|
||||
mod = mod1.gcd(mod2) and
|
||||
mod != 1 and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
(
|
||||
if comp.isStrict()
|
||||
then resultIsStrict = testIsTrue
|
||||
else resultIsStrict = testIsTrue.booleanNot()
|
||||
) and
|
||||
(
|
||||
resultIsStrict = true and d = 1
|
||||
or
|
||||
resultIsStrict = false and d = 0
|
||||
) and
|
||||
(
|
||||
testIsTrue = true and k = v2 - v1
|
||||
or
|
||||
testIsTrue = false and k = v1 - v2
|
||||
) and
|
||||
strengthen = (((k - d) % mod) + mod) % mod
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition that tests whether `v` is bounded by `e + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `upper = true` : `v <= e + delta`
|
||||
* - `upper = false` : `v >= e + delta`
|
||||
*/
|
||||
private SemGuard boundFlowCond(
|
||||
SemSsaVariable v, SemExpr e, int delta, boolean upper, boolean testIsTrue
|
||||
) {
|
||||
exists(
|
||||
SemRelationalExpr comp, int d1, int d2, int d3, int strengthen, boolean compIsUpper,
|
||||
boolean resultIsStrict
|
||||
|
|
||||
comp = result.asExpr() and
|
||||
boundCondition(comp, v, e, d1, compIsUpper) and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
upper = compIsUpper.booleanXor(testIsTrue.booleanNot()) and
|
||||
(
|
||||
if comp.isStrict()
|
||||
then resultIsStrict = testIsTrue
|
||||
else resultIsStrict = testIsTrue.booleanNot()
|
||||
) and
|
||||
(
|
||||
if
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemIntegerType or
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemAddressType
|
||||
then
|
||||
upper = true and strengthen = -1
|
||||
or
|
||||
upper = false and strengthen = 1
|
||||
else strengthen = 0
|
||||
) and
|
||||
(
|
||||
exists(int k | modulusComparison(comp, testIsTrue, k) and d2 = strengthen * k)
|
||||
or
|
||||
not modulusComparison(comp, testIsTrue, _) and d2 = 0
|
||||
) and
|
||||
// A strict inequality `x < y` can be strengthened to `x <= y - 1`.
|
||||
(
|
||||
resultIsStrict = true and d3 = strengthen
|
||||
or
|
||||
resultIsStrict = false and d3 = 0
|
||||
) and
|
||||
delta = d1 + d2 + d3
|
||||
)
|
||||
or
|
||||
exists(boolean testIsTrue0 |
|
||||
semImplies_v2(result, testIsTrue, boundFlowCond(v, e, delta, upper, testIsTrue0), testIsTrue0)
|
||||
)
|
||||
or
|
||||
result = semEqFlowCond(v, e, delta, true, testIsTrue) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
// guard that tests whether `v2` is bounded by `e + delta + d1 - d2` and
|
||||
// exists a guard `guardEq` such that `v = v2 - d1 + d2`.
|
||||
exists(SemSsaVariable v2, SemGuard guardEq, boolean eqIsTrue, int d1, int d2 |
|
||||
guardEq = semEqFlowCond(v, semSsaRead(v2, d1), d2, true, eqIsTrue) and
|
||||
result = boundFlowCond(v2, e, delta + d1 - d2, upper, testIsTrue) and
|
||||
// guardEq needs to control guard
|
||||
guardEq.directlyControls(result.getBasicBlock(), eqIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSemReason =
|
||||
TSemNoReason() or
|
||||
TSemCondReason(SemGuard guard) { possibleReason(guard) }
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound. This can either be `CondReason` if the bound
|
||||
* is due to a specific condition, or `NoReason` if the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
abstract class SemReason extends TSemReason {
|
||||
/** Gets a textual representation of this reason. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
class SemNoReason extends SemReason, TSemNoReason {
|
||||
override string toString() { result = "NoReason" }
|
||||
}
|
||||
|
||||
/** A reason for an inferred bound pointing to a condition. */
|
||||
class SemCondReason extends SemReason, TSemCondReason {
|
||||
/** Gets the condition that is the reason for the bound. */
|
||||
SemGuard getCond() { this = TSemCondReason(result) }
|
||||
|
||||
override string toString() { result = getCond().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e + delta` is a valid bound for `v` at `pos`.
|
||||
* - `upper = true` : `v <= e + delta`
|
||||
* - `upper = false` : `v >= e + delta`
|
||||
*/
|
||||
private predicate boundFlowStepSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta, boolean upper, SemReason reason
|
||||
) {
|
||||
semSsaUpdateStep(v, e, delta) and
|
||||
pos.hasReadOfVar(v) and
|
||||
(upper = true or upper = false) and
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = boundFlowCond(v, e, delta, upper, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
|
||||
reason = TSemCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `v != e + delta` at `pos` and `v` is of integral type. */
|
||||
private predicate unequalFlowStepIntegralSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta, SemReason reason
|
||||
) {
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemIntegerType and
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = semEqFlowCond(v, e, delta, false, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
|
||||
reason = TSemCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that does conversion, boxing, or unboxing
|
||||
*/
|
||||
private class ConvertOrBoxExpr extends SemUnaryExpr {
|
||||
ConvertOrBoxExpr() {
|
||||
this instanceof SemConvertExpr
|
||||
or
|
||||
this instanceof SemBoxExpr
|
||||
or
|
||||
this instanceof SemUnboxExpr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cast that can be ignored for the purpose of range analysis.
|
||||
*/
|
||||
private class SafeCastExpr extends ConvertOrBoxExpr {
|
||||
SafeCastExpr() {
|
||||
conversionCannotOverflow(getTrackedType(pragma[only_bind_into](getOperand())),
|
||||
getTrackedType(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `typ` is a small integral type with the given lower and upper bounds.
|
||||
*/
|
||||
private predicate typeBound(SemIntegerType typ, int lowerbound, int upperbound) {
|
||||
exists(int bitSize | bitSize = typ.getByteSize() * 8 |
|
||||
bitSize < 32 and
|
||||
(
|
||||
if typ.isSigned()
|
||||
then (
|
||||
upperbound = 1.bitShiftLeft(bitSize - 1) - 1 and
|
||||
lowerbound = -upperbound - 1
|
||||
) else (
|
||||
lowerbound = 0 and
|
||||
upperbound = 1.bitShiftLeft(bitSize) - 1
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A cast to a small integral type that may overflow or underflow.
|
||||
*/
|
||||
private class NarrowingCastExpr extends ConvertOrBoxExpr {
|
||||
NarrowingCastExpr() {
|
||||
not this instanceof SafeCastExpr and
|
||||
typeBound(getTrackedType(this), _, _)
|
||||
}
|
||||
|
||||
/** Gets the lower bound of the resulting type. */
|
||||
int getLowerBound() { typeBound(getTrackedType(this), result, _) }
|
||||
|
||||
/** Gets the upper bound of the resulting type. */
|
||||
int getUpperBound() { typeBound(getTrackedType(this), _, result) }
|
||||
}
|
||||
|
||||
/** Holds if `e >= 1` as determined by sign analysis. */
|
||||
private predicate strictlyPositiveIntegralExpr(SemExpr e) {
|
||||
semStrictlyPositive(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/** Holds if `e <= -1` as determined by sign analysis. */
|
||||
private predicate strictlyNegativeIntegralExpr(SemExpr e) {
|
||||
semStrictlyNegative(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e1 + delta` is a valid bound for `e2`.
|
||||
* - `upper = true` : `e2 <= e1 + delta`
|
||||
* - `upper = false` : `e2 >= e1 + delta`
|
||||
*/
|
||||
private predicate boundFlowStep(SemExpr e2, SemExpr e1, int delta, boolean upper) {
|
||||
semValueFlowStep(e2, e1, delta) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
e2.(SafeCastExpr).getOperand() = e1 and
|
||||
delta = 0 and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
|
||||
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
|
||||
not x instanceof SemConstantIntegerExpr and
|
||||
not e1 instanceof SemConstantIntegerExpr and
|
||||
if strictlyPositiveIntegralExpr(x)
|
||||
then upper = false and delta = 1
|
||||
else
|
||||
if semPositive(x)
|
||||
then upper = false and delta = 0
|
||||
else
|
||||
if strictlyNegativeIntegralExpr(x)
|
||||
then upper = true and delta = -1
|
||||
else
|
||||
if semNegative(x)
|
||||
then upper = true and delta = 0
|
||||
else none()
|
||||
)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
|
||||
not x instanceof SemConstantIntegerExpr and
|
||||
if strictlyPositiveIntegralExpr(x)
|
||||
then upper = true and delta = -1
|
||||
else
|
||||
if semPositive(x)
|
||||
then upper = true and delta = 0
|
||||
else
|
||||
if strictlyNegativeIntegralExpr(x)
|
||||
then upper = false and delta = 1
|
||||
else
|
||||
if semNegative(x)
|
||||
then upper = false and delta = 0
|
||||
else none()
|
||||
)
|
||||
or
|
||||
e2.(SemRemExpr).getRightOperand() = e1 and
|
||||
semPositive(e1) and
|
||||
delta = -1 and
|
||||
upper = true
|
||||
or
|
||||
e2.(SemRemExpr).getLeftOperand() = e1 and semPositive(e1) and delta = 0 and upper = true
|
||||
or
|
||||
e2.(SemBitAndExpr).getAnOperand() = e1 and
|
||||
semPositive(e1) and
|
||||
delta = 0 and
|
||||
upper = true
|
||||
or
|
||||
e2.(SemBitOrExpr).getAnOperand() = e1 and
|
||||
semPositive(e2) and
|
||||
delta = 0 and
|
||||
upper = false
|
||||
or
|
||||
Specific::hasBound(e2, e1, delta, upper)
|
||||
}
|
||||
|
||||
/** Holds if `e2 = e1 * factor` and `factor > 0`. */
|
||||
private predicate boundFlowStepMul(SemExpr e2, SemExpr e1, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 |
|
||||
e2.(SemMulExpr).hasOperands(e1, c) and factor = k
|
||||
or
|
||||
exists(SemShiftLeftExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e2 = e1 / factor` and `factor > 0`.
|
||||
*
|
||||
* This conflates division, right shift, and unsigned right shift and is
|
||||
* therefore only valid for non-negative numbers.
|
||||
*/
|
||||
private predicate boundFlowStepDiv(SemExpr e2, SemExpr e1, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 |
|
||||
exists(SemDivExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = k
|
||||
)
|
||||
or
|
||||
exists(SemShiftRightExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
|
||||
)
|
||||
or
|
||||
exists(SemShiftRightUnsignedExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `v` at `pos`.
|
||||
* - `upper = true` : `v <= b + delta`
|
||||
* - `upper = false` : `v >= b + delta`
|
||||
*/
|
||||
private predicate boundedSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int delta, boolean upper,
|
||||
boolean fromBackEdge, int origdelta, SemReason reason
|
||||
) {
|
||||
exists(SemExpr mid, int d1, int d2, SemReason r1, SemReason r2 |
|
||||
boundFlowStepSsa(v, pos, mid, d1, upper, r1) and
|
||||
bounded(mid, b, d2, upper, fromBackEdge, origdelta, r2) and
|
||||
// upper = true: v <= mid + d1 <= b + d1 + d2 = b + delta
|
||||
// upper = false: v >= mid + d1 >= b + d1 + d2 = b + delta
|
||||
delta = d1 + d2 and
|
||||
(if r1 instanceof SemNoReason then reason = r2 else reason = r1)
|
||||
)
|
||||
or
|
||||
exists(int d, SemReason r1, SemReason r2 |
|
||||
boundedSsa(v, pos, b, d, upper, fromBackEdge, origdelta, r2) or
|
||||
boundedPhi(v, b, d, upper, fromBackEdge, origdelta, r2)
|
||||
|
|
||||
unequalIntegralSsa(v, pos, b, d, r1) and
|
||||
(
|
||||
upper = true and delta = d - 1
|
||||
or
|
||||
upper = false and delta = d + 1
|
||||
) and
|
||||
(
|
||||
reason = r1
|
||||
or
|
||||
reason = r2 and not r2 instanceof SemNoReason
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v != b + delta` at `pos` and `v` is of integral type.
|
||||
*/
|
||||
private predicate unequalIntegralSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int delta, SemReason reason
|
||||
) {
|
||||
exists(SemExpr e, int d1, int d2 |
|
||||
unequalFlowStepIntegralSsa(v, pos, e, d1, reason) and
|
||||
boundedUpper(e, b, d1) and
|
||||
boundedLower(e, b, d2) and
|
||||
delta = d2 + d1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is an upper bound for `e`.
|
||||
*
|
||||
* This predicate only exists to prevent a bad standard order in `unequalIntegralSsa`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedUpper(SemExpr e, SemBound b, int delta) {
|
||||
bounded(e, b, delta, true, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a lower bound for `e`.
|
||||
*
|
||||
* This predicate only exists to prevent a bad standard order in `unequalIntegralSsa`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedLower(SemExpr e, SemBound b, int delta) {
|
||||
bounded(e, b, delta, false, _, _, _)
|
||||
}
|
||||
|
||||
/** Weakens a delta to lie in the range `[-1..1]`. */
|
||||
bindingset[delta, upper]
|
||||
private int weakenDelta(boolean upper, int delta) {
|
||||
delta in [-1 .. 1] and result = delta
|
||||
or
|
||||
upper = true and result = -1 and delta < -1
|
||||
or
|
||||
upper = false and result = 1 and delta > 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `inp` when used as an input to
|
||||
* `phi` along `edge`.
|
||||
* - `upper = true` : `inp <= b + delta`
|
||||
* - `upper = false` : `inp >= b + delta`
|
||||
*/
|
||||
private predicate boundedPhiInp(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, SemBound b, int delta,
|
||||
boolean upper, boolean fromBackEdge, int origdelta, SemReason reason
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
exists(int d, boolean fromBackEdge0 |
|
||||
boundedSsa(inp, edge, b, d, upper, fromBackEdge0, origdelta, reason)
|
||||
or
|
||||
boundedPhi(inp, b, d, upper, fromBackEdge0, origdelta, reason)
|
||||
or
|
||||
b.(SemSsaBound).getAVariable() = inp and
|
||||
d = 0 and
|
||||
(upper = true or upper = false) and
|
||||
fromBackEdge0 = false and
|
||||
origdelta = 0 and
|
||||
reason = TSemNoReason()
|
||||
|
|
||||
if semBackEdge(phi, inp, edge)
|
||||
then
|
||||
fromBackEdge = true and
|
||||
(
|
||||
fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta
|
||||
or
|
||||
fromBackEdge0 = false and delta = d
|
||||
)
|
||||
else (
|
||||
delta = d and fromBackEdge = fromBackEdge0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `inp` when used as an input to
|
||||
* `phi` along `edge`.
|
||||
* - `upper = true` : `inp <= b + delta`
|
||||
* - `upper = false` : `inp >= b + delta`
|
||||
*
|
||||
* Equivalent to `boundedPhiInp(phi, inp, edge, b, delta, upper, _, _, _)`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate boundedPhiInp1(
|
||||
SemSsaPhiNode phi, SemBound b, boolean upper, SemSsaVariable inp,
|
||||
SemSsaReadPositionPhiInputEdge edge, int delta
|
||||
) {
|
||||
boundedPhiInp(phi, inp, edge, b, delta, upper, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `phi` is a valid bound for `inp` when used as an input to `phi`
|
||||
* along `edge`.
|
||||
* - `upper = true` : `inp <= phi`
|
||||
* - `upper = false` : `inp >= phi`
|
||||
*/
|
||||
private predicate selfBoundedPhiInp(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, boolean upper
|
||||
) {
|
||||
exists(int d, SemSsaBound phibound |
|
||||
phibound.getAVariable() = phi and
|
||||
boundedPhiInp(phi, inp, edge, phibound, d, upper, _, _, _) and
|
||||
(
|
||||
upper = true and d <= 0
|
||||
or
|
||||
upper = false and d >= 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for some input, `inp`, to `phi`, and
|
||||
* thus a candidate bound for `phi`.
|
||||
* - `upper = true` : `inp <= b + delta`
|
||||
* - `upper = false` : `inp >= b + delta`
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate boundedPhiCand(
|
||||
SemSsaPhiNode phi, boolean upper, SemBound b, int delta, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
boundedPhiInp(phi, _, _, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the candidate bound `b + delta` for `phi` is valid for the phi input
|
||||
* `inp` along `edge`.
|
||||
*/
|
||||
private predicate boundedPhiCandValidForEdge(
|
||||
SemSsaPhiNode phi, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge
|
||||
) {
|
||||
boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and
|
||||
(
|
||||
exists(int d | boundedPhiInp1(phi, b, upper, inp, edge, d) | upper = true and d <= delta)
|
||||
or
|
||||
exists(int d | boundedPhiInp1(phi, b, upper, inp, edge, d) | upper = false and d >= delta)
|
||||
or
|
||||
selfBoundedPhiInp(phi, inp, edge, upper)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `phi`.
|
||||
* - `upper = true` : `phi <= b + delta`
|
||||
* - `upper = false` : `phi >= b + delta`
|
||||
*/
|
||||
private predicate boundedPhi(
|
||||
SemSsaPhiNode phi, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
forex(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge | edge.phiInput(phi, inp) |
|
||||
boundedPhiCandValidForEdge(phi, b, delta, upper, fromBackEdge, origdelta, reason, inp, edge)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` has an upper (for `upper = true`) or lower
|
||||
* (for `upper = false`) bound of `b`.
|
||||
*/
|
||||
private predicate baseBound(SemExpr e, int b, boolean upper) {
|
||||
Specific::hasConstantBound(e, b, upper)
|
||||
or
|
||||
upper = false and
|
||||
b = 0 and
|
||||
semPositive(e.(SemBitAndExpr).getAnOperand()) and
|
||||
// REVIEW: We let the language opt out here to preserve original results.
|
||||
not Specific::ignoreZeroLowerBound(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value being cast has an upper (for `upper = true`) or lower
|
||||
* (for `upper = false`) bound within the bounds of the resulting type.
|
||||
* For `upper = true` this means that the cast will not overflow and for
|
||||
* `upper = false` this means that the cast will not underflow.
|
||||
*/
|
||||
private predicate safeNarrowingCast(NarrowingCastExpr cast, boolean upper) {
|
||||
exists(int bound | bounded(cast.getOperand(), any(SemZeroBound zb), bound, upper, _, _, _) |
|
||||
upper = true and bound <= cast.getUpperBound()
|
||||
or
|
||||
upper = false and bound >= cast.getLowerBound()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate boundedCastExpr(
|
||||
NarrowingCastExpr cast, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
bounded(cast.getOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e`.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*/
|
||||
private predicate bounded(
|
||||
SemExpr e, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
not Specific::ignoreExprBound(e) and
|
||||
(
|
||||
e = b.getExpr(delta) and
|
||||
(upper = true or upper = false) and
|
||||
fromBackEdge = false and
|
||||
origdelta = delta and
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
baseBound(e, delta, upper) and
|
||||
b instanceof SemZeroBound and
|
||||
fromBackEdge = false and
|
||||
origdelta = delta and
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
boundedSsa(v, bb, b, delta, upper, fromBackEdge, origdelta, reason) and
|
||||
e = v.getAUse() and
|
||||
bb.getBlock() = e.getBasicBlock()
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int d1, int d2 |
|
||||
boundFlowStep(e, mid, d1, upper) and
|
||||
// Constants have easy, base-case bounds, so let's not infer any recursive bounds.
|
||||
not e instanceof SemConstantIntegerExpr and
|
||||
bounded(mid, b, d2, upper, fromBackEdge, origdelta, reason) and
|
||||
// upper = true: e <= mid + d1 <= b + d1 + d2 = b + delta
|
||||
// upper = false: e >= mid + d1 >= b + d1 + d2 = b + delta
|
||||
delta = d1 + d2
|
||||
)
|
||||
or
|
||||
exists(SemSsaPhiNode phi |
|
||||
boundedPhi(phi, b, delta, upper, fromBackEdge, origdelta, reason) and
|
||||
e = phi.getAUse()
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int factor, int d |
|
||||
boundFlowStepMul(e, mid, factor) and
|
||||
not e instanceof SemConstantIntegerExpr and
|
||||
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
|
||||
b instanceof SemZeroBound and
|
||||
delta = d * factor
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int factor, int d |
|
||||
boundFlowStepDiv(e, mid, factor) and
|
||||
not e instanceof SemConstantIntegerExpr and
|
||||
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
|
||||
b instanceof SemZeroBound and
|
||||
d >= 0 and
|
||||
delta = d / factor
|
||||
)
|
||||
or
|
||||
exists(NarrowingCastExpr cast |
|
||||
cast = e and
|
||||
safeNarrowingCast(cast, upper.booleanNot()) and
|
||||
boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
SemConditionalExpr cond, int d1, int d2, boolean fbe1, boolean fbe2, int od1, int od2,
|
||||
SemReason r1, SemReason r2
|
||||
|
|
||||
cond = e and
|
||||
boundedConditionalExpr(cond, b, upper, true, d1, fbe1, od1, r1) and
|
||||
boundedConditionalExpr(cond, b, upper, false, d2, fbe2, od2, r2) and
|
||||
(
|
||||
delta = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1
|
||||
or
|
||||
delta = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2
|
||||
)
|
||||
|
|
||||
upper = true and delta = d1.maximum(d2)
|
||||
or
|
||||
upper = false and delta = d1.minimum(d2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate boundedConditionalExpr(
|
||||
SemConditionalExpr cond, SemBound b, boolean upper, boolean branch, int delta,
|
||||
boolean fromBackEdge, int origdelta, SemReason reason
|
||||
) {
|
||||
bounded(cond.getBranchExpr(branch), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
import CppRangeAnalysis
|
||||
|
||||
@@ -3,86 +3,90 @@
|
||||
*/
|
||||
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import RangeAnalysisStage
|
||||
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadCopy(SemExpr e) { none() }
|
||||
module CppLangImpl implements LangSig<FloatDelta> {
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadCopy(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore the bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreExprBound(SemExpr e) { none() }
|
||||
/**
|
||||
* Ignore the bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreExprBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore any inferred zero lower bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreZeroLowerBound(SemExpr e) { none() }
|
||||
/**
|
||||
* Ignore any inferred zero lower bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreZeroLowerBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
|
||||
/**
|
||||
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
|
||||
|
||||
/**
|
||||
* Adds additional results to `ssaRead()` that are specific to Java.
|
||||
*
|
||||
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
|
||||
* in exactly the same way as the old Java implementation. Once the new implementation matches the
|
||||
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
|
||||
* or not they come from a post-increment/decrement expression.
|
||||
*/
|
||||
SemExpr specificSsaRead(SemSsaVariable v, int delta) { none() }
|
||||
/**
|
||||
* Adds additional results to `ssaRead()` that are specific to Java.
|
||||
*
|
||||
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
|
||||
* in exactly the same way as the old Java implementation. Once the new implementation matches the
|
||||
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
|
||||
* or not they come from a post-increment/decrement expression.
|
||||
*/
|
||||
SemExpr specificSsaRead(SemSsaVariable v, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
|
||||
*/
|
||||
predicate hasConstantBound(SemExpr e, int bound, boolean upper) { none() }
|
||||
/**
|
||||
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
|
||||
*/
|
||||
predicate hasConstantBound(SemExpr e, float bound, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
|
||||
*/
|
||||
predicate hasBound(SemExpr e, SemExpr bound, int delta, boolean upper) { none() }
|
||||
/**
|
||||
* Holds if `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
|
||||
*/
|
||||
predicate hasBound(SemExpr e, SemExpr bound, float delta, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the value of `dest` is known to be `src + delta`.
|
||||
*/
|
||||
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, int delta) { none() }
|
||||
/**
|
||||
* Holds if the value of `dest` is known to be `src + delta`.
|
||||
*/
|
||||
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified expression,
|
||||
* if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateType(SemExpr e) { none() }
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified expression,
|
||||
* if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateType(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified source
|
||||
* variable, if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateTypeForSsaVariable(SemSsaVariable var) { none() }
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified source
|
||||
* variable, if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateTypeForSsaVariable(SemSsaVariable var) { none() }
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,133 +3,138 @@
|
||||
*/
|
||||
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import RangeAnalysisSpecific as Specific
|
||||
private import RangeAnalysisSpecific
|
||||
private import RangeAnalysisStage as Range
|
||||
private import ConstantAnalysis
|
||||
|
||||
/**
|
||||
* Gets an expression that equals `v - d`.
|
||||
*/
|
||||
SemExpr semSsaRead(SemSsaVariable v, int delta) {
|
||||
// There are various language-specific extension points that can be removed once we no longer
|
||||
// expect to match the original Java implementation's results exactly.
|
||||
result = v.getAUse() and delta = 0
|
||||
or
|
||||
exists(int d1, SemConstantIntegerExpr c |
|
||||
result.(SemAddExpr).hasOperands(semSsaRead(v, d1), c) and
|
||||
delta = d1 - c.getIntValue() and
|
||||
not Specific::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
exists(SemSubExpr sub, int d1, SemConstantIntegerExpr c |
|
||||
result = sub and
|
||||
sub.getLeftOperand() = semSsaRead(v, d1) and
|
||||
sub.getRightOperand() = c and
|
||||
delta = d1 + c.getIntValue() and
|
||||
not Specific::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
result = v.(SemSsaExplicitUpdate).getSourceExpr() and
|
||||
delta = 0 and
|
||||
not Specific::ignoreSsaReadAssignment(v)
|
||||
or
|
||||
result = Specific::specificSsaRead(v, delta)
|
||||
or
|
||||
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta) and
|
||||
not Specific::ignoreSsaReadCopy(result)
|
||||
or
|
||||
result.(SemStoreExpr).getOperand() = semSsaRead(v, delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition that tests whether `v` equals `e + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `isEq = true` : `v == e + delta`
|
||||
* - `isEq = false` : `v != e + delta`
|
||||
*/
|
||||
SemGuard semEqFlowCond(SemSsaVariable v, SemExpr e, int delta, boolean isEq, boolean testIsTrue) {
|
||||
exists(boolean eqpolarity |
|
||||
result.isEquality(semSsaRead(v, delta), e, eqpolarity) and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
eqpolarity.booleanXor(testIsTrue).booleanNot() = isEq
|
||||
)
|
||||
or
|
||||
exists(boolean testIsTrue0 |
|
||||
semImplies_v2(result, testIsTrue, semEqFlowCond(v, e, delta, isEq, testIsTrue0), testIsTrue0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is an `SsaExplicitUpdate` that equals `e + delta`.
|
||||
*/
|
||||
predicate semSsaUpdateStep(SemSsaExplicitUpdate v, SemExpr e, int delta) {
|
||||
exists(SemExpr defExpr | defExpr = v.getSourceExpr() |
|
||||
defExpr.(SemCopyValueExpr).getOperand() = e and delta = 0
|
||||
module RangeUtil<Range::DeltaSig D, Range::LangSig<D> Lang> implements Range::UtilSig<D> {
|
||||
/**
|
||||
* Gets an expression that equals `v - d`.
|
||||
*/
|
||||
SemExpr semSsaRead(SemSsaVariable v, D::Delta delta) {
|
||||
// There are various language-specific extension points that can be removed once we no longer
|
||||
// expect to match the original Java implementation's results exactly.
|
||||
result = v.getAUse() and delta = D::fromInt(0)
|
||||
or
|
||||
defExpr.(SemStoreExpr).getOperand() = e and delta = 0
|
||||
exists(D::Delta d1, SemConstantIntegerExpr c |
|
||||
result.(SemAddExpr).hasOperands(semSsaRead(v, d1), c) and
|
||||
delta = D::fromFloat(D::toFloat(d1) - c.getIntValue()) and
|
||||
not Lang::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
defExpr.(SemAddOneExpr).getOperand() = e and delta = 1
|
||||
exists(SemSubExpr sub, D::Delta d1, SemConstantIntegerExpr c |
|
||||
result = sub and
|
||||
sub.getLeftOperand() = semSsaRead(v, d1) and
|
||||
sub.getRightOperand() = c and
|
||||
delta = D::fromFloat(D::toFloat(d1) + c.getIntValue()) and
|
||||
not Lang::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
defExpr.(SemSubOneExpr).getOperand() = e and delta = -1
|
||||
result = v.(SemSsaExplicitUpdate).getSourceExpr() and
|
||||
delta = D::fromFloat(0) and
|
||||
not Lang::ignoreSsaReadAssignment(v)
|
||||
or
|
||||
e = defExpr and
|
||||
not (
|
||||
defExpr instanceof SemCopyValueExpr or
|
||||
defExpr instanceof SemStoreExpr or
|
||||
defExpr instanceof SemAddOneExpr or
|
||||
defExpr instanceof SemSubOneExpr
|
||||
) and
|
||||
delta = 0
|
||||
)
|
||||
}
|
||||
result = Lang::specificSsaRead(v, delta)
|
||||
or
|
||||
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta) and
|
||||
not Lang::ignoreSsaReadCopy(result)
|
||||
or
|
||||
result.(SemStoreExpr).getOperand() = semSsaRead(v, delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e1 + delta` equals `e2`.
|
||||
*/
|
||||
predicate semValueFlowStep(SemExpr e2, SemExpr e1, int delta) {
|
||||
e2.(SemCopyValueExpr).getOperand() = e1 and delta = 0
|
||||
or
|
||||
e2.(SemStoreExpr).getOperand() = e1 and delta = 0
|
||||
or
|
||||
e2.(SemAddOneExpr).getOperand() = e1 and delta = 1
|
||||
or
|
||||
e2.(SemSubOneExpr).getOperand() = e1 and delta = -1
|
||||
or
|
||||
Specific::additionalValueFlowStep(e2, e1, delta)
|
||||
or
|
||||
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
|
||||
x.(SemConstantIntegerExpr).getIntValue() = delta
|
||||
)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
x.(SemConstantIntegerExpr).getIntValue() = -delta
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Gets a condition that tests whether `v` equals `e + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `isEq = true` : `v == e + delta`
|
||||
* - `isEq = false` : `v != e + delta`
|
||||
*/
|
||||
SemGuard semEqFlowCond(
|
||||
SemSsaVariable v, SemExpr e, D::Delta delta, boolean isEq, boolean testIsTrue
|
||||
) {
|
||||
exists(boolean eqpolarity |
|
||||
result.isEquality(semSsaRead(v, delta), e, eqpolarity) and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
eqpolarity.booleanXor(testIsTrue).booleanNot() = isEq
|
||||
)
|
||||
or
|
||||
exists(boolean testIsTrue0 |
|
||||
semImplies_v2(result, testIsTrue, semEqFlowCond(v, e, delta, isEq, testIsTrue0), testIsTrue0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified expression's range information.
|
||||
*
|
||||
* Usually, this just `e.getSemType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedType(SemExpr e) {
|
||||
result = Specific::getAlternateType(e)
|
||||
or
|
||||
not exists(Specific::getAlternateType(e)) and result = e.getSemType()
|
||||
}
|
||||
/**
|
||||
* Holds if `v` is an `SsaExplicitUpdate` that equals `e + delta`.
|
||||
*/
|
||||
predicate semSsaUpdateStep(SemSsaExplicitUpdate v, SemExpr e, D::Delta delta) {
|
||||
exists(SemExpr defExpr | defExpr = v.getSourceExpr() |
|
||||
defExpr.(SemCopyValueExpr).getOperand() = e and delta = D::fromFloat(0)
|
||||
or
|
||||
defExpr.(SemStoreExpr).getOperand() = e and delta = D::fromFloat(0)
|
||||
or
|
||||
defExpr.(SemAddOneExpr).getOperand() = e and delta = D::fromFloat(1)
|
||||
or
|
||||
defExpr.(SemSubOneExpr).getOperand() = e and delta = D::fromFloat(-1)
|
||||
or
|
||||
e = defExpr and
|
||||
not (
|
||||
defExpr instanceof SemCopyValueExpr or
|
||||
defExpr instanceof SemStoreExpr or
|
||||
defExpr instanceof SemAddOneExpr or
|
||||
defExpr instanceof SemSubOneExpr
|
||||
) and
|
||||
delta = D::fromFloat(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified source variable's range information.
|
||||
*
|
||||
* Usually, this just `e.getType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedTypeForSsaVariable(SemSsaVariable var) {
|
||||
result = Specific::getAlternateTypeForSsaVariable(var)
|
||||
or
|
||||
not exists(Specific::getAlternateTypeForSsaVariable(var)) and result = var.getType()
|
||||
/**
|
||||
* Holds if `e1 + delta` equals `e2`.
|
||||
*/
|
||||
predicate semValueFlowStep(SemExpr e2, SemExpr e1, D::Delta delta) {
|
||||
e2.(SemCopyValueExpr).getOperand() = e1 and delta = D::fromFloat(0)
|
||||
or
|
||||
e2.(SemStoreExpr).getOperand() = e1 and delta = D::fromFloat(0)
|
||||
or
|
||||
e2.(SemAddOneExpr).getOperand() = e1 and delta = D::fromFloat(1)
|
||||
or
|
||||
e2.(SemSubOneExpr).getOperand() = e1 and delta = D::fromFloat(-1)
|
||||
or
|
||||
Lang::additionalValueFlowStep(e2, e1, delta)
|
||||
or
|
||||
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
|
||||
D::fromInt(x.(SemConstantIntegerExpr).getIntValue()) = delta
|
||||
)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
D::fromInt(-x.(SemConstantIntegerExpr).getIntValue()) = delta
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified expression's range information.
|
||||
*
|
||||
* Usually, this just `e.getSemType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedType(SemExpr e) {
|
||||
result = Lang::getAlternateType(e)
|
||||
or
|
||||
not exists(Lang::getAlternateType(e)) and result = e.getSemType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified source variable's range information.
|
||||
*
|
||||
* Usually, this just `e.getType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedTypeForSsaVariable(SemSsaVariable var) {
|
||||
result = Lang::getAlternateTypeForSsaVariable(var)
|
||||
or
|
||||
not exists(Lang::getAlternateTypeForSsaVariable(var)) and result = var.getType()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,488 +6,494 @@
|
||||
* three-valued domain `{negative, zero, positive}`.
|
||||
*/
|
||||
|
||||
private import RangeAnalysisStage
|
||||
private import SignAnalysisSpecific as Specific
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import RangeUtils
|
||||
private import Sign
|
||||
|
||||
/**
|
||||
* An SSA definition for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*/
|
||||
abstract private class SignDef instanceof SemSsaVariable {
|
||||
final string toString() { result = super.toString() }
|
||||
module SignAnalysis<DeltaSig D, UtilSig<D> Utils> {
|
||||
/**
|
||||
* An SSA definition for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*/
|
||||
abstract private class SignDef instanceof SemSsaVariable {
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/** Gets the possible signs of this SSA definition. */
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is computed based on standard flow. */
|
||||
abstract private class FlowSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
|
||||
private class ExplicitSignDef extends FlowSignDef instanceof SemSsaExplicitUpdate {
|
||||
final override Sign getSign() { result = semExprSign(super.getSourceExpr()) }
|
||||
}
|
||||
|
||||
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
|
||||
private class PhiSignDef extends FlowSignDef instanceof SemSsaPhiNode {
|
||||
final override Sign getSign() {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(this, inp) and
|
||||
result = semSsaSign(inp, edge)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*
|
||||
* Concrete implementations extend one of the following subclasses:
|
||||
* - `ConstantSignExpr`, for expressions with a compile-time constant value.
|
||||
* - `FlowSignExpr`, for expressions whose sign can be computed from the signs of their operands.
|
||||
* - `CustomsignExpr`, for expressions whose sign can be computed by a language-specific
|
||||
* implementation.
|
||||
*
|
||||
* If the same expression matches more than one of the above subclasses, the sign is computed as
|
||||
* follows:
|
||||
* - The sign of a `ConstantSignExpr` is computed solely from `ConstantSignExpr.getSign()`,
|
||||
* regardless of any other subclasses.
|
||||
* - If a non-`ConstantSignExpr` expression matches exactly one of `FlowSignExpr` or
|
||||
* `CustomSignExpr`, the sign is computed by that class' `getSign()` predicate.
|
||||
* - If a non-`ConstantSignExpr` expression matches both `FlowSignExpr` and `CustomSignExpr`, the
|
||||
* sign is the _intersection_ of the signs of those two classes' `getSign()` predicates. Thus,
|
||||
* both classes have the opportunity to _restrict_ the set of possible signs, not to generate new
|
||||
* possible signs.
|
||||
* - If an expression does not match any of the three subclasses, then it can have any sign.
|
||||
*
|
||||
* Note that the `getSign()` predicate is introduced only in subclasses of `SignExpr`.
|
||||
*/
|
||||
abstract class SignExpr instanceof SemExpr {
|
||||
SignExpr() { not Specific::ignoreExprSign(this) }
|
||||
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
/** An expression whose sign is determined by its constant numeric value. */
|
||||
private class ConstantSignExpr extends SignExpr {
|
||||
ConstantSignExpr() {
|
||||
this instanceof SemConstantIntegerExpr or
|
||||
exists(this.(SemNumericLiteralExpr).getApproximateFloatValue())
|
||||
/** Gets the possible signs of this SSA definition. */
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
final override Sign getSign() {
|
||||
exists(int i | this.(SemConstantIntegerExpr).getIntValue() = i |
|
||||
i < 0 and result = TNeg()
|
||||
/** An SSA definition whose sign is computed based on standard flow. */
|
||||
abstract private class FlowSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
|
||||
private class ExplicitSignDef extends FlowSignDef instanceof SemSsaExplicitUpdate {
|
||||
final override Sign getSign() { result = semExprSign(super.getSourceExpr()) }
|
||||
}
|
||||
|
||||
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
|
||||
private class PhiSignDef extends FlowSignDef instanceof SemSsaPhiNode {
|
||||
final override Sign getSign() {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(this, inp) and
|
||||
result = semSsaSign(inp, edge)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*
|
||||
* Concrete implementations extend one of the following subclasses:
|
||||
* - `ConstantSignExpr`, for expressions with a compile-time constant value.
|
||||
* - `FlowSignExpr`, for expressions whose sign can be computed from the signs of their operands.
|
||||
* - `CustomsignExpr`, for expressions whose sign can be computed by a language-specific
|
||||
* implementation.
|
||||
*
|
||||
* If the same expression matches more than one of the above subclasses, the sign is computed as
|
||||
* follows:
|
||||
* - The sign of a `ConstantSignExpr` is computed solely from `ConstantSignExpr.getSign()`,
|
||||
* regardless of any other subclasses.
|
||||
* - If a non-`ConstantSignExpr` expression matches exactly one of `FlowSignExpr` or
|
||||
* `CustomSignExpr`, the sign is computed by that class' `getSign()` predicate.
|
||||
* - If a non-`ConstantSignExpr` expression matches both `FlowSignExpr` and `CustomSignExpr`, the
|
||||
* sign is the _intersection_ of the signs of those two classes' `getSign()` predicates. Thus,
|
||||
* both classes have the opportunity to _restrict_ the set of possible signs, not to generate new
|
||||
* possible signs.
|
||||
* - If an expression does not match any of the three subclasses, then it can have any sign.
|
||||
*
|
||||
* Note that the `getSign()` predicate is introduced only in subclasses of `SignExpr`.
|
||||
*/
|
||||
abstract class SignExpr instanceof SemExpr {
|
||||
SignExpr() { not Specific::ignoreExprSign(this) }
|
||||
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
/** An expression whose sign is determined by its constant numeric value. */
|
||||
private class ConstantSignExpr extends SignExpr {
|
||||
ConstantSignExpr() {
|
||||
this instanceof SemConstantIntegerExpr or
|
||||
exists(this.(SemNumericLiteralExpr).getApproximateFloatValue())
|
||||
}
|
||||
|
||||
final override Sign getSign() {
|
||||
exists(int i | this.(SemConstantIntegerExpr).getIntValue() = i |
|
||||
i < 0 and result = TNeg()
|
||||
or
|
||||
i = 0 and result = TZero()
|
||||
or
|
||||
i > 0 and result = TPos()
|
||||
)
|
||||
or
|
||||
i = 0 and result = TZero()
|
||||
or
|
||||
i > 0 and result = TPos()
|
||||
)
|
||||
or
|
||||
not exists(this.(SemConstantIntegerExpr).getIntValue()) and
|
||||
exists(float f | f = this.(SemNumericLiteralExpr).getApproximateFloatValue() |
|
||||
f < 0 and result = TNeg()
|
||||
or
|
||||
f = 0 and result = TZero()
|
||||
or
|
||||
f > 0 and result = TPos()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract private class NonConstantSignExpr extends SignExpr {
|
||||
NonConstantSignExpr() { not this instanceof ConstantSignExpr }
|
||||
|
||||
final override Sign getSign() {
|
||||
// The result is the _intersection_ of the signs computed from flow and by the language.
|
||||
(result = this.(FlowSignExpr).getSignRestriction() or not this instanceof FlowSignExpr) and
|
||||
(result = this.(CustomSignExpr).getSignRestriction() or not this instanceof CustomSignExpr)
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression whose sign is computed from the signs of its operands. */
|
||||
abstract private class FlowSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
/** An expression whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
/** An expression whose sign is unknown. */
|
||||
private class UnknownSignExpr extends SignExpr {
|
||||
UnknownSignExpr() {
|
||||
not this instanceof FlowSignExpr and
|
||||
not this instanceof CustomSignExpr and
|
||||
not this instanceof ConstantSignExpr and
|
||||
(
|
||||
// Only track numeric types.
|
||||
getTrackedType(this) instanceof SemNumericType
|
||||
or
|
||||
// Unless the language says to track this expression anyway.
|
||||
Specific::trackUnknownNonNumericExpr(this)
|
||||
)
|
||||
not exists(this.(SemConstantIntegerExpr).getIntValue()) and
|
||||
exists(float f | f = this.(SemNumericLiteralExpr).getApproximateFloatValue() |
|
||||
f < 0 and result = TNeg()
|
||||
or
|
||||
f = 0 and result = TZero()
|
||||
or
|
||||
f > 0 and result = TPos()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
final override Sign getSign() { semAnySign(result) }
|
||||
}
|
||||
abstract private class NonConstantSignExpr extends SignExpr {
|
||||
NonConstantSignExpr() { not this instanceof ConstantSignExpr }
|
||||
|
||||
/**
|
||||
* A `Load` expression whose sign is computed from the sign of its SSA definition, restricted by
|
||||
* inference from any intervening guards.
|
||||
*/
|
||||
class UseSignExpr extends FlowSignExpr {
|
||||
SemSsaVariable v;
|
||||
|
||||
UseSignExpr() { v.getAUse() = this }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
// Propagate via SSA
|
||||
// Propagate the sign from the def of `v`, incorporating any inference from guards.
|
||||
result = semSsaSign(v, any(SemSsaReadPositionBlock bb | bb.getAnExpr() = this))
|
||||
or
|
||||
// No block for this read. Just use the sign of the def.
|
||||
// REVIEW: How can this happen?
|
||||
not exists(SemSsaReadPositionBlock bb | bb.getAnExpr() = this) and
|
||||
result = semSsaDefSign(v)
|
||||
final override Sign getSign() {
|
||||
// The result is the _intersection_ of the signs computed from flow and by the language.
|
||||
(result = this.(FlowSignExpr).getSignRestriction() or not this instanceof FlowSignExpr) and
|
||||
(result = this.(CustomSignExpr).getSignRestriction() or not this instanceof CustomSignExpr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A binary expression whose sign is computed from the signs of its operands. */
|
||||
private class BinarySignExpr extends FlowSignExpr {
|
||||
SemBinaryExpr binary;
|
||||
/** An expression whose sign is computed from the signs of its operands. */
|
||||
abstract private class FlowSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
BinarySignExpr() { binary = this }
|
||||
/** An expression whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
exists(SemExpr left, SemExpr right |
|
||||
binaryExprOperands(binary, left, right) and
|
||||
/** An expression whose sign is unknown. */
|
||||
private class UnknownSignExpr extends SignExpr {
|
||||
UnknownSignExpr() {
|
||||
not this instanceof FlowSignExpr and
|
||||
not this instanceof CustomSignExpr and
|
||||
not this instanceof ConstantSignExpr and
|
||||
(
|
||||
// Only track numeric types.
|
||||
Utils::getTrackedType(this) instanceof SemNumericType
|
||||
or
|
||||
// Unless the language says to track this expression anyway.
|
||||
Specific::trackUnknownNonNumericExpr(this)
|
||||
)
|
||||
}
|
||||
|
||||
final override Sign getSign() { semAnySign(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Load` expression whose sign is computed from the sign of its SSA definition, restricted by
|
||||
* inference from any intervening guards.
|
||||
*/
|
||||
class UseSignExpr extends FlowSignExpr {
|
||||
SemSsaVariable v;
|
||||
|
||||
UseSignExpr() { v.getAUse() = this }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
// Propagate via SSA
|
||||
// Propagate the sign from the def of `v`, incorporating any inference from guards.
|
||||
result = semSsaSign(v, any(SemSsaReadPositionBlock bb | bb.getAnExpr() = this))
|
||||
or
|
||||
// No block for this read. Just use the sign of the def.
|
||||
// REVIEW: How can this happen?
|
||||
not exists(SemSsaReadPositionBlock bb | bb.getAnExpr() = this) and
|
||||
result = semSsaDefSign(v)
|
||||
}
|
||||
}
|
||||
|
||||
/** A binary expression whose sign is computed from the signs of its operands. */
|
||||
private class BinarySignExpr extends FlowSignExpr {
|
||||
SemBinaryExpr binary;
|
||||
|
||||
BinarySignExpr() { binary = this }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
exists(SemExpr left, SemExpr right |
|
||||
binaryExprOperands(binary, left, right) and
|
||||
result =
|
||||
semExprSign(pragma[only_bind_out](left))
|
||||
.applyBinaryOp(semExprSign(pragma[only_bind_out](right)), binary.getOpcode())
|
||||
)
|
||||
or
|
||||
exists(SemDivExpr div | div = binary |
|
||||
result = semExprSign(div.getLeftOperand()) and
|
||||
result != TZero() and
|
||||
div.getRightOperand().(SemFloatingPointLiteralExpr).getFloatValue() = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binaryExprOperands(SemBinaryExpr binary, SemExpr left, SemExpr right) {
|
||||
binary.getLeftOperand() = left and binary.getRightOperand() = right
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression.
|
||||
*/
|
||||
private class SemCastExpr instanceof SemUnaryExpr {
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
SemCastExpr() {
|
||||
this instanceof SemConvertExpr
|
||||
or
|
||||
this instanceof SemBoxExpr
|
||||
or
|
||||
this instanceof SemUnboxExpr
|
||||
}
|
||||
}
|
||||
|
||||
/** A unary expression whose sign is computed from the sign of its operand. */
|
||||
private class UnarySignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr unary;
|
||||
|
||||
UnarySignExpr() { unary = this and not this instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
result =
|
||||
semExprSign(pragma[only_bind_out](left))
|
||||
.applyBinaryOp(semExprSign(pragma[only_bind_out](right)), binary.getOpcode())
|
||||
)
|
||||
or
|
||||
exists(SemDivExpr div | div = binary |
|
||||
result = semExprSign(div.getLeftOperand()) and
|
||||
result != TZero() and
|
||||
div.getRightOperand().(SemFloatingPointLiteralExpr).getFloatValue() = 0
|
||||
semExprSign(pragma[only_bind_out](unary.getOperand())).applyUnaryOp(unary.getOpcode())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression, whose sign is computed based on
|
||||
* the sign of its operand and the source and destination types.
|
||||
*/
|
||||
abstract private class CastSignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr cast;
|
||||
|
||||
CastSignExpr() { cast = this and cast instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() { result = semExprSign(cast.getOperand()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert` expression.
|
||||
*/
|
||||
private class ConvertSignExpr extends CastSignExpr {
|
||||
override SemConvertExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Box` expression.
|
||||
*/
|
||||
private class BoxSignExpr extends CastSignExpr {
|
||||
override SemBoxExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Unbox` expression.
|
||||
*/
|
||||
private class UnboxSignExpr extends CastSignExpr {
|
||||
override SemUnboxExpr cast;
|
||||
|
||||
UnboxSignExpr() {
|
||||
exists(SemType fromType | fromType = Utils::getTrackedType(cast.getOperand()) |
|
||||
// Only numeric source types are handled here.
|
||||
fromType instanceof SemNumericType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate unknownSign(SemExpr e) { e instanceof UnknownSignExpr }
|
||||
|
||||
/**
|
||||
* Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate lowerBound(
|
||||
SemExpr lowerbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(lowerbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getLesserOperand() = lowerbound and
|
||||
comp.getGreaterOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getGreaterOperand() = lowerbound and
|
||||
comp.getLesserOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binaryExprOperands(SemBinaryExpr binary, SemExpr left, SemExpr right) {
|
||||
binary.getLeftOperand() = left and binary.getRightOperand() = right
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression.
|
||||
*/
|
||||
private class SemCastExpr extends SemUnaryExpr {
|
||||
SemCastExpr() {
|
||||
this instanceof SemConvertExpr
|
||||
or
|
||||
this instanceof SemBoxExpr
|
||||
or
|
||||
this instanceof SemUnboxExpr
|
||||
}
|
||||
}
|
||||
|
||||
/** A unary expression whose sign is computed from the sign of its operand. */
|
||||
private class UnarySignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr unary;
|
||||
|
||||
UnarySignExpr() { unary = this and not this instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
result = semExprSign(pragma[only_bind_out](unary.getOperand())).applyUnaryOp(unary.getOpcode())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression, whose sign is computed based on
|
||||
* the sign of its operand and the source and destination types.
|
||||
*/
|
||||
abstract private class CastSignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr cast;
|
||||
|
||||
CastSignExpr() { cast = this and cast instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() { result = semExprSign(cast.getOperand()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert` expression.
|
||||
*/
|
||||
private class ConvertSignExpr extends CastSignExpr {
|
||||
override SemConvertExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Box` expression.
|
||||
*/
|
||||
private class BoxSignExpr extends CastSignExpr {
|
||||
override SemBoxExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Unbox` expression.
|
||||
*/
|
||||
private class UnboxSignExpr extends CastSignExpr {
|
||||
override SemUnboxExpr cast;
|
||||
|
||||
UnboxSignExpr() {
|
||||
exists(SemType fromType | fromType = getTrackedType(cast.getOperand()) |
|
||||
// Only numeric source types are handled here.
|
||||
fromType instanceof SemNumericType
|
||||
/**
|
||||
* Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate upperBound(
|
||||
SemExpr upperbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(upperbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getGreaterOperand() = upperbound and
|
||||
comp.getLesserOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getLesserOperand() = upperbound and
|
||||
comp.getGreaterOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate unknownSign(SemExpr e) { e instanceof UnknownSignExpr }
|
||||
/**
|
||||
* Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
|
||||
* restricted to only include bounds for which we might determine a sign. The
|
||||
* boolean `isEq` gives the polarity:
|
||||
* - `isEq = true` : `v = eqbound`
|
||||
* - `isEq = false` : `v != eqbound`
|
||||
*/
|
||||
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
|
||||
exists(SemGuard guard, boolean testIsTrue, boolean polarity |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue) and
|
||||
guard.isEquality(eqbound, Utils::semSsaRead(v, D::fromInt(0)), polarity) and
|
||||
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
|
||||
not unknownSign(eqbound)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate lowerBound(
|
||||
SemExpr lowerbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(lowerbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getLesserOperand() = lowerbound and
|
||||
comp.getGreaterOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
|
||||
* order for `v` to be positive.
|
||||
*/
|
||||
private predicate posBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
|
||||
* order for `v` to be negative.
|
||||
*/
|
||||
private predicate negBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
|
||||
* can be zero.
|
||||
*/
|
||||
private predicate zeroBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, _)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be positive at `pos`. */
|
||||
private predicate posBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
posBound(bound, v, pos) and TPos() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be negative at `pos`. */
|
||||
private predicate negBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
negBound(bound, v, pos) and TNeg() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be zero at `pos`. */
|
||||
private predicate zeroBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) and TNeg() = semExprSign(bound)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getGreaterOperand() = lowerbound and
|
||||
comp.getLesserOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate upperBound(
|
||||
SemExpr upperbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(upperbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getGreaterOperand() = upperbound and
|
||||
comp.getLesserOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
lowerBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getLesserOperand() = upperbound and
|
||||
comp.getGreaterOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
upperBound(bound, v, pos, _) and TPos() = semExprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, true) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, false) and TZero() != semExprSign(bound)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
|
||||
* restricted to only include bounds for which we might determine a sign. The
|
||||
* boolean `isEq` gives the polarity:
|
||||
* - `isEq = true` : `v = eqbound`
|
||||
* - `isEq = false` : `v != eqbound`
|
||||
*/
|
||||
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
|
||||
exists(SemGuard guard, boolean testIsTrue, boolean polarity |
|
||||
/**
|
||||
* Holds if there is a bound that might restrict whether `v` has the sign `s`
|
||||
* at `pos`.
|
||||
*/
|
||||
private predicate hasGuard(SemSsaVariable v, SemSsaReadPosition pos, Sign s) {
|
||||
s = TPos() and posBound(_, v, pos)
|
||||
or
|
||||
s = TNeg() and negBound(_, v, pos)
|
||||
or
|
||||
s = TZero() and zeroBound(_, v, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where the sign
|
||||
* might be ruled out by a guard.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign guardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue) and
|
||||
guard.isEquality(eqbound, semSsaRead(v, 0), polarity) and
|
||||
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
|
||||
not unknownSign(eqbound)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
|
||||
* order for `v` to be positive.
|
||||
*/
|
||||
private predicate posBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
|
||||
* order for `v` to be negative.
|
||||
*/
|
||||
private predicate negBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
|
||||
* can be zero.
|
||||
*/
|
||||
private predicate zeroBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, _)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be positive at `pos`. */
|
||||
private predicate posBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
posBound(bound, v, pos) and TPos() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be negative at `pos`. */
|
||||
private predicate negBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
negBound(bound, v, pos) and TNeg() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be zero at `pos`. */
|
||||
private predicate zeroBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) and TNeg() = semExprSign(bound)
|
||||
or
|
||||
lowerBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, _) and TPos() = semExprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, true) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, false) and TZero() != semExprSign(bound)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a bound that might restrict whether `v` has the sign `s`
|
||||
* at `pos`.
|
||||
*/
|
||||
private predicate hasGuard(SemSsaVariable v, SemSsaReadPosition pos, Sign s) {
|
||||
s = TPos() and posBound(_, v, pos)
|
||||
or
|
||||
s = TNeg() and negBound(_, v, pos)
|
||||
or
|
||||
s = TZero() and zeroBound(_, v, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where the sign
|
||||
* might be ruled out by a guard.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign guardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where no guard
|
||||
* can rule it out.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign unguardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
not hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at read position `pos`, where a guard could have
|
||||
* ruled out the sign but does not.
|
||||
* This does not check that the definition of `v` also allows the sign.
|
||||
*/
|
||||
private Sign guardedSsaSignOk(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = TPos() and
|
||||
forex(SemExpr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TNeg() and
|
||||
forex(SemExpr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TZero() and
|
||||
forex(SemExpr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v` at `pos`. */
|
||||
private Sign semSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = unguardedSsaSign(v, pos)
|
||||
or
|
||||
result = guardedSsaSign(v, pos) and
|
||||
result = guardedSsaSignOk(v, pos)
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v`. */
|
||||
pragma[nomagic]
|
||||
Sign semSsaDefSign(SemSsaVariable v) { result = v.(SignDef).getSign() }
|
||||
|
||||
/** Gets a possible sign for `e`. */
|
||||
cached
|
||||
Sign semExprSign(SemExpr e) {
|
||||
exists(Sign s | s = e.(SignExpr).getSign() |
|
||||
if
|
||||
getTrackedType(e) instanceof SemUnsignedIntegerType and
|
||||
s = TNeg() and
|
||||
not Specific::ignoreTypeRestrictions(e)
|
||||
then result = TPos()
|
||||
else result = s
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy predicate that holds for any sign. This is added to improve readability
|
||||
* of cases where the sign is unrestricted.
|
||||
*/
|
||||
predicate semAnySign(Sign s) { any() }
|
||||
|
||||
/** Holds if `e` can be positive and cannot be negative. */
|
||||
predicate semPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg()
|
||||
}
|
||||
|
||||
/** Holds if `e` can be negative and cannot be positive. */
|
||||
predicate semNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly positive. */
|
||||
predicate semStrictlyPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TZero()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly negative. */
|
||||
predicate semStrictlyNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TZero()
|
||||
hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where no guard
|
||||
* can rule it out.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign unguardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
not hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at read position `pos`, where a guard could have
|
||||
* ruled out the sign but does not.
|
||||
* This does not check that the definition of `v` also allows the sign.
|
||||
*/
|
||||
private Sign guardedSsaSignOk(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = TPos() and
|
||||
forex(SemExpr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TNeg() and
|
||||
forex(SemExpr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TZero() and
|
||||
forex(SemExpr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v` at `pos`. */
|
||||
private Sign semSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = unguardedSsaSign(v, pos)
|
||||
or
|
||||
result = guardedSsaSign(v, pos) and
|
||||
result = guardedSsaSignOk(v, pos)
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v`. */
|
||||
pragma[nomagic]
|
||||
Sign semSsaDefSign(SemSsaVariable v) { result = v.(SignDef).getSign() }
|
||||
|
||||
/** Gets a possible sign for `e`. */
|
||||
cached
|
||||
Sign semExprSign(SemExpr e) {
|
||||
exists(Sign s | s = e.(SignExpr).getSign() |
|
||||
if
|
||||
Utils::getTrackedType(e) instanceof SemUnsignedIntegerType and
|
||||
s = TNeg() and
|
||||
not Specific::ignoreTypeRestrictions(e)
|
||||
then result = TPos()
|
||||
else result = s
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy predicate that holds for any sign. This is added to improve readability
|
||||
* of cases where the sign is unrestricted.
|
||||
*/
|
||||
predicate semAnySign(Sign s) { any() }
|
||||
|
||||
/** Holds if `e` can be positive and cannot be negative. */
|
||||
predicate semPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg()
|
||||
}
|
||||
|
||||
/** Holds if `e` can be negative and cannot be positive. */
|
||||
predicate semNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly positive. */
|
||||
predicate semStrictlyPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TZero()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly negative. */
|
||||
predicate semStrictlyNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TZero()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,76 +15,24 @@
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
string getATopLevelDomain() {
|
||||
result =
|
||||
[
|
||||
"com", "ru", "net", "org", "de", "jp", "uk", "br", "pl", "in", "it", "fr", "au", "info", "nl",
|
||||
"cn", "ir", "es", "cz", "biz", "ca", "eu", "ua", "kr", "za", "co", "gr", "ro", "se", "tw",
|
||||
"vn", "mx", "ch", "tr", "at", "be", "hu", "tv", "dk", "me", "ar", "us", "no", "sk", "fi",
|
||||
"id", "cl", "nz", "by", "xyz", "pt", "ie", "il", "kz", "my", "hk", "lt", "cc", "sg", "io",
|
||||
"edu", "gov"
|
||||
]
|
||||
}
|
||||
|
||||
predicate hardCodedAddressOrIP(StringLiteral txt) {
|
||||
exists(string s | s = txt.getValueText() |
|
||||
// Hard-coded ip addresses, such as 127.0.0.1
|
||||
s.regexpMatch("\"[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+\"") or
|
||||
// Hard-coded addresses such as www.mycompany.com
|
||||
s.matches("\"www.%\"") or
|
||||
s.matches("\"http:%\"") or
|
||||
s.matches("\"https:%\"") or
|
||||
s.matches("\"%.com\"") or
|
||||
s.matches("\"%.ru\"") or
|
||||
s.matches("\"%.net\"") or
|
||||
s.matches("\"%.org\"") or
|
||||
s.matches("\"%.de\"") or
|
||||
s.matches("\"%.jp\"") or
|
||||
s.matches("\"%.uk\"") or
|
||||
s.matches("\"%.br\"") or
|
||||
s.matches("\"%.pl\"") or
|
||||
s.matches("\"%.in\"") or
|
||||
s.matches("\"%.it\"") or
|
||||
s.matches("\"%.fr\"") or
|
||||
s.matches("\"%.au\"") or
|
||||
s.matches("\"%.info\"") or
|
||||
s.matches("\"%.nl\"") or
|
||||
s.matches("\"%.cn\"") or
|
||||
s.matches("\"%.ir\"") or
|
||||
s.matches("\"%.es\"") or
|
||||
s.matches("\"%.cz\"") or
|
||||
s.matches("\"%.biz\"") or
|
||||
s.matches("\"%.ca\"") or
|
||||
s.matches("\"%.eu\"") or
|
||||
s.matches("\"%.ua\"") or
|
||||
s.matches("\"%.kr\"") or
|
||||
s.matches("\"%.za\"") or
|
||||
s.matches("\"%.co\"") or
|
||||
s.matches("\"%.gr\"") or
|
||||
s.matches("\"%.ro\"") or
|
||||
s.matches("\"%.se\"") or
|
||||
s.matches("\"%.tw\"") or
|
||||
s.matches("\"%.vn\"") or
|
||||
s.matches("\"%.mx\"") or
|
||||
s.matches("\"%.ch\"") or
|
||||
s.matches("\"%.tr\"") or
|
||||
s.matches("\"%.at\"") or
|
||||
s.matches("\"%.be\"") or
|
||||
s.matches("\"%.hu\"") or
|
||||
s.matches("\"%.tv\"") or
|
||||
s.matches("\"%.dk\"") or
|
||||
s.matches("\"%.me\"") or
|
||||
s.matches("\"%.ar\"") or
|
||||
s.matches("\"%.us\"") or
|
||||
s.matches("\"%.no\"") or
|
||||
s.matches("\"%.sk\"") or
|
||||
s.matches("\"%.fi\"") or
|
||||
s.matches("\"%.id\"") or
|
||||
s.matches("\"%.cl\"") or
|
||||
s.matches("\"%.nz\"") or
|
||||
s.matches("\"%.by\"") or
|
||||
s.matches("\"%.xyz\"") or
|
||||
s.matches("\"%.pt\"") or
|
||||
s.matches("\"%.ie\"") or
|
||||
s.matches("\"%.il\"") or
|
||||
s.matches("\"%.kz\"") or
|
||||
s.matches("\"%.my\"") or
|
||||
s.matches("\"%.hk\"") or
|
||||
s.matches("\"%.lt\"") or
|
||||
s.matches("\"%.cc\"") or
|
||||
s.matches("\"%.sg\"") or
|
||||
s.matches("\"%.io\"") or
|
||||
s.matches("\"%.edu\"") or
|
||||
s.matches("\"%.gov\"")
|
||||
s.regexpMatch("\"(www\\.|http:|https:).*\"") or
|
||||
s.regexpMatch("\".*\\.(" + strictconcat(getATopLevelDomain(), "|") + ")\"")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -207,34 +207,35 @@ bad_asts.cpp:
|
||||
# 27| Type = [SpecifiedType] const Point
|
||||
# 27| ValueCategory = lvalue
|
||||
# 28| getStmt(1): [ReturnStmt] return ...
|
||||
# 30| [TopLevelFunction] void Bad::errorExpr()
|
||||
# 30| <params>:
|
||||
# 30| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 31| getStmt(0): [DeclStmt] declaration
|
||||
# 31| getDeclarationEntry(0): [VariableDeclarationEntry] definition of intref
|
||||
# 31| Type = [LValueReferenceType] int &
|
||||
# 31| getVariable().getInitializer(): [Initializer] initializer for intref
|
||||
# 31| getExpr(): [ErrorExpr] <error expr>
|
||||
# 31| Type = [ErroneousType] error
|
||||
# 31| ValueCategory = prvalue
|
||||
# 32| getStmt(1): [DeclStmt] declaration
|
||||
# 32| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
|
||||
# 32| Type = [IntType] int
|
||||
# 32| getVariable().getInitializer(): [Initializer] initializer for x
|
||||
# 32| getExpr(): [ErrorExpr] <error expr>
|
||||
# 32| Type = [ErroneousType] error
|
||||
# 32| ValueCategory = prvalue
|
||||
# 33| getStmt(2): [ExprStmt] ExprStmt
|
||||
# 33| getExpr(): [AssignExpr] ... = ...
|
||||
# 33| Type = [IntType] int
|
||||
# 33| ValueCategory = lvalue
|
||||
# 33| getLValue(): [VariableAccess] x
|
||||
# 33| Type = [IntType] int
|
||||
# 33| ValueCategory = lvalue
|
||||
# 33| getRValue(): [ErrorExpr] <error expr>
|
||||
# 33| Type = [ErroneousType] error
|
||||
# 33| ValueCategory = prvalue(load)
|
||||
# 34| getStmt(3): [ReturnStmt] return ...
|
||||
bad_stmts.cpp:
|
||||
# 5| [TopLevelFunction] void Bad::errorExpr()
|
||||
# 5| <params>:
|
||||
# 5| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 6| getStmt(0): [DeclStmt] declaration
|
||||
# 6| getDeclarationEntry(0): [VariableDeclarationEntry] definition of intref
|
||||
# 6| Type = [LValueReferenceType] int &
|
||||
# 6| getVariable().getInitializer(): [Initializer] initializer for intref
|
||||
# 6| getExpr(): [ErrorExpr] <error expr>
|
||||
# 6| Type = [ErroneousType] error
|
||||
# 6| ValueCategory = prvalue
|
||||
# 7| getStmt(1): [DeclStmt] declaration
|
||||
# 7| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
|
||||
# 7| Type = [IntType] int
|
||||
# 7| getVariable().getInitializer(): [Initializer] initializer for x
|
||||
# 7| getExpr(): [ErrorExpr] <error expr>
|
||||
# 7| Type = [ErroneousType] error
|
||||
# 7| ValueCategory = prvalue
|
||||
# 8| getStmt(2): [ExprStmt] ExprStmt
|
||||
# 8| getExpr(): [AssignExpr] ... = ...
|
||||
# 8| Type = [IntType] int
|
||||
# 8| ValueCategory = lvalue
|
||||
# 8| getLValue(): [VariableAccess] x
|
||||
# 8| Type = [IntType] int
|
||||
# 8| ValueCategory = lvalue
|
||||
# 8| getRValue(): [ErrorExpr] <error expr>
|
||||
# 8| Type = [ErroneousType] error
|
||||
# 8| ValueCategory = prvalue(load)
|
||||
# 9| getStmt(3): [ReturnStmt] return ...
|
||||
clang.cpp:
|
||||
# 5| [TopLevelFunction] int* globalIntAddress()
|
||||
# 5| <params>:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// semmle-extractor-options: -std=c++17 --expect_errors
|
||||
// semmle-extractor-options: -std=c++17
|
||||
|
||||
// Test cases that illustrate known bad ASTs that we have to work around in IR generation.
|
||||
namespace Bad {
|
||||
@@ -26,10 +26,4 @@ namespace Bad {
|
||||
void CallCopyConstructor(const Point& a) {
|
||||
Point b = a; // Copy constructor contains literal expressions with no values.
|
||||
}
|
||||
|
||||
void errorExpr() {
|
||||
int &intref = 0;
|
||||
int x = 0[0];
|
||||
x = 1[1];
|
||||
}
|
||||
}
|
||||
|
||||
10
cpp/ql/test/library-tests/ir/ir/bad_stmts.cpp
Normal file
10
cpp/ql/test/library-tests/ir/ir/bad_stmts.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// semmle-extractor-options: -std=c++17 --expect_errors
|
||||
|
||||
// Test cases that illustrate known bad ASTs that we have to work around in IR generation.
|
||||
namespace Bad {
|
||||
void errorExpr() {
|
||||
int &intref = 0;
|
||||
int x = 0[0];
|
||||
x = 1[1];
|
||||
}
|
||||
}
|
||||
@@ -98,17 +98,17 @@
|
||||
| bad_asts.cpp:27:15:27:15 | StoreValue | r27_6 |
|
||||
| bad_asts.cpp:27:15:27:15 | Unary | r27_3 |
|
||||
| bad_asts.cpp:27:15:27:15 | Unary | r27_4 |
|
||||
| bad_asts.cpp:30:8:30:16 | ChiPartial | partial:m30_3 |
|
||||
| bad_asts.cpp:30:8:30:16 | ChiTotal | total:m30_2 |
|
||||
| bad_asts.cpp:30:8:30:16 | SideEffect | m30_3 |
|
||||
| bad_asts.cpp:31:10:31:15 | Address | &:r31_1 |
|
||||
| bad_asts.cpp:31:18:31:19 | StoreValue | r31_2 |
|
||||
| bad_asts.cpp:32:9:32:9 | Address | &:r32_1 |
|
||||
| bad_asts.cpp:32:12:32:16 | StoreValue | r32_2 |
|
||||
| bad_asts.cpp:33:5:33:5 | Address | &:r33_3 |
|
||||
| bad_asts.cpp:33:5:33:12 | Address | &:r33_1 |
|
||||
| bad_asts.cpp:33:5:33:12 | Load | ~m30_4 |
|
||||
| bad_asts.cpp:33:5:33:12 | StoreValue | r33_2 |
|
||||
| bad_stmts.cpp:5:8:5:16 | ChiPartial | partial:m5_3 |
|
||||
| bad_stmts.cpp:5:8:5:16 | ChiTotal | total:m5_2 |
|
||||
| bad_stmts.cpp:5:8:5:16 | SideEffect | m5_3 |
|
||||
| bad_stmts.cpp:6:10:6:15 | Address | &:r6_1 |
|
||||
| bad_stmts.cpp:6:18:6:19 | StoreValue | r6_2 |
|
||||
| bad_stmts.cpp:7:9:7:9 | Address | &:r7_1 |
|
||||
| bad_stmts.cpp:7:12:7:16 | StoreValue | r7_2 |
|
||||
| bad_stmts.cpp:8:5:8:5 | Address | &:r8_3 |
|
||||
| bad_stmts.cpp:8:5:8:12 | Address | &:r8_1 |
|
||||
| bad_stmts.cpp:8:5:8:12 | Load | ~m5_4 |
|
||||
| bad_stmts.cpp:8:5:8:12 | StoreValue | r8_2 |
|
||||
| clang.cpp:5:6:5:21 | Address | &:r5_5 |
|
||||
| clang.cpp:5:6:5:21 | ChiPartial | partial:m5_3 |
|
||||
| clang.cpp:5:6:5:21 | ChiTotal | total:m5_2 |
|
||||
|
||||
@@ -120,25 +120,26 @@ bad_asts.cpp:
|
||||
# 26| v26_10(void) = AliasedUse : ~m?
|
||||
# 26| v26_11(void) = ExitFunction :
|
||||
|
||||
# 30| void Bad::errorExpr()
|
||||
# 30| Block 0
|
||||
# 30| v30_1(void) = EnterFunction :
|
||||
# 30| mu30_2(unknown) = AliasedDefinition :
|
||||
# 30| mu30_3(unknown) = InitializeNonLocal :
|
||||
# 31| r31_1(glval<int &>) = VariableAddress[intref] :
|
||||
# 31| r31_2(error) = Error :
|
||||
# 31| mu31_3(int &) = Store[intref] : &:r31_1, r31_2
|
||||
# 32| r32_1(glval<int>) = VariableAddress[x] :
|
||||
# 32| r32_2(error) = Error :
|
||||
# 32| mu32_3(int) = Store[x] : &:r32_1, r32_2
|
||||
# 33| r33_1(glval<error>) = Error :
|
||||
# 33| r33_2(error) = Load[?] : &:r33_1, ~m?
|
||||
# 33| r33_3(glval<int>) = VariableAddress[x] :
|
||||
# 33| mu33_4(int) = Store[x] : &:r33_3, r33_2
|
||||
# 34| v34_1(void) = NoOp :
|
||||
# 30| v30_4(void) = ReturnVoid :
|
||||
# 30| v30_5(void) = AliasedUse : ~m?
|
||||
# 30| v30_6(void) = ExitFunction :
|
||||
bad_stmts.cpp:
|
||||
# 5| void Bad::errorExpr()
|
||||
# 5| Block 0
|
||||
# 5| v5_1(void) = EnterFunction :
|
||||
# 5| mu5_2(unknown) = AliasedDefinition :
|
||||
# 5| mu5_3(unknown) = InitializeNonLocal :
|
||||
# 6| r6_1(glval<int &>) = VariableAddress[intref] :
|
||||
# 6| r6_2(error) = Error :
|
||||
# 6| mu6_3(int &) = Store[intref] : &:r6_1, r6_2
|
||||
# 7| r7_1(glval<int>) = VariableAddress[x] :
|
||||
# 7| r7_2(error) = Error :
|
||||
# 7| mu7_3(int) = Store[x] : &:r7_1, r7_2
|
||||
# 8| r8_1(glval<error>) = Error :
|
||||
# 8| r8_2(error) = Load[?] : &:r8_1, ~m?
|
||||
# 8| r8_3(glval<int>) = VariableAddress[x] :
|
||||
# 8| mu8_4(int) = Store[x] : &:r8_3, r8_2
|
||||
# 9| v9_1(void) = NoOp :
|
||||
# 5| v5_4(void) = ReturnVoid :
|
||||
# 5| v5_5(void) = AliasedUse : ~m?
|
||||
# 5| v5_6(void) = ExitFunction :
|
||||
|
||||
clang.cpp:
|
||||
# 5| int* globalIntAddress()
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import cpp
|
||||
import experimental.semmle.code.cpp.semantic.analysis.ModulusAnalysis
|
||||
import experimental.semmle.code.cpp.semantic.Semantic
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeUtils
|
||||
import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysisSpecific
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysis
|
||||
import semmle.code.cpp.ir.IR as IR
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
module ModulusAnalysisInstantiated =
|
||||
ModulusAnalysis<FloatDelta, Bounds, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
class ModulusAnalysisTest extends InlineExpectationsTest {
|
||||
ModulusAnalysisTest() { this = "ModulusAnalysisTest" }
|
||||
|
||||
@@ -23,7 +30,7 @@ class ModulusAnalysisTest extends InlineExpectationsTest {
|
||||
|
||||
private string getAModString(SemExpr e) {
|
||||
exists(SemBound b, int delta, int mod |
|
||||
semExprModulus(e, b, delta, mod) and
|
||||
ModulusAnalysisInstantiated::semExprModulus(e, b, delta, mod) and
|
||||
result = b.toString() + "," + delta.toString() + "," + mod.toString() and
|
||||
not (delta = 0 and mod = 0)
|
||||
)
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import cpp
|
||||
import experimental.semmle.code.cpp.semantic.analysis.SignAnalysisCommon
|
||||
import experimental.semmle.code.cpp.semantic.Semantic
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeUtils
|
||||
import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysisSpecific
|
||||
import semmle.code.cpp.ir.IR as IR
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
module SignAnalysisInstantiated = SignAnalysis<FloatDelta, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
class SignAnalysisTest extends InlineExpectationsTest {
|
||||
SignAnalysisTest() { this = "SignAnalysisTest" }
|
||||
|
||||
@@ -21,4 +26,6 @@ class SignAnalysisTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
private string getASignString(SemExpr e) { result = strictconcat(semExprSign(e).toString(), "") }
|
||||
private string getASignString(SemExpr e) {
|
||||
result = strictconcat(SignAnalysisInstantiated::semExprSign(e).toString(), "")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
struct Foo
|
||||
struct Allocators
|
||||
{
|
||||
Foo(int x, int y) : m_x(x), m_y(y) {}
|
||||
~Foo() {m_x = m_y = 0;}
|
||||
Allocators(int x, int y) : m_x(x), m_y(y) {}
|
||||
~Allocators() {m_x = m_y = 0;}
|
||||
|
||||
// NB: In Microsoft mode, size_t is predeclared.
|
||||
static void* operator new(size_t sz, int z, int w) { return nullptr; }
|
||||
@@ -13,7 +13,7 @@ struct Foo
|
||||
|
||||
int main()
|
||||
{
|
||||
auto foo = new(11, 22) Foo(33, 44);
|
||||
auto foo = new(11, 22) Allocators(33, 44);
|
||||
delete foo;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
struct Foo {
|
||||
~Foo();
|
||||
struct ArrayDelete {
|
||||
~ArrayDelete();
|
||||
};
|
||||
|
||||
void f() {
|
||||
delete[] (Foo*)nullptr;
|
||||
delete[] (ArrayDelete*)nullptr;
|
||||
}
|
||||
|
||||
@@ -74,8 +74,8 @@ argHasPostUpdate
|
||||
| ir.cpp:625:5:625:5 | s | ArgumentNode is missing PostUpdateNode. |
|
||||
postWithInFlow
|
||||
| VacuousDestructorCall.cpp:10:22:10:22 | i [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:11:4:13 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:17:4:19 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:18:4:20 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:24:4:26 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| assignexpr.cpp:9:4:9:4 | i [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| builtin.c:34:23:34:31 | staticint [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| builtin.c:39:37:39:45 | carry_out [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -1483,17 +1483,17 @@ postWithInFlow
|
||||
| aggregateinitializer.c:3:6:3:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aggregateinitializer.c:3:11:3:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aggregateinitializer.c:3:11:3:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:23:3:28 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:31:3:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:11:4:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:11:4:13 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:17:4:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:17:4:19 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:30:3:35 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:38:3:43 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:18:4:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:18:4:20 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:24:4:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:24:4:26 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:7:56:7:70 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:8:16:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:36 | Call [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:36 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:36 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:43 | Call [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:43 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:43 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:18:1:18:1 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| assignexpr.cpp:9:4:9:4 | i [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| bad_asts.cpp:10:7:10:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -17,6 +17,24 @@ edges
|
||||
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
|
||||
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
|
||||
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address indirection |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:52:14:52:20 | address indirection |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:56:14:56:20 | address indirection |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:60:14:60:20 | address indirection |
|
||||
subpaths
|
||||
nodes
|
||||
| test.cpp:16:25:16:30 | call to getenv | semmle.label | call to getenv |
|
||||
@@ -34,7 +52,21 @@ nodes
|
||||
| test.cpp:42:14:42:20 | address | semmle.label | address |
|
||||
| test.cpp:42:14:42:20 | address | semmle.label | address |
|
||||
| test.cpp:42:14:42:20 | address indirection | semmle.label | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:52:14:52:20 | address | semmle.label | address |
|
||||
| test.cpp:52:14:52:20 | address | semmle.label | address |
|
||||
| test.cpp:52:14:52:20 | address indirection | semmle.label | address indirection |
|
||||
| test.cpp:56:14:56:20 | address | semmle.label | address |
|
||||
| test.cpp:56:14:56:20 | address | semmle.label | address |
|
||||
| test.cpp:56:14:56:20 | address indirection | semmle.label | address indirection |
|
||||
| test.cpp:60:14:60:20 | address | semmle.label | address |
|
||||
| test.cpp:60:14:60:20 | address | semmle.label | address |
|
||||
| test.cpp:60:14:60:20 | address indirection | semmle.label | address indirection |
|
||||
#select
|
||||
| test.cpp:20:7:20:12 | call to strcmp | test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:16:25:16:30 | call to getenv | call to getenv |
|
||||
| test.cpp:31:7:31:12 | call to strcmp | test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:27:25:27:30 | call to getenv | call to getenv |
|
||||
| test.cpp:42:7:42:12 | call to strcmp | test.cpp:38:25:38:30 | call to getenv | test.cpp:42:14:42:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:38:25:38:30 | call to getenv | call to getenv |
|
||||
| test.cpp:52:7:52:12 | call to strcmp | test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:49:25:49:30 | call to getenv | call to getenv |
|
||||
| test.cpp:56:7:56:12 | call to strcmp | test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:49:25:49:30 | call to getenv | call to getenv |
|
||||
| test.cpp:60:7:60:12 | call to strcmp | test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:49:25:49:30 | call to getenv | call to getenv |
|
||||
|
||||
@@ -43,3 +43,27 @@ void processRequest3()
|
||||
isServer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void processRequest4()
|
||||
{
|
||||
const char *address = getenv("SERVERIP");
|
||||
bool cond = false;
|
||||
|
||||
if (strcmp(address, "127.0.0.1")) { cond = true; } // BAD
|
||||
if (strcmp(address, "127_0_0_1")) { cond = true; } // GOOD (not an IP)
|
||||
if (strcmp(address, "127.0.0")) { cond = true; } // GOOD (not an IP)
|
||||
if (strcmp(address, "127.0.0.0.1")) { cond = true; } // GOOD (not an IP)
|
||||
if (strcmp(address, "http://mycompany")) { cond = true; } // BAD
|
||||
if (strcmp(address, "http_//mycompany")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "htt://mycompany")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "httpp://mycompany")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "mycompany.com")) { cond = true; } // BAD
|
||||
if (strcmp(address, "mycompany_com")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "mycompany.c")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "mycompany.comm")) { cond = true; } // GOOD (not an address)
|
||||
|
||||
if (cond) {
|
||||
isServer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Remove operators from the virtualizable type.
|
||||
compatibility: full
|
||||
@@ -92,7 +92,8 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
yield return Tuples.cil_parameter_out(pe);
|
||||
if (p.Attributes.HasFlag(ParameterAttributes.In))
|
||||
yield return Tuples.cil_parameter_in(pe);
|
||||
Attribute.Populate(Context, pe, p.GetCustomAttributes());
|
||||
foreach (var c in Attribute.Populate(Context, pe, p.GetCustomAttributes()))
|
||||
yield return c;
|
||||
}
|
||||
|
||||
yield return Tuples.metadata_handle(this, Context.Assembly, MetadataTokens.GetToken(handle));
|
||||
@@ -205,7 +206,8 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
yield return Tuples.cil_newslot(this);
|
||||
|
||||
// Populate attributes
|
||||
Attribute.Populate(Context, this, md.GetCustomAttributes());
|
||||
foreach (var c in Attribute.Populate(Context, this, md.GetCustomAttributes()))
|
||||
yield return c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* Added library support for generic attributes (also for CIL extracted attributes).
|
||||
* `cil.ConstructedType::getName` was changed to include printing of the type arguments.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Support for `static virtual` and `static abstract` interface members.
|
||||
* Support for *operators* in interface definitions.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Attributes on methods in CIL are now extracted (Bugfix).
|
||||
@@ -27,3 +27,19 @@ class Attribute extends Element, @cil_attribute {
|
||||
|
||||
override CS::Location getLocation() { result = getDeclaration().getLocation() }
|
||||
}
|
||||
|
||||
/** A generic attribute to a declaration. */
|
||||
class GenericAttribute extends Attribute {
|
||||
private ConstructedType type;
|
||||
|
||||
GenericAttribute() { type = this.getType() }
|
||||
|
||||
/** Gets the total number of type arguments. */
|
||||
int getNumberOfTypeArguments() { result = count(int i | cil_type_argument(type, i, _)) }
|
||||
|
||||
/** Gets the `i`th type argument, if any. */
|
||||
Type getTypeArgument(int i) { result = type.getTypeArgument(i) }
|
||||
|
||||
/** Get a type argument. */
|
||||
Type getATypeArgument() { result = this.getTypeArgument(_) }
|
||||
}
|
||||
|
||||
@@ -28,6 +28,12 @@ class ConstructedGeneric extends Generic, DotNet::ConstructedGeneric {
|
||||
final override Type getTypeArgument(int n) { cil_type_argument(this, n, result) }
|
||||
}
|
||||
|
||||
/** Gets the concatenation of the `getName()` of type arguments. */
|
||||
language[monotonicAggregates]
|
||||
private string getTypeArgumentsNames(ConstructedGeneric cg) {
|
||||
result = strictconcat(Type t, int i | t = cg.getTypeArgument(i) | t.getName(), "," order by i)
|
||||
}
|
||||
|
||||
/** An unbound generic type. */
|
||||
class UnboundGenericType extends UnboundGeneric, Type { }
|
||||
|
||||
@@ -41,6 +47,10 @@ class ConstructedType extends ConstructedGeneric, Type {
|
||||
override predicate isInterface() { this.getUnboundType().isInterface() }
|
||||
|
||||
override predicate isClass() { this.getUnboundType().isClass() }
|
||||
|
||||
final override string getName() {
|
||||
result = this.getUndecoratedName() + "<" + getTypeArgumentsNames(this) + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** A constructed generic method. */
|
||||
|
||||
@@ -37,8 +37,14 @@ class Attributable extends @attributable {
|
||||
}
|
||||
|
||||
private string getAttributeName(Attribute a) {
|
||||
exists(string type | type = a.getType().getName() |
|
||||
if type.matches("%Attribute") then result = type.prefix(type.length() - 9) else result = type
|
||||
exists(string type, string pattern |
|
||||
type = a.getType().getName() and pattern = "(.*)Attribute(<.*>)?"
|
||||
|
|
||||
type.regexpMatch(pattern) and
|
||||
result = concat(int i | i = [1, 2] | type.regexpCapture(pattern, i) order by i)
|
||||
or
|
||||
not type.regexpMatch(pattern) and
|
||||
result = type
|
||||
)
|
||||
}
|
||||
|
||||
@@ -99,6 +105,31 @@ class Attribute extends TopLevelExprParent, @attribute {
|
||||
override string getAPrimaryQlClass() { result = "Attribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic attribute, for example `[...]` on line 1 in
|
||||
*
|
||||
* ```csharp
|
||||
* [MyGenericAttribute<int>(0)]
|
||||
* public void SomeMethod(string s) { }
|
||||
* ```
|
||||
*/
|
||||
class GenericAttribute extends Attribute {
|
||||
private ConstructedClass type;
|
||||
|
||||
GenericAttribute() { type = this.getType() }
|
||||
|
||||
/** Gets the total number of type arguments. */
|
||||
int getNumberOfTypeArguments() { result = count(int i | type_arguments(_, i, type)) }
|
||||
|
||||
/** Gets the `i`th type argument, if any. */
|
||||
Type getTypeArgument(int i) { result = type.getTypeArgument(i) }
|
||||
|
||||
/** Get a type argument. */
|
||||
Type getATypeArgument() { result = this.getTypeArgument(_) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GenericAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute with default kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
@@ -110,6 +141,17 @@ class DefaultAttribute extends Attribute, @attribute_default {
|
||||
override string getAPrimaryQlClass() { result = "DefaultAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic attribute with default kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
* [MyAttribute<string>(0)]
|
||||
* int SomeMethod() { return 1; }
|
||||
* ```
|
||||
*/
|
||||
class GenericDefaultAttribute extends GenericAttribute, DefaultAttribute {
|
||||
override string getAPrimaryQlClass() { result = "GenericDefaultAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute with return kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
@@ -123,6 +165,17 @@ class ReturnAttribute extends Attribute, @attribute_return {
|
||||
override string getAPrimaryQlClass() { result = "ReturnAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic attribute with return kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
* [return: MyAttribute<object>(0)]
|
||||
* int SomeMethod() { return 1; }
|
||||
* ```
|
||||
*/
|
||||
class GenericReturnAttribute extends GenericAttribute, ReturnAttribute {
|
||||
override string getAPrimaryQlClass() { result = "GenericReturnAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute with assembly kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
@@ -135,6 +188,16 @@ class AssemblyAttribute extends Attribute, @attribute_assembly {
|
||||
override string getAPrimaryQlClass() { result = "AssemblyAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic attribute with assembly kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
* [assembly: MyAttribute<string>(0)]
|
||||
* ```
|
||||
*/
|
||||
class GenericAssemblyAttribute extends GenericAttribute, AssemblyAttribute {
|
||||
override string getAPrimaryQlClass() { result = "GenericAssemblyAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute with module kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
@@ -146,3 +209,13 @@ class ModuleAttribute extends Attribute, @attribute_module {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ModuleAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic attribute with module kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
* [module: MyAttribute<string>(0)]
|
||||
* ```
|
||||
*/
|
||||
class GenericModuleAttribute extends GenericAttribute, ModuleAttribute {
|
||||
override string getAPrimaryQlClass() { result = "GenericModuleAttribute" }
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ pragma[nomagic]
|
||||
private Virtualizable getACompatibleInterfaceMemberAux(Virtualizable m) {
|
||||
result = getACompatibleInterfaceAccessor(m) or
|
||||
result = getACompatibleInterfaceIndexer(m) or
|
||||
result = getACompatibleInterfaceMethod(m)
|
||||
result = getACompatibleRelevantInterfaceMember(m)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,11 +210,13 @@ private predicate getACompatibleInterfaceIndexerAux(Indexer i, ValueOrRefType t)
|
||||
t = getAPossibleImplementor(i.getDeclaringType())
|
||||
}
|
||||
|
||||
private Method getACompatibleInterfaceMethod0(Method m, int i) {
|
||||
result = getAnInterfaceMethodCandidate(m) and
|
||||
private RelevantInterfaceMember getACompatibleRelevantInterfaceMember0(
|
||||
RelevantInterfaceMember m, int i
|
||||
) {
|
||||
result = getARelevantInterfaceMemberCandidate(m) and
|
||||
i = -1
|
||||
or
|
||||
result = getACompatibleInterfaceMethod0(m, i - 1) and
|
||||
result = getACompatibleRelevantInterfaceMember0(m, i - 1) and
|
||||
exists(Type t1, Type t2 |
|
||||
t1 = getArgumentOrReturnType(m, i) and
|
||||
t2 = getArgumentOrReturnType(result, i)
|
||||
@@ -223,32 +225,47 @@ private Method getACompatibleInterfaceMethod0(Method m, int i) {
|
||||
)
|
||||
}
|
||||
|
||||
private Method getACompatibleInterfaceMethod(Method m) {
|
||||
result = getACompatibleInterfaceMethod0(m, m.getNumberOfParameters())
|
||||
/**
|
||||
* A class of callables relevant for interface member compatibility.
|
||||
*/
|
||||
private class RelevantInterfaceMember extends Callable {
|
||||
RelevantInterfaceMember() {
|
||||
this instanceof Method or
|
||||
this instanceof Operator
|
||||
}
|
||||
|
||||
predicate isPublic() {
|
||||
this.(Method).isPublic() or
|
||||
this.(Operator).isPublic()
|
||||
}
|
||||
}
|
||||
|
||||
private RelevantInterfaceMember getACompatibleRelevantInterfaceMember(RelevantInterfaceMember m) {
|
||||
result = getACompatibleRelevantInterfaceMember0(m, m.getNumberOfParameters())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an interface method that may potentially be implemented by `m`.
|
||||
* Gets an interface method or operator that may potentially be implemented by `m`.
|
||||
*
|
||||
* That is, a method with the same name, same number of parameters, and declared
|
||||
* in a type that is a possible implementor type for the interface type.
|
||||
*/
|
||||
private Method getAnInterfaceMethodCandidate(Method m) {
|
||||
getAPotentialInterfaceMethodAux(result, m.getDeclaringType(), m.getUndecoratedName(),
|
||||
private RelevantInterfaceMember getARelevantInterfaceMemberCandidate(RelevantInterfaceMember m) {
|
||||
getAPotentialRelevantInterfaceMemberAux(result, m.getDeclaringType(), m.getUndecoratedName(),
|
||||
m.getNumberOfParameters()) and
|
||||
m.isPublic()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate getAPotentialInterfaceMethodAux(
|
||||
Method m, ValueOrRefType t, string name, int params
|
||||
private predicate getAPotentialRelevantInterfaceMemberAux(
|
||||
RelevantInterfaceMember m, ValueOrRefType t, string name, int params
|
||||
) {
|
||||
t = getAPossibleImplementor(m.getDeclaringType()) and
|
||||
name = m.getUndecoratedName() and
|
||||
params = m.getNumberOfParameters()
|
||||
}
|
||||
|
||||
private Type getArgumentOrReturnType(Method m, int i) {
|
||||
private Type getArgumentOrReturnType(RelevantInterfaceMember m, int i) {
|
||||
i = 0 and result = m.getReturnType()
|
||||
or
|
||||
result = m.getParameter(i - 1).getType()
|
||||
|
||||
@@ -184,7 +184,7 @@ private class TOverridable = @virtualizable or @callable_accessor;
|
||||
|
||||
/**
|
||||
* A declaration that can be overridden or implemented. That is, a method,
|
||||
* a property, an indexer, an event, or an accessor.
|
||||
* a property, an indexer, an event, an accessor, or an operator.
|
||||
*
|
||||
* Unlike `Virtualizable`, this class includes accessors.
|
||||
*/
|
||||
@@ -360,7 +360,7 @@ class Overridable extends Declaration, TOverridable {
|
||||
|
||||
/**
|
||||
* A member where the `virtual` modifier is valid. That is, a method,
|
||||
* a property, an indexer, or an event.
|
||||
* a property, an indexer, an event, or an operator.
|
||||
*
|
||||
* Equivalently, these are the members that can be defined in an interface.
|
||||
*
|
||||
|
||||
@@ -670,7 +670,7 @@ compiler_generated(unique int id: @modifiable ref);
|
||||
|
||||
@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr;
|
||||
|
||||
@virtualizable = @method | @property | @indexer | @event;
|
||||
@virtualizable = @method | @property | @indexer | @event | @operator;
|
||||
|
||||
exprorstmt_name(
|
||||
unique int parent_id: @named_exprorstmt ref,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Add operators to the virtualizable type.
|
||||
compatibility: full
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,8 @@ private predicate isOsSpecific(Declaration d) {
|
||||
.matches("%" +
|
||||
[
|
||||
"libobjc", "libproc", "System.Diagnostics.Tracing.XplatEventLogger",
|
||||
"System.Threading.AutoreleasePool"
|
||||
"System.Threading.AutoreleasePool",
|
||||
"System.Diagnostics.Tracing.EventSource.<WriteEventString>"
|
||||
] + "%")
|
||||
}
|
||||
|
||||
|
||||
@@ -472,6 +472,12 @@
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper.LessThan | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper.Swap | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper.SwapIfGreater | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0,!1>.GreaterThan | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0,!1>.LessThan | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0>.GreaterThan | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0>.LessThan | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0>.Swap | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0>.SwapIfGreater | parameter | 32 |
|
||||
| Parameter 0 of System.ConsolePal.<UpdatedCachedCursorPosition>g__IncrementX\|113_1 | parameter | 32 |
|
||||
| Parameter 0 of System.ConsolePal.<UpdatedCachedCursorPosition>g__IncrementY\|113_0 | parameter | 32 |
|
||||
| Parameter 0 of System.ConsolePal.GetWindowSize | parameter | 32 |
|
||||
@@ -661,6 +667,10 @@
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.CastHelpers.Element | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.CastHelpers.HashShift | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.CastHelpers.KeyToBucket | parameter | 32 |
|
||||
@@ -672,6 +682,9 @@
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.InteropServices.ComWrappers.GetIUnknownImpl | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.InteropServices.ComWrappers.GetIUnknownImplInternal | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.InteropServices.MemoryMarshal.CreateReadOnlySpan | parameter | 32 |
|
||||
@@ -737,6 +750,7 @@
|
||||
| Parameter 0 of System.Threading.Thread.VolatileRead | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.Thread.VolatileWrite | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.ThreadLocal.GrowTable | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.ThreadLocal<!0>.GrowTable | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.ThreadPool.GetAvailableThreads | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.ThreadPool.GetAvailableThreadsNative | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.ThreadPool.GetMaxThreads | parameter | 32 |
|
||||
@@ -789,6 +803,7 @@
|
||||
| Parameter 1 of System.Buffers.Binary.BinaryPrimitives.TryReadUInt64BigEndian | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.Binary.BinaryPrimitives.TryReadUInt64LittleEndian | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.MemoryManager.TryGetArray | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.MemoryManager<!0>.TryGetArray | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.StandardFormat.ParseHelper | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.StandardFormat.TryParse | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.Text.FormattingHelpers.CountDecimalTrailingZeros | parameter | 32 |
|
||||
@@ -835,20 +850,36 @@
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue.TryPeek | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue.TryTake | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue<!0>.SnapForObservation | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue<!0>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue<!0>.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue<!0>.TryPeek | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue<Object>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueueSegment.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueueSegment.TryPeek | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueueSegment<!0>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueueSegment<!0>.TryPeek | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.IProducerConsumerCollection.TryTake | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.IProducerConsumerCollection<!0>.TryTake | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.DictionaryEntry.Deconstruct | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.EnumerableHelpers.ToArray | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper.GreaterThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper.LessThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper.Swap | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper.SwapIfGreater | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0,!1>.GreaterThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0,!1>.LessThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0>.GreaterThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0>.LessThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0>.Swap | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0>.SwapIfGreater | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.KeyValuePair.Deconstruct | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.KeyValuePair<String,ResourceSet>.Deconstruct | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Queue.MoveNext | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Queue.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Queue.TryPeek | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Stack.TryPop | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Queue<!0>.MoveNext | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Stack<ConsoleKeyInfo>.TryPop | parameter | 32 |
|
||||
| Parameter 1 of System.ConsolePal.GetWindowSize | parameter | 32 |
|
||||
| Parameter 1 of System.ConsolePal.TryGetCachedCursorPosition | parameter | 32 |
|
||||
| Parameter 1 of System.ConsolePal.TryGetCursorPosition | parameter | 32 |
|
||||
@@ -974,6 +1005,21 @@
|
||||
| Parameter 1 of System.Half.TryParse | parameter | 32 |
|
||||
| Parameter 1 of System.HashCode.Initialize | parameter | 32 |
|
||||
| Parameter 1 of System.INumber.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Byte>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Char>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Decimal>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Double>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Half>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Int16>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Int32>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Int64>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<IntPtr>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<SByte>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Single>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<UInt16>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<UInt32>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<UInt64>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<UIntPtr>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.DelegateEnumerator.ShouldIncludeEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.DelegateEnumerator.ShouldRecurseIntoEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.DelegateEnumerator.TransformEntry | parameter | 32 |
|
||||
@@ -983,6 +1029,8 @@
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.FindTransform.BeginInvoke | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.FindTransform.EndInvoke | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.FindTransform.Invoke | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable<!0>.FindPredicate.Invoke | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable<!0>.FindTransform.Invoke | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerableFactory.<>c.<DirectoryInfos>b__7_0 | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerableFactory.<>c.<FileInfos>b__6_0 | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerableFactory.<>c.<FileSystemInfos>b__8_0 | parameter | 32 |
|
||||
@@ -999,6 +1047,9 @@
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator.ShouldIncludeEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator.ShouldRecurseIntoEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator.TransformEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator<!0>.ShouldIncludeEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator<!0>.ShouldRecurseIntoEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator<!0>.TransformEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.FileSystem.DirectoryExists | parameter | 32 |
|
||||
| Parameter 1 of System.IO.FileSystem.FileExists | parameter | 32 |
|
||||
| Parameter 1 of System.IO.FileSystemInfo.Init | parameter | 32 |
|
||||
@@ -1073,7 +1124,14 @@
|
||||
| Parameter 1 of System.OrdinalComparer.IsWellKnownOrdinalComparerCore | parameter | 32 |
|
||||
| Parameter 1 of System.ParseNumbers.EatWhiteSpace | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlyMemory.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlyMemory<!0>.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlyMemory<Char>.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan<!0>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan<Byte>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan<Char>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan<Int32>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan<Object>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Reflection.Binder.ReorderArgumentArray | parameter | 32 |
|
||||
| Parameter 1 of System.Reflection.CustomAttribute.FilterCustomAttributeRecord | parameter | 32 |
|
||||
| Parameter 1 of System.Reflection.CustomAttribute.GetPropertyOrFieldData | parameter | 32 |
|
||||
@@ -1107,9 +1165,27 @@
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetNotificationForWaitCompletion | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.SetNotificationForWaitCompletion | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Boolean>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Boolean>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Byte[]>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Byte[]>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String[]>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String[]>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.SetNotificationForWaitCompletion | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<Int32>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<Int32>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Start | parameter | 32 |
|
||||
@@ -1120,6 +1196,13 @@
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<Int32>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<Int32>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<VoidTaskResult>.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.QCallAssembly..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.QCallModule..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.QCallTypeHandle..ctor | parameter | 32 |
|
||||
@@ -1195,6 +1278,8 @@
|
||||
| Parameter 1 of System.RuntimeType.RuntimeTypeCache.GetMemberList | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.AddSpecialInterface | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.Insert | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.AddSpecialInterface | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.Insert | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeType.SplitName | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeTypeHandle.CopyRuntimeTypeHandles | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeTypeHandle.GetActivationInfo | parameter | 32 |
|
||||
@@ -1204,6 +1289,13 @@
|
||||
| Parameter 1 of System.Single.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.Single.TryParse | parameter | 32 |
|
||||
| Parameter 1 of System.Span..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<!0>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<!1>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<Byte>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<Char>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<GuidResult>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<Int32>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<Object>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.SpanHelpers.SequenceEqual | parameter | 32 |
|
||||
| Parameter 1 of System.String.Create | parameter | 32 |
|
||||
| Parameter 1 of System.String.IndexOfNewlineChar | parameter | 32 |
|
||||
@@ -1260,9 +1352,13 @@
|
||||
| Parameter 1 of System.Threading.SpinLock.Enter | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.SpinLock.TryEnter | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.IProducerConsumerQueue.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.IProducerConsumerQueue<!0>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.IProducerConsumerQueue<Task>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.MultiProducerMultiConsumerQueue.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.SingleProducerSingleConsumerQueue.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.SingleProducerSingleConsumerQueue.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.SingleProducerSingleConsumerQueue<!0>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.SingleProducerSingleConsumerQueue<!0>.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.Task.AddToList | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.Task.CreationOptionsFromContinuationOptions | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.Task.ExecuteWithThreadLocal | parameter | 32 |
|
||||
@@ -1310,6 +1406,9 @@
|
||||
| Parameter 1 of System.UIntPtr.TryParse | parameter | 32 |
|
||||
| Parameter 1 of System.Version.TryParse | parameter | 32 |
|
||||
| Parameter 1 of System.WeakReference.TryGetTarget | parameter | 32 |
|
||||
| Parameter 1 of System.WeakReference<AssemblyLoadContext>.TryGetTarget | parameter | 32 |
|
||||
| Parameter 1 of System.WeakReference<CounterGroup>.TryGetTarget | parameter | 32 |
|
||||
| Parameter 1 of System.WeakReference<EventSource>.TryGetTarget | parameter | 32 |
|
||||
| Parameter 1 of System.__DTString.GetRegularToken | parameter | 32 |
|
||||
| Parameter 2 of Internal.Runtime.InteropServices.IClassFactory.CreateInstance | parameter | 32 |
|
||||
| Parameter 2 of Interop.Globalization.GetJapaneseEraStartDate | parameter | 32 |
|
||||
@@ -1370,15 +1469,49 @@
|
||||
| Parameter 2 of System.Char.TryFormat | parameter | 32 |
|
||||
| Parameter 2 of System.Char.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Concurrent.ConcurrentQueue.SnapForObservation | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Concurrent.ConcurrentQueue<!0>.SnapForObservation | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.DictionaryEntry.Deconstruct | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary.CollectionsMarshalHelper.GetValueRefOrAddDefault | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary.Remove | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<!0,!1>.CollectionsMarshalHelper.GetValueRefOrAddDefault | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Int32,ChannelInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Int32,CultureInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Int32,HashSet<Token>>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Int32,String>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Int32,Task>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<MemberInfo,NullabilityState>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Module,NotAnnotatedStatus>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Object,Object>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<ReadOnlyMemory<Char>,ConsoleKeyInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,Assembly>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,Boolean>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,CultureData>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,CultureInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,Int32>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,IntPtr>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,IsolatedComponentLoadContext>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,List<Int32>>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,List<RuntimePropertyInfo>>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,LocalDataStoreSlot>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,Object>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,ResourceLocator>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,ResourceSet>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,String>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,TimeZoneInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,Type>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Type,AttributeUsageAttribute>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Type,TraceLoggingTypeInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<UInt64,Char>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<UInt64,String>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.HashSet.AddIfNotPresent | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.HashSet.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.HashSet<!0>.AddIfNotPresent | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.IDictionary.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.IDictionary<String,String>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.IReadOnlyDictionary.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.KeyValuePair.Deconstruct | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.KeyValuePair<String,ResourceSet>.Deconstruct | parameter | 32 |
|
||||
| Parameter 2 of System.ComponentModel.DefaultValueAttribute.ctor>g__TryConvertFromInvariantString\|2_0 | parameter | 32 |
|
||||
| Parameter 2 of System.ConsolePal.<TryGetCursorPosition>g__BufferUntil\|83_1 | parameter | 32 |
|
||||
| Parameter 2 of System.Convert.CopyToTempBufferWithoutWhiteSpace | parameter | 32 |
|
||||
@@ -1509,8 +1642,50 @@
|
||||
| Parameter 2 of System.IO.UnmanagedMemoryAccessor.Read | parameter | 32 |
|
||||
| Parameter 2 of System.IO.UnmanagedMemoryAccessor.Write | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Byte>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Char>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<DateOnly>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<DateTime>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<DateTimeOffset>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Decimal>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Double>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Guid>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Half>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Int16>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Int32>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Int64>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<IntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<SByte>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Single>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<TimeOnly>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<TimeSpan>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<UInt16>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<UInt32>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<UInt64>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<UIntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanFormattable.TryFormat | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Byte>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Char>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<DateOnly>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<DateTime>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<DateTimeOffset>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Decimal>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Double>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Guid>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Half>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Int16>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Int32>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Int64>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<IntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<SByte>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Single>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<TimeOnly>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<TimeSpan>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<UInt16>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<UInt32>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<UInt64>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<UIntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.Int16.TryFormat | parameter | 32 |
|
||||
| Parameter 2 of System.Int16.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.Int32.TryFormat | parameter | 32 |
|
||||
@@ -1550,6 +1725,8 @@
|
||||
| Parameter 2 of System.ParseNumbers.GrabLongs | parameter | 32 |
|
||||
| Parameter 2 of System.ParseNumbers.IsDigit | parameter | 32 |
|
||||
| Parameter 2 of System.ReadOnlyMemory.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 2 of System.ReadOnlyMemory<!0>.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 2 of System.ReadOnlyMemory<Char>.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 2 of System.Reflection.AssemblyName.EscapeAsciiChar | parameter | 32 |
|
||||
| Parameter 2 of System.Reflection.CustomAttribute.AttributeUsageCheck | parameter | 32 |
|
||||
| Parameter 2 of System.Reflection.CustomAttribute.ParseAttributeUsageAttribute | parameter | 32 |
|
||||
@@ -1610,18 +1787,35 @@
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Boolean>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Byte[]>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String[]>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<Int32>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable.Container.FindEntry | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable.Container.TryGetEntry | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable.Container.TryGetValueWorker | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<!0,!1>.Container.FindEntry | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<!0,!1>.Container.TryGetEntry | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<!0,!1>.Container.TryGetValueWorker | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<!0,!1>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<Assembly,DllImportResolver>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<Object,SerializationInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ICastable.IsInstanceOfInterface | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ICastableHelpers.IsInstanceOfInterface | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<Int32>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.InteropServices.CollectionsMarshal.GetValueRefOrAddDefault | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.InteropServices.ComTypes.IBindCtx.GetObjectParam | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.InteropServices.ComTypes.IConnectionPoint.Advise | parameter | 32 |
|
||||
@@ -1724,6 +1918,7 @@
|
||||
| Parameter 2 of System.Text.UTF8Encoding.DecodeFirstRune | parameter | 32 |
|
||||
| Parameter 2 of System.Text.UTF8Encoding.TryGetByteCount | parameter | 32 |
|
||||
| Parameter 2 of System.Text.Unicode.TextSegmentationUtility.DecodeFirstRune.Invoke | parameter | 32 |
|
||||
| Parameter 2 of System.Text.Unicode.TextSegmentationUtility.DecodeFirstRune<!0>.Invoke | parameter | 32 |
|
||||
| Parameter 2 of System.Text.Unicode.Utf8.FromUtf16 | parameter | 32 |
|
||||
| Parameter 2 of System.Text.Unicode.Utf8.ToUtf16 | parameter | 32 |
|
||||
| Parameter 2 of System.Text.Unicode.Utf8Utility.GetPointerToFirstInvalidByte | parameter | 32 |
|
||||
@@ -1748,6 +1943,8 @@
|
||||
| Parameter 2 of System.Threading.SpinLock.TryEnter | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.Tasks.SingleProducerSingleConsumerQueue.EnqueueSlow | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.Tasks.SingleProducerSingleConsumerQueue.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.Tasks.SingleProducerSingleConsumerQueue<!0>.EnqueueSlow | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.Tasks.SingleProducerSingleConsumerQueue<!0>.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.Tasks.Task.CreationOptionsFromContinuationOptions | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.ThreadPool.GetNextConfigUInt32Value | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.ThreadPoolWorkQueue.Dequeue | parameter | 32 |
|
||||
@@ -1810,6 +2007,7 @@
|
||||
| Parameter 3 of System.Byte.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.Char.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.Collections.Concurrent.ConcurrentQueue.SnapForObservation | parameter | 32 |
|
||||
| Parameter 3 of System.Collections.Concurrent.ConcurrentQueue<!0>.SnapForObservation | parameter | 32 |
|
||||
| Parameter 3 of System.Collections.Hashtable.InitHash | parameter | 32 |
|
||||
| Parameter 3 of System.ConsolePal.<TryGetCursorPosition>g__AppendToStdInReaderUntil\|83_2 | parameter | 32 |
|
||||
| Parameter 3 of System.ConsolePal.<TryGetCursorPosition>g__BufferUntil\|83_1 | parameter | 32 |
|
||||
@@ -1868,6 +2066,21 @@
|
||||
| Parameter 3 of System.Half.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.HashCode.Initialize | parameter | 32 |
|
||||
| Parameter 3 of System.INumber.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Byte>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Char>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Decimal>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Double>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Half>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Int16>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Int32>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Int64>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<IntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<SByte>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Single>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<UInt16>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<UInt32>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<UInt64>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<UIntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.IO.BufferedStream.WriteToBuffer | parameter | 32 |
|
||||
| Parameter 3 of System.IO.Path.TryJoin | parameter | 32 |
|
||||
| Parameter 3 of System.IO.StdInReader.GetKeyFromCharValue | parameter | 32 |
|
||||
@@ -1947,6 +2160,7 @@
|
||||
| Parameter 3 of System.Resources.ResourceReader.GetResourceData | parameter | 32 |
|
||||
| Parameter 3 of System.Resources.RuntimeResourceSet.ReadValue | parameter | 32 |
|
||||
| Parameter 3 of System.Runtime.CompilerServices.ConditionalWeakTable.Container.TryGetEntry | parameter | 32 |
|
||||
| Parameter 3 of System.Runtime.CompilerServices.ConditionalWeakTable<!0,!1>.Container.TryGetEntry | parameter | 32 |
|
||||
| Parameter 3 of System.Runtime.InteropServices.ComTypes.IMoniker.BindToObject | parameter | 32 |
|
||||
| Parameter 3 of System.Runtime.InteropServices.ComTypes.IMoniker.BindToStorage | parameter | 32 |
|
||||
| Parameter 3 of System.Runtime.InteropServices.ComTypes.IMoniker.ComposeWith | parameter | 32 |
|
||||
@@ -1981,6 +2195,8 @@
|
||||
| Parameter 3 of System.RuntimeType.GetType | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.PopulateLiteralFields | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.PopulateRtFields | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.PopulateLiteralFields | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.PopulateRtFields | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeTypeHandle.GetActivationInfo | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeTypeHandle.GetTypeByName | parameter | 32 |
|
||||
| Parameter 3 of System.SByte.TryParse | parameter | 32 |
|
||||
@@ -2005,6 +2221,7 @@
|
||||
| Parameter 3 of System.Text.UTF8Encoding.DecodeFirstRune | parameter | 32 |
|
||||
| Parameter 3 of System.Text.UTF8Encoding.EncodeRune | parameter | 32 |
|
||||
| Parameter 3 of System.Text.Unicode.TextSegmentationUtility.DecodeFirstRune.Invoke | parameter | 32 |
|
||||
| Parameter 3 of System.Text.Unicode.TextSegmentationUtility.DecodeFirstRune<!0>.Invoke | parameter | 32 |
|
||||
| Parameter 3 of System.Text.Unicode.Utf8.FromUtf16 | parameter | 32 |
|
||||
| Parameter 3 of System.Text.Unicode.Utf8.ToUtf16 | parameter | 32 |
|
||||
| Parameter 3 of System.Text.Unicode.Utf8Utility.GetPointerToFirstInvalidByte | parameter | 32 |
|
||||
@@ -2015,6 +2232,7 @@
|
||||
| Parameter 3 of System.Threading.SpinLock.ContinueTryEnterWithThreadTracking | parameter | 32 |
|
||||
| Parameter 3 of System.Threading.Tasks.AwaitTaskContinuation.RunCallback | parameter | 32 |
|
||||
| Parameter 3 of System.Threading.Tasks.SingleProducerSingleConsumerQueue.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 3 of System.Threading.Tasks.SingleProducerSingleConsumerQueue<!0>.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 3 of System.Threading.Tasks.Task.AtomicStateUpdate | parameter | 32 |
|
||||
| Parameter 3 of System.Threading.ThreadPool.GetNextConfigUInt32Value | parameter | 32 |
|
||||
| Parameter 3 of System.TimeOnly.TryParse | parameter | 32 |
|
||||
@@ -2052,6 +2270,7 @@
|
||||
| Parameter 4 of System.Buffers.Text.Utf8Parser.TryParseAsSpecialFloatingPoint | parameter | 32 |
|
||||
| Parameter 4 of System.Buffers.Text.Utf8Parser.TryParseNumber | parameter | 32 |
|
||||
| Parameter 4 of System.Collections.Concurrent.ConcurrentQueue.SnapForObservation | parameter | 32 |
|
||||
| Parameter 4 of System.Collections.Concurrent.ConcurrentQueue<!0>.SnapForObservation | parameter | 32 |
|
||||
| Parameter 4 of System.Collections.Hashtable.InitHash | parameter | 32 |
|
||||
| Parameter 4 of System.ConsolePal.<TryGetCursorPosition>g__AppendToStdInReaderUntil\|83_2 | parameter | 32 |
|
||||
| Parameter 4 of System.ConsolePal.<TryGetCursorPosition>g__BufferUntil\|83_1 | parameter | 32 |
|
||||
@@ -2163,6 +2382,7 @@
|
||||
| Parameter 4 of System.RuntimeFieldHandle.GetValue | parameter | 32 |
|
||||
| Parameter 4 of System.RuntimeType.FilterHelper | parameter | 32 |
|
||||
| Parameter 4 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.PopulateEvents | parameter | 32 |
|
||||
| Parameter 4 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.PopulateEvents | parameter | 32 |
|
||||
| Parameter 4 of System.RuntimeTypeHandle.GetActivationInfo | parameter | 32 |
|
||||
| Parameter 4 of System.TermInfo.ParameterizedStrings.EvaluateInternal | parameter | 32 |
|
||||
| Parameter 4 of System.Text.ASCIIEncoding.GetByteCountFast | parameter | 32 |
|
||||
@@ -2228,6 +2448,8 @@
|
||||
| Parameter 5 of System.RuntimeType.FilterHelper | parameter | 32 |
|
||||
| Parameter 5 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.PopulateProperties | parameter | 32 |
|
||||
| Parameter 5 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.PopulateRtFields | parameter | 32 |
|
||||
| Parameter 5 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.PopulateProperties | parameter | 32 |
|
||||
| Parameter 5 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.PopulateRtFields | parameter | 32 |
|
||||
| Parameter 5 of System.TermInfo.ParameterizedStrings.EvaluateInternal | parameter | 32 |
|
||||
| Parameter 5 of System.Text.ASCIIEncoding.GetBytesFast | parameter | 32 |
|
||||
| Parameter 5 of System.Text.ASCIIEncoding.GetCharsFast | parameter | 32 |
|
||||
@@ -2336,10 +2558,16 @@
|
||||
| Parameter 21 of System.TupleExtensions.Deconstruct | parameter | 32 |
|
||||
| System.ByReference.Value | property | 32 |
|
||||
| System.ByReference.get_Value | method | 32 |
|
||||
| System.ByReference<!0>.get_Value | method | 32 |
|
||||
| System.ByReference<Byte>.get_Value | method | 32 |
|
||||
| System.Collections.Generic.Dictionary.CollectionsMarshalHelper.GetValueRefOrAddDefault | method | 32 |
|
||||
| System.Collections.Generic.Dictionary.FindValue | method | 32 |
|
||||
| System.Collections.Generic.Dictionary.GetBucket | method | 32 |
|
||||
| System.Collections.Generic.Dictionary<!0,!1>.CollectionsMarshalHelper.GetValueRefOrAddDefault | method | 32 |
|
||||
| System.Collections.Generic.Dictionary<!0,!1>.FindValue | method | 32 |
|
||||
| System.Collections.Generic.Dictionary<!0,!1>.GetBucket | method | 32 |
|
||||
| System.Collections.Generic.HashSet.GetBucketRef | method | 32 |
|
||||
| System.Collections.Generic.HashSet<!0>.GetBucketRef | method | 32 |
|
||||
| System.Decimal.AsMutable | method | 32 |
|
||||
| System.Decimal.Max | method | 32 |
|
||||
| System.Decimal.Min | method | 32 |
|
||||
@@ -2350,6 +2578,16 @@
|
||||
| System.ReadOnlySpan.GetPinnableReference | method | 32 |
|
||||
| System.ReadOnlySpan.Item | property | 32 |
|
||||
| System.ReadOnlySpan.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<!0>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<Boolean>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<Byte>.GetPinnableReference | method | 32 |
|
||||
| System.ReadOnlySpan<Byte>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<Char>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<Int32>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<Object>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<SafeWaitHandle>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<String>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<WaitHandle>.get_Item | method | 32 |
|
||||
| System.Runtime.CompilerServices.CastHelpers.Element | method | 32 |
|
||||
| System.Runtime.CompilerServices.CastHelpers.LdelemaRef | method | 32 |
|
||||
| System.Runtime.CompilerServices.CastHelpers.TableData | method | 32 |
|
||||
@@ -2371,6 +2609,19 @@
|
||||
| System.Span.GetPinnableReference | method | 32 |
|
||||
| System.Span.Item | property | 32 |
|
||||
| System.Span.get_Item | method | 32 |
|
||||
| System.Span<!0>.get_Item | method | 32 |
|
||||
| System.Span<!1>.get_Item | method | 32 |
|
||||
| System.Span<Byte>.GetPinnableReference | method | 32 |
|
||||
| System.Span<Byte>.get_Item | method | 32 |
|
||||
| System.Span<Char>.GetPinnableReference | method | 32 |
|
||||
| System.Span<Char>.get_Item | method | 32 |
|
||||
| System.Span<EventPipeProviderConfigurationNative>.GetPinnableReference | method | 32 |
|
||||
| System.Span<EventPipeProviderConfigurationNative>.get_Item | method | 32 |
|
||||
| System.Span<IOVector>.get_Item | method | 32 |
|
||||
| System.Span<Int32>.get_Item | method | 32 |
|
||||
| System.Span<IntPtr>.get_Item | method | 32 |
|
||||
| System.Span<Object>.get_Item | method | 32 |
|
||||
| System.Span<SafeWaitHandle>.get_Item | method | 32 |
|
||||
| System.SpanHelpers.Add | method | 32 |
|
||||
| System.String.GetPinnableReference | method | 32 |
|
||||
| System.String.GetRawStringData | method | 32 |
|
||||
|
||||
23
csharp/ql/test/library-tests/csharp11/GenericAttribute.cs
Normal file
23
csharp/ql/test/library-tests/csharp11/GenericAttribute.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
[assembly: MyGenericAttribute<int>()]
|
||||
[module: MyGeneric2<object, object>()]
|
||||
|
||||
public class MyGenericAttribute<T> : Attribute { }
|
||||
public class MyGeneric2Attribute<T, U> : Attribute { }
|
||||
|
||||
public class TestGenericAttribute
|
||||
{
|
||||
|
||||
[MyGenericAttribute<int>()]
|
||||
public void M1() { }
|
||||
|
||||
[MyGeneric<string>()]
|
||||
public void M2() { }
|
||||
|
||||
[MyGeneric2<int, string>()]
|
||||
public void M3() { }
|
||||
|
||||
[return: MyGeneric<object>()]
|
||||
public int M4() { return 0; }
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
public interface INumber<T> where T : INumber<T>
|
||||
{
|
||||
static abstract T operator ++(T other);
|
||||
|
||||
static virtual T operator --(T other) => other;
|
||||
|
||||
static abstract T Add(T left, T right);
|
||||
|
||||
static virtual T Subtract(T left, T right) => left;
|
||||
|
||||
static T Zero() => default(T);
|
||||
}
|
||||
|
||||
public class Complex : INumber<Complex>
|
||||
{
|
||||
public double Real { get; private set; } = 0.0;
|
||||
public double Imaginary { get; private set; } = 0.0;
|
||||
|
||||
public Complex() { }
|
||||
|
||||
public static Complex Zero() => new Complex();
|
||||
|
||||
public static Complex operator ++(Complex other) =>
|
||||
new Complex { Real = other.Real + 1.0, Imaginary = other.Imaginary };
|
||||
|
||||
public static Complex operator --(Complex other) =>
|
||||
new Complex { Real = other.Real - 1.0, Imaginary = other.Imaginary };
|
||||
|
||||
public static Complex Add(Complex left, Complex right) =>
|
||||
new Complex { Real = left.Real + right.Real, Imaginary = left.Imaginary + right.Imaginary };
|
||||
|
||||
public static Complex Subtract(Complex left, Complex right) =>
|
||||
new Complex { Real = left.Real - right.Real, Imaginary = left.Imaginary - right.Imaginary };
|
||||
}
|
||||
16
csharp/ql/test/library-tests/csharp11/cil/Assembly.cs_
Normal file
16
csharp/ql/test/library-tests/csharp11/cil/Assembly.cs_
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Assembly
|
||||
{
|
||||
public class MyAssemblyGeneric1Attribute<T> : Attribute { }
|
||||
public class MyAssemblyGeneric2Attribute<T, U> : Attribute { }
|
||||
|
||||
public class TestAssemblyGenericAttribute
|
||||
{
|
||||
[MyAssemblyGeneric1Attribute<object>()]
|
||||
public void M1() { }
|
||||
|
||||
[MyAssemblyGeneric2<int, string>()]
|
||||
public void M2() { }
|
||||
}
|
||||
}
|
||||
BIN
csharp/ql/test/library-tests/csharp11/cil/assembly.dll
Normal file
BIN
csharp/ql/test/library-tests/csharp11/cil/assembly.dll
Normal file
Binary file not shown.
7
csharp/ql/test/library-tests/csharp11/cil/class1.cs
Normal file
7
csharp/ql/test/library-tests/csharp11/cil/class1.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
using Assembly;
|
||||
|
||||
public class Class1
|
||||
{
|
||||
public static void Main(string[] args) { }
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
| assembly.dll:0:0:0:0 | [MyAssemblyGeneric1Attribute<Object>(...)] | MyAssemblyGeneric1Attribute<Object> | 1 | (Object) |
|
||||
| assembly.dll:0:0:0:0 | [MyAssemblyGeneric2Attribute<Int32,String>(...)] | MyAssemblyGeneric2Attribute<Int32,String> | 2 | (Int32,String) |
|
||||
@@ -0,0 +1,9 @@
|
||||
import semmle.code.cil.CIL
|
||||
|
||||
private string getTypeArguments(GenericAttribute a) {
|
||||
result = "(" + concat(Type t | t = a.getATypeArgument() | t.getName(), ",") + ")"
|
||||
}
|
||||
|
||||
from GenericAttribute a
|
||||
where a.getFile().getStem() = "assembly"
|
||||
select a, a.getType().getName(), a.getNumberOfTypeArguments(), getTypeArguments(a)
|
||||
1
csharp/ql/test/library-tests/csharp11/cil/options
Normal file
1
csharp/ql/test/library-tests/csharp11/cil/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --cil
|
||||
@@ -0,0 +1,6 @@
|
||||
| GenericAttribute.cs:3:12:3:34 | [assembly: MyGeneric<Int32>(...)] | MyGenericAttribute<Int32> | 1 | (Int32) | GenericAssemblyAttribute |
|
||||
| GenericAttribute.cs:4:10:4:35 | [module: MyGeneric2<Object,Object>(...)] | MyGeneric2Attribute<Object,Object> | 2 | (Object) | GenericModuleAttribute |
|
||||
| GenericAttribute.cs:12:6:12:28 | [MyGeneric<Int32>(...)] | MyGenericAttribute<Int32> | 1 | (Int32) | GenericDefaultAttribute |
|
||||
| GenericAttribute.cs:15:6:15:22 | [MyGeneric<String>(...)] | MyGenericAttribute<String> | 1 | (String) | GenericDefaultAttribute |
|
||||
| GenericAttribute.cs:18:6:18:28 | [MyGeneric2<Int32,String>(...)] | MyGeneric2Attribute<Int32,String> | 2 | (Int32,String) | GenericDefaultAttribute |
|
||||
| GenericAttribute.cs:21:14:21:30 | [return: MyGeneric<Object>(...)] | MyGenericAttribute<Object> | 1 | (Object) | GenericReturnAttribute |
|
||||
10
csharp/ql/test/library-tests/csharp11/genericAttribute.ql
Normal file
10
csharp/ql/test/library-tests/csharp11/genericAttribute.ql
Normal file
@@ -0,0 +1,10 @@
|
||||
import csharp
|
||||
|
||||
private string getTypeArguments(GenericAttribute a) {
|
||||
result = "(" + concat(Type t | t = a.getATypeArgument() | t.getName(), ",") + ")"
|
||||
}
|
||||
|
||||
from GenericAttribute a
|
||||
where a.getFile().getStem() = "GenericAttribute"
|
||||
select a, a.getType().getName(), a.getNumberOfTypeArguments(), getTypeArguments(a),
|
||||
a.getAPrimaryQlClass()
|
||||
@@ -0,0 +1,20 @@
|
||||
interfacemembers
|
||||
| INumber<> | StaticInterfaceMembers.cs:3:32:3:33 | ++ | abstract |
|
||||
| INumber<> | StaticInterfaceMembers.cs:3:32:3:33 | ++ | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:3:32:3:33 | ++ | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:5:31:5:32 | -- | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:5:31:5:32 | -- | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:5:31:5:32 | -- | virtual |
|
||||
| INumber<> | StaticInterfaceMembers.cs:7:23:7:25 | Add | abstract |
|
||||
| INumber<> | StaticInterfaceMembers.cs:7:23:7:25 | Add | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:7:23:7:25 | Add | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:9:22:9:29 | Subtract | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:9:22:9:29 | Subtract | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:9:22:9:29 | Subtract | virtual |
|
||||
| INumber<> | StaticInterfaceMembers.cs:11:14:11:17 | Zero | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:11:14:11:17 | Zero | static |
|
||||
implements
|
||||
| StaticInterfaceMembers.cs:23:36:23:37 | ++ | StaticInterfaceMembers.cs:3:32:3:33 | ++ |
|
||||
| StaticInterfaceMembers.cs:26:36:26:37 | -- | StaticInterfaceMembers.cs:5:31:5:32 | -- |
|
||||
| StaticInterfaceMembers.cs:29:27:29:29 | Add | StaticInterfaceMembers.cs:7:23:7:25 | Add |
|
||||
| StaticInterfaceMembers.cs:32:27:32:34 | Subtract | StaticInterfaceMembers.cs:9:22:9:29 | Subtract |
|
||||
@@ -0,0 +1,18 @@
|
||||
import csharp
|
||||
|
||||
query predicate interfacemembers(string interface, Member m, string modifier) {
|
||||
exists(Interface i |
|
||||
i.isUnboundDeclaration() and
|
||||
i.getFile().getStem() = "StaticInterfaceMembers" and
|
||||
i.getName() = interface and
|
||||
m = i.getAMember() and
|
||||
modifier = m.getAModifier().getName()
|
||||
)
|
||||
}
|
||||
|
||||
query predicate implements(Overridable o, Virtualizable v) {
|
||||
v.getFile().getStem() = "StaticInterfaceMembers" and
|
||||
(v.isVirtual() or v.isAbstract()) and
|
||||
v.isStatic() and
|
||||
v.getAnImplementor() = o
|
||||
}
|
||||
@@ -7,6 +7,7 @@ type NUL && "%CODEQL_DIST%\codeql" database index-files ^
|
||||
--include-extension=.xml ^
|
||||
--size-limit 10m ^
|
||||
--language xml ^
|
||||
--working-dir=. ^
|
||||
-- ^
|
||||
"%CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE%" ^
|
||||
>nul 2>&1
|
||||
|
||||
@@ -9,6 +9,7 @@ set -eu
|
||||
--include-extension=.xml \
|
||||
--size-limit 10m \
|
||||
--language xml \
|
||||
--working-dir=. \
|
||||
-- \
|
||||
"$CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE" \
|
||||
> /dev/null 2>&1
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import go
|
||||
private import semmle.go.dataflow.DataFlowForStringsNewReplacer
|
||||
|
||||
/** Provides predicates and classes for working with string operations. */
|
||||
module StringOps {
|
||||
@@ -162,6 +163,114 @@ module StringOps {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is equivalent to `strings.ReplaceAll(s, old, new)`.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `StringOps::ReplaceAll::Range` instead.
|
||||
*/
|
||||
class ReplaceAll extends DataFlow::Node instanceof ReplaceAll::Range {
|
||||
/**
|
||||
* Gets the `old` in `strings.ReplaceAll(s, old, new)`.
|
||||
*/
|
||||
string getReplacedString() { result = super.getReplacedString() }
|
||||
}
|
||||
|
||||
/** Provides predicates and classes for working with prefix checks. */
|
||||
module ReplaceAll {
|
||||
/**
|
||||
* An expression that is equivalent to `strings.ReplaceAll(s, old, new)`.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models, extend
|
||||
* `StringOps::ReplaceAll` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the `old` in `strings.ReplaceAll(s, old, new)`.
|
||||
*/
|
||||
abstract string getReplacedString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `strings.ReplaceAll` or `strings.Replace` with a negative `n`
|
||||
* so that all instances are replaced.
|
||||
*/
|
||||
private class StringsReplaceAll extends Range, DataFlow::CallNode {
|
||||
StringsReplaceAll() {
|
||||
exists(string name | this.getTarget().hasQualifiedName("strings", name) |
|
||||
name = "ReplaceAll"
|
||||
or
|
||||
name = "Replace" and
|
||||
this.getArgument(3).getNumericValue() < 0
|
||||
)
|
||||
}
|
||||
|
||||
override string getReplacedString() { result = this.getArgument(1).getStringValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `strings.NewReplacer`.
|
||||
*/
|
||||
private class StringsNewReplacerCall extends DataFlow::CallNode {
|
||||
StringsNewReplacerCall() { this.getTarget().hasQualifiedName("strings", "NewReplacer") }
|
||||
|
||||
/**
|
||||
* Gets an argument to this call corresponding to a string that will be
|
||||
* replaced.
|
||||
*/
|
||||
DataFlow::Node getAReplacedArgument() {
|
||||
exists(int n | n % 2 = 0 and result = this.getArgument(n))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration for tracking flow from a call to `strings.NewReplacer` to
|
||||
* the receiver of a call to `strings.Replacer.Replace` or
|
||||
* `strings.Replacer.WriteString`.
|
||||
*/
|
||||
private class StringsNewReplacerConfiguration extends DataFlowForStringsNewReplacer::Configuration {
|
||||
StringsNewReplacerConfiguration() { this = "StringsNewReplacerConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof StringsNewReplacerCall
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
sink = call.getReceiver() and
|
||||
call.getTarget().hasQualifiedName("strings", "Replacer", ["Replace", "WriteString"])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `strings.Replacer.Replace` or `strings.Replacer.WriteString`.
|
||||
*/
|
||||
private class StringsReplacerReplaceOrWriteString extends Range {
|
||||
string replacedString;
|
||||
|
||||
StringsReplacerReplaceOrWriteString() {
|
||||
exists(
|
||||
StringsNewReplacerConfiguration config, StringsNewReplacerCall source,
|
||||
DataFlow::Node sink, DataFlow::MethodCallNode call
|
||||
|
|
||||
config.hasFlow(source, sink) and
|
||||
sink = call.getReceiver() and
|
||||
replacedString = source.getAReplacedArgument().getStringValue() and
|
||||
(
|
||||
call.getTarget().hasQualifiedName("strings", "Replacer", "Replace") and
|
||||
this = call.getResult()
|
||||
or
|
||||
call.getTarget().hasQualifiedName("strings", "Replacer", "WriteString") and
|
||||
this = call.getArgument(1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getReplacedString() { result = replacedString }
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates and classes for working with Printf-style formatters. */
|
||||
module Formatting {
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Provides a library for local (intra-procedural) and global (inter-procedural)
|
||||
* data flow analysis: deciding whether data can flow from a _source_ to a
|
||||
* _sink_.
|
||||
*
|
||||
* Unless configured otherwise, _flow_ means that the exact value of
|
||||
* the source may reach the sink. We do not track flow across pointer
|
||||
* dereferences or array indexing. To track these types of flow, where the
|
||||
* exact value may not be preserved, import
|
||||
* `semmle.code.go.dataflow.TaintTracking`.
|
||||
*
|
||||
* To use global (interprocedural) data flow, extend the class
|
||||
* `DataFlow::Configuration` as documented on that class. To use local
|
||||
* (intraprocedural) data flow, invoke `DataFlow::localFlow` or
|
||||
* `DataFlow::LocalFlowStep` with arguments of type `DataFlow::Node`.
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
/**
|
||||
* Provides a library for local (intra-procedural) and global (inter-procedural)
|
||||
* data flow analysis.
|
||||
*/
|
||||
module DataFlowForStringsNewReplacer {
|
||||
import semmle.go.dataflow.internal.DataFlowImplForStringsNewReplacer
|
||||
import Properties
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,22 +41,12 @@ module LogInjection {
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `strings.Replace` or `strings.ReplaceAll`, considered as a sanitizer
|
||||
* for log injection.
|
||||
* An expression that is equivalent to `strings.ReplaceAll(s, old, new)`,
|
||||
* where `old` is a newline character, considered as a sanitizer for log
|
||||
* injection.
|
||||
*/
|
||||
class ReplaceSanitizer extends Sanitizer {
|
||||
ReplaceSanitizer() {
|
||||
exists(string name, DataFlow::CallNode call |
|
||||
this = call and
|
||||
call.getTarget().hasQualifiedName("strings", name) and
|
||||
call.getArgument(1).getStringValue().matches("%" + ["\r", "\n"] + "%")
|
||||
|
|
||||
name = "Replace" and
|
||||
call.getArgument(3).getNumericValue() < 0
|
||||
or
|
||||
name = "ReplaceAll"
|
||||
)
|
||||
}
|
||||
class ReplaceSanitizer extends StringOps::ReplaceAll, Sanitizer {
|
||||
ReplaceSanitizer() { this.getReplacedString() = ["\r", "\n"] }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -84,24 +84,13 @@ module StringBreak {
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `strings.Replace` or `strings.ReplaceAll`, considered as a sanitizer
|
||||
* for unsafe quoting.
|
||||
* An expression that is equivalent to `strings.ReplaceAll(s, old, new)`,
|
||||
* considered as a sanitizer for unsafe quoting.
|
||||
*/
|
||||
class ReplaceSanitizer extends Sanitizer {
|
||||
class ReplaceSanitizer extends StringOps::ReplaceAll, Sanitizer {
|
||||
Quote quote;
|
||||
|
||||
ReplaceSanitizer() {
|
||||
exists(string name, DataFlow::CallNode call |
|
||||
this = call and
|
||||
call.getTarget().hasQualifiedName("strings", name) and
|
||||
call.getArgument(1).getStringValue().matches("%" + quote + "%")
|
||||
|
|
||||
name = "Replace" and
|
||||
call.getArgument(3).getNumericValue() < 0
|
||||
or
|
||||
name = "ReplaceAll"
|
||||
)
|
||||
}
|
||||
ReplaceSanitizer() { this.getReplacedString().matches("%" + quote + "%") }
|
||||
|
||||
override Quote getQuote() { result = quote }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Replacing "\r" or "\n" using the functions `strings.ReplaceAll`, `strings.Replace`, `strings.Replacer.Replace` and `strings.Replacer.WriteString` has been added as a sanitizer for the queries "Log entries created from user input".
|
||||
* The functions `strings.Replacer.Replace` and `strings.Replacer.WriteString` have been added as sanitizers for the query "Potentially unsafe quoting".
|
||||
@@ -1,9 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"strings"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
)
|
||||
|
||||
// Good because there is no concatenation with quotes:
|
||||
@@ -37,3 +39,28 @@ func saveGood3(id string, version interface{}) {
|
||||
Values(id, sq.Expr("'"+escaped+"'")).
|
||||
Exec()
|
||||
}
|
||||
|
||||
var globalReplacer = strings.NewReplacer("\"", "", "'", "")
|
||||
|
||||
// Good because quote characters are removed before concatenation:
|
||||
func saveGood4(id string, version interface{}) {
|
||||
versionJSON, _ := json.Marshal(version)
|
||||
escaped := globalReplacer.Replace(string(versionJSON))
|
||||
sq.StatementBuilder.
|
||||
Insert("resources").
|
||||
Columns("resource_id", "version_md5").
|
||||
Values(id, sq.Expr("'"+escaped+"'")).
|
||||
Exec()
|
||||
}
|
||||
|
||||
// Good because quote characters are removed before concatenation:
|
||||
func saveGood5(id string, version interface{}) {
|
||||
versionJSON, _ := json.Marshal(version)
|
||||
buf := new(bytes.Buffer)
|
||||
globalReplacer.WriteString(buf, string(versionJSON))
|
||||
sq.StatementBuilder.
|
||||
Insert("resources").
|
||||
Columns("resource_id", "version_md5").
|
||||
Values(id, sq.Expr("'"+buf.String()+"'")).
|
||||
Exec()
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package main
|
||||
//go:generate depstubber -vendor go.uber.org/zap Logger,SugaredLogger NewProduction
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
@@ -378,8 +379,43 @@ func handlerGood2(req *http.Request) {
|
||||
log.Printf("user %s logged in.\n", escapedUsername)
|
||||
}
|
||||
|
||||
// GOOD: The user-provided value is escaped before being written to the log.
|
||||
func handlerGood3(req *http.Request) {
|
||||
username := req.URL.Query()["username"][0]
|
||||
replacer := strings.NewReplacer("\n", "", "\r", "")
|
||||
log.Printf("user %s logged in.\n", replacer.Replace(username))
|
||||
log.Printf("user %s logged in.\n", replacerLocal1(username))
|
||||
log.Printf("user %s logged in.\n", replacerLocal2(username))
|
||||
log.Printf("user %s logged in.\n", replacerGlobal1(username))
|
||||
log.Printf("user %s logged in.\n", replacerGlobal2(username))
|
||||
}
|
||||
|
||||
func replacerLocal1(s string) string {
|
||||
replacer := strings.NewReplacer("\n", "", "\r", "")
|
||||
return replacer.Replace(s)
|
||||
}
|
||||
|
||||
func replacerLocal2(s string) string {
|
||||
replacer := strings.NewReplacer("\n", "", "\r", "")
|
||||
buf := new(bytes.Buffer)
|
||||
replacer.WriteString(buf, s)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
var globalReplacer = strings.NewReplacer("\n", "", "\r", "")
|
||||
|
||||
func replacerGlobal1(s string) string {
|
||||
return globalReplacer.Replace(s)
|
||||
}
|
||||
|
||||
func replacerGlobal2(s string) string {
|
||||
buf := new(bytes.Buffer)
|
||||
globalReplacer.WriteString(buf, s)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// GOOD: User-provided values formatted using a %q directive, which escapes newlines
|
||||
func handlerGood3(req *http.Request, ctx *goproxy.ProxyCtx) {
|
||||
func handlerGood4(req *http.Request, ctx *goproxy.ProxyCtx) {
|
||||
username := req.URL.Query()["username"][0]
|
||||
testFlag := req.URL.Query()["testFlag"][0]
|
||||
log.Printf("user %q logged in.\n", username)
|
||||
|
||||
@@ -38,13 +38,12 @@ jakarta.ws.rs.client,1,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,
|
||||
jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,,
|
||||
jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,,94,55
|
||||
java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
java.io,37,,40,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,22,,,,,,,,40,
|
||||
java.lang,13,,75,,,,,,,,,,,,8,,,,,,4,,,1,,,,,,,,,,,,,,,,56,19
|
||||
java.math,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
java.io,37,,42,,15,,,,,,,,,,,,,,,,,,,,,,,,,,,22,,,,,,,,41,1
|
||||
java.lang,13,,76,,,,,,,,,,,,8,,,,,,4,,,1,,,,,,,,,,,,,,,,53,23
|
||||
java.net,10,3,7,,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,,,,,,,3,7,
|
||||
java.nio,15,,16,,13,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,16,
|
||||
java.sql,11,,1,,,,,,,,4,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,1,
|
||||
java.util,44,,461,,,,,,,,,,,,34,,,,,,,5,2,,1,2,,,,,,,,,,,,,,36,425
|
||||
java.sql,11,,2,,,,,,,,4,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,1,1
|
||||
java.util,44,,465,,,,,,,,,,,,34,,,,,,,5,2,,1,2,,,,,,,,,,,,,,38,427
|
||||
javax.faces.context,2,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,7,,
|
||||
javax.jms,,9,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,57,
|
||||
javax.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23
|
||||
|
||||
|
@@ -18,10 +18,10 @@ Java framework & library support
|
||||
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,728,39,,6,,,,,
|
||||
JBoss Logging,``org.jboss.logging``,,,324,,,,,,,
|
||||
`JSON-java <https://github.com/stleary/JSON-java>`_,``org.json``,,236,,,,,,,,
|
||||
Java Standard Library,``java.*``,3,602,130,28,,,7,,,10
|
||||
Java Standard Library,``java.*``,3,609,130,28,,,7,,,10
|
||||
Java extensions,"``javax.*``, ``jakarta.*``",63,609,32,,,4,,1,1,2
|
||||
Kotlin Standard Library,``kotlin*``,,1835,12,10,,,,,,2
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,29,477,101,,,,19,14,,29
|
||||
Others,"``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.hubspot.jinjava``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jdbi.v3.core``, ``org.jooq``, ``org.mvel2``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",60,300,269,,,,14,18,,3
|
||||
Totals,,217,8449,1563,129,6,10,107,33,1,86
|
||||
Totals,,217,8456,1563,129,6,10,107,33,1,86
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ fun getFunctionsByFqName(pluginContext: IrPluginContext, fqName: String): Collec
|
||||
return getFunctionsByFqName(pluginContext, FqName(fqName))
|
||||
}
|
||||
|
||||
fun getFunctionsByFqName(pluginContext: IrPluginContext, fqName: FqName): Collection<IrSimpleFunctionSymbol> {
|
||||
private fun getFunctionsByFqName(pluginContext: IrPluginContext, fqName: FqName): Collection<IrSimpleFunctionSymbol> {
|
||||
@OptIn(FirIncompatiblePluginAPI::class)
|
||||
return pluginContext.referenceFunctions(fqName)
|
||||
}
|
||||
@@ -27,7 +27,7 @@ fun getPropertiesByFqName(pluginContext: IrPluginContext, fqName: String): Colle
|
||||
return getPropertiesByFqName(pluginContext, FqName(fqName))
|
||||
}
|
||||
|
||||
fun getPropertiesByFqName(pluginContext: IrPluginContext, fqName: FqName): Collection<IrPropertySymbol> {
|
||||
private fun getPropertiesByFqName(pluginContext: IrPluginContext, fqName: FqName): Collection<IrPropertySymbol> {
|
||||
@OptIn(FirIncompatiblePluginAPI::class)
|
||||
return pluginContext.referenceProperties(fqName)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added `AllowContentAccessMethod` to represent the `setAllowContentAccess` method of the `android.webkit.WebSettings` class.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added more dataflow models for frequently-used JDK APIs.
|
||||
* Removed summary model for `java.lang.String#endsWith(String)` and added neutral model for this API.
|
||||
* Added additional taint step for `java.lang.String#endsWith(String)` to `ConditionalBypassFlowConfig`.
|
||||
@@ -63,6 +63,7 @@ extensions:
|
||||
- ["java.io", "File", True, "getAbsolutePath", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "getCanonicalFile", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "getCanonicalPath", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "getName", "()", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "toPath", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "toString", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "File", True, "toURI", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
@@ -74,6 +75,7 @@ extensions:
|
||||
- ["java.io", "InputStream", True, "readNBytes", "(int)", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "InputStream", True, "transferTo", "(OutputStream)", "", "Argument[-1]", "Argument[0]", "taint", "manual"]
|
||||
- ["java.io", "InputStreamReader", False, "InputStreamReader", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.io", "IOException", False, "IOException", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.io", "ObjectInput", True, "read", "", "", "Argument[-1]", "Argument[0]", "taint", "manual"]
|
||||
- ["java.io", "ObjectInputStream", False, "ObjectInputStream", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.io", "OutputStream", True, "write", "(byte[])", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
@@ -84,3 +86,9 @@ extensions:
|
||||
- ["java.io", "StringReader", False, "StringReader", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.io", "Writer", True, "toString", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.io", "Writer", True, "write", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.io", "File", "exists", "()", "manual"]
|
||||
|
||||
@@ -37,9 +37,10 @@ extensions:
|
||||
- ["java.lang", "CharSequence", True, "charAt", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "CharSequence", True, "subSequence", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "CharSequence", True, "toString", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "Exception", False, "Exception", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "IllegalArgumentException", False, "IllegalArgumentException", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "IllegalStateException", False, "IllegalStateException", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "Integer", False, "parseInt", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "IndexOutOfBoundsException", False, "IndexOutOfBoundsException", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "Iterable", True, "forEach", "(Consumer)", "", "Argument[-1].Element", "Argument[0].Parameter[0]", "value", "manual"]
|
||||
- ["java.lang", "Iterable", True, "iterator", "()", "", "Argument[-1].Element", "ReturnValue.Element", "value", "manual"]
|
||||
- ["java.lang", "Iterable", True, "spliterator", "()", "", "Argument[-1].Element", "ReturnValue.Element", "value", "manual"]
|
||||
@@ -47,12 +48,13 @@ extensions:
|
||||
- ["java.lang", "Object", True, "clone", "", "", "Argument[-1].MapKey", "ReturnValue.MapKey", "value", "manual"]
|
||||
- ["java.lang", "Object", True, "clone", "", "", "Argument[-1].MapValue", "ReturnValue.MapValue", "value", "manual"]
|
||||
- ["java.lang", "RuntimeException", False, "RuntimeException", "(String)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "RuntimeException", False, "RuntimeException", "(String,Throwable)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "value", "manual"]
|
||||
- ["java.lang", "RuntimeException", False, "RuntimeException", "(String,Throwable)", "", "Argument[1]", "Argument[-1].SyntheticField[java.lang.Throwable.cause]", "value", "manual"]
|
||||
- ["java.lang", "RuntimeException", False, "RuntimeException", "(Throwable)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.cause]", "value", "manual"]
|
||||
- ["java.lang", "String", False, "String", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "concat", "(String)", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "concat", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "copyValueOf", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "endsWith", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "format", "(Locale,String,Object[])", "", "Argument[1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "format", "(Locale,String,Object[])", "", "Argument[2].ArrayElement", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "format", "(String,Object[])", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
@@ -87,34 +89,56 @@ extensions:
|
||||
- ["java.lang", "String", False, "valueOf", "(char)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "valueOf", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "valueOf", "(char[],int,int)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "String", False, "valueOf", "(int)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.lang", "StringBuffer", True, "StringBuffer", "(CharSequence)", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.lang", "StringBuffer", True, "StringBuffer", "(String)", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.lang", "StringBuilder", True, "StringBuilder", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.lang", "System", False, "arraycopy", "", "", "Argument[0]", "Argument[2]", "taint", "manual"]
|
||||
- ["java.lang", "Throwable", False, "Throwable", "(Throwable)", "", "Argument[0]", "Argument[-1].SyntheticField[java.lang.Throwable.cause]", "value", "manual"]
|
||||
- ["java.lang", "Throwable", False, "getCause", "()", "", "Argument[-1].SyntheticField[java.lang.Throwable.cause]", "ReturnValue", "value", "manual"]
|
||||
- ["java.lang", "Throwable", False, "getMessage", "()", "", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "ReturnValue", "value", "manual"]
|
||||
- ["java.lang", "Throwable", True, "getCause", "()", "", "Argument[-1].SyntheticField[java.lang.Throwable.cause]", "ReturnValue", "value", "manual"]
|
||||
- ["java.lang", "Throwable", True, "getMessage", "()", "", "Argument[-1].SyntheticField[java.lang.Throwable.message]", "ReturnValue", "value", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.lang", "AbstractStringBuilder", "length", "()", "manual"]
|
||||
- ["java.lang", "Boolean", "equals", "(Object)", "manual"]
|
||||
- ["java.lang", "Class", "getClassLoader", "()", "manual"]
|
||||
- ["java.lang", "Class", "getName", "()", "manual"]
|
||||
- ["java.lang", "Class", "getSimpleName", "()", "manual"]
|
||||
- ["java.lang", "Class", "isAssignableFrom", "(Class)", "manual"]
|
||||
- ["java.lang", "Enum", "Enum", "(String,int)", "manual"]
|
||||
- ["java.lang", "Enum", "equals", "(Object)", "manual"]
|
||||
- ["java.lang", "Enum", "name", "()", "manual"]
|
||||
- ["java.lang", "Enum", "toString", "()", "manual"]
|
||||
- ["java.lang", "Long", "equals", "(Object)", "manual"]
|
||||
- ["java.lang", "Object", "equals", "(Object)", "manual"]
|
||||
- ["java.lang", "Object", "getClass", "()", "manual"]
|
||||
- ["java.lang", "Object", "hashCode", "()", "manual"]
|
||||
- ["java.lang", "Object", "toString", "()", "manual"]
|
||||
- ["java.lang", "String", "contains", "(CharSequence)", "manual"]
|
||||
- ["java.lang", "String", "endsWith", "(String)", "manual"]
|
||||
- ["java.lang", "String", "equals", "(Object)", "manual"]
|
||||
- ["java.lang", "String", "equalsIgnoreCase", "(String)", "manual"]
|
||||
- ["java.lang", "String", "hashCode", "()", "manual"]
|
||||
- ["java.lang", "String", "indexOf", "(String)", "manual"]
|
||||
- ["java.lang", "String", "isEmpty", "()", "manual"]
|
||||
- ["java.lang", "String", "length", "()", "manual"]
|
||||
- ["java.lang", "String", "startsWith", "(String)", "manual"]
|
||||
- ["java.lang", "System", "currentTimeMillis", "()", "manual"]
|
||||
- ["java.lang", "System", "nanoTime", "()", "manual"]
|
||||
- ["java.lang", "Thread", "currentThread", "()", "manual"]
|
||||
- ["java.lang", "Thread", "sleep", "(long)", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.lang", "Integer", "intValue", "()", "manual"] # taint-numeric
|
||||
- ["java.lang", "Integer", "parseInt", "(String)", "manual"] # taint-numeric
|
||||
- ["java.lang", "Integer", "toString", "(int)", "manual"] # taint-numeric
|
||||
- ["java.lang", "Integer", "valueOf", "(int)", "manual"] # taint-numeric
|
||||
- ["java.lang", "Long", "longValue", "()", "manual"] # taint-numeric
|
||||
- ["java.lang", "Long", "parseLong", "(String)", "manual"] # taint-numeric
|
||||
- ["java.lang", "Long", "toString", "()", "manual"] # taint-numeric
|
||||
- ["java.lang", "Math", "min", "(int,int)", "manual"] # value-numeric
|
||||
- ["java.lang", "String", "valueOf", "(int)", "manual"] # taint-numeric
|
||||
- ["java.lang", "String", "valueOf", "(long)", "manual"] # taint-numeric
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.math", "BigDecimal", False, "BigDecimal", "(String)", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.math", "BigDecimal", "compareTo", "(BigDecimal)", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.math", "BigDecimal", "BigDecimal", "(String)", "manual"] # taint-numeric
|
||||
- ["java.math", "BigDecimal", "valueOf", "(double)", "manual"] # taint-numeric
|
||||
- ["java.math", "BigDecimal", "valueOf", "(long)", "manual"] # taint-numeric
|
||||
|
||||
@@ -19,4 +19,16 @@ extensions:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["java.sql", "PreparedStatement", True, "setString", "(int,String)", "", "Argument[1]", "Argument[-1]", "value", "manual"]
|
||||
- ["java.sql", "ResultSet", True, "getString", "(String)", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.sql", "ResultSet", "next", "()", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.sql", "PreparedStatement", "setInt", "(int,int)", "manual"] # value-numeric
|
||||
- ["java.sql", "ResultSet", "getInt", "(String)", "manual"] # taint-numeric
|
||||
|
||||
9
java/ql/lib/ext/java.text.model.yml
Normal file
9
java/ql/lib/ext/java.text.model.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.text", "DateFormat", "format", "(Date)", "manual"] # taint-numeric
|
||||
- ["java.text", "SimpleDateFormat", "SimpleDateFormat", "(String)", "manual"] # taint-numeric
|
||||
11
java/ql/lib/ext/java.time.model.yml
Normal file
11
java/ql/lib/ext/java.time.model.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.time", "Instant", "now", "()", "manual"]
|
||||
- ["java.time", "ZonedDateTime", "now", "()", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.time", "LocalDate", "of", "(int,int,int)", "manual"] # taint-numeric
|
||||
16
java/ql/lib/ext/java.util.concurrent.atomic.model.yml
Normal file
16
java/ql/lib/ext/java.util.concurrent.atomic.model.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["java.util.concurrent.atomic", "AtomicReference", False, "AtomicReference", "(Object)", "", "Argument[0]", "Argument[-1].SyntheticField[java.util.concurrent.atomic.AtomicReference.value]", "value", "manual"]
|
||||
- ["java.util.concurrent.atomic", "AtomicReference", False, "get", "()", "", "Argument[-1].SyntheticField[java.util.concurrent.atomic.AtomicReference.value]", "ReturnValue", "value", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.util.concurrent.atomic", "AtomicInteger", "AtomicInteger", "(int)", "manual"] # value-numeric
|
||||
- ["java.util.concurrent.atomic", "AtomicInteger", "get", "()", "manual"] # value-numeric
|
||||
@@ -21,3 +21,14 @@ extensions:
|
||||
- ["java.util.concurrent", "TransferQueue", True, "transfer", "(Object)", "", "Argument[0]", "Argument[-1].Element", "value", "manual"]
|
||||
- ["java.util.concurrent", "TransferQueue", True, "tryTransfer", "(Object)", "", "Argument[0]", "Argument[-1].Element", "value", "manual"]
|
||||
- ["java.util.concurrent", "TransferQueue", True, "tryTransfer", "(Object,long,TimeUnit)", "", "Argument[0]", "Argument[-1].Element", "value", "manual"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.util.concurrent", "CountDownLatch", "countDown", "()", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.util.concurrent", "CountDownLatch", "CountDownLatch", "(int)", "manual"] # value-numeric
|
||||
- ["java.util.concurrent", "CountDownLatch", "getCount", "()", "manual"] # value-numeric
|
||||
|
||||
@@ -335,6 +335,8 @@ extensions:
|
||||
- ["java.util", "Stack", True, "peek", "()", "", "Argument[-1].Element", "ReturnValue", "value", "manual"]
|
||||
- ["java.util", "Stack", True, "pop", "()", "", "Argument[-1].Element", "ReturnValue", "value", "manual"]
|
||||
- ["java.util", "Stack", True, "push", "(Object)", "", "Argument[0]", "Argument[-1].Element", "value", "manual"]
|
||||
- ["java.util", "StringJoiner", False, "add", "(CharSequence)", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.util", "StringJoiner", False, "add", "(CharSequence)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.util", "StringTokenizer", False, "StringTokenizer", "", "", "Argument[0]", "Argument[-1]", "taint", "manual"]
|
||||
- ["java.util", "StringTokenizer", False, "nextElement", "()", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
- ["java.util", "StringTokenizer", False, "nextToken", "", "", "Argument[-1]", "ReturnValue", "taint", "manual"]
|
||||
@@ -360,8 +362,13 @@ extensions:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.util", "Collections", "emptyList", "()", "manual"]
|
||||
- ["java.util", "ArrayList", "ArrayList", "(int)", "manual"]
|
||||
- ["java.util", "ArrayList", "size", "()", "manual"]
|
||||
- ["java.util", "Collection", "isEmpty", "()", "manual"]
|
||||
- ["java.util", "Collection", "size", "()", "manual"]
|
||||
- ["java.util", "Collections", "emptyList", "()", "manual"]
|
||||
- ["java.util", "Collections", "emptyMap", "()", "manual"]
|
||||
- ["java.util", "Collections", "emptySet", "()", "manual"]
|
||||
- ["java.util", "Iterator", "hasNext", "()", "manual"]
|
||||
- ["java.util", "List", "contains", "(Object)", "manual"]
|
||||
- ["java.util", "List", "isEmpty", "()", "manual"]
|
||||
@@ -371,6 +378,7 @@ extensions:
|
||||
- ["java.util", "Map", "size", "()", "manual"]
|
||||
- ["java.util", "Objects", "equals", "(Object,Object)", "manual"]
|
||||
- ["java.util", "Objects", "hash", "(Object[])", "manual"]
|
||||
- ["java.util", "Objects", "nonNull", "(Object)", "manual"]
|
||||
- ["java.util", "Optional", "empty", "()", "manual"]
|
||||
- ["java.util", "Optional", "isPresent", "()", "manual"]
|
||||
- ["java.util", "Set", "contains", "(Object)", "manual"]
|
||||
@@ -378,3 +386,13 @@ extensions:
|
||||
- ["java.util", "Set", "size", "()", "manual"]
|
||||
- ["java.util", "UUID", "randomUUID", "()", "manual"]
|
||||
- ["java.util", "UUID", "toString", "()", "manual"]
|
||||
|
||||
# The below APIs are currently being stored as neutral models since `WithoutElement` has not yet been implemented for Java.
|
||||
# When `WithoutElement` is implemented, these should be changed to summary models of the form `Argument[-1].WithoutElement -> Argument[-1]`.
|
||||
- ["java.util", "List", "clear", "()", "manual"]
|
||||
- ["java.util", "Map", "clear", "()", "manual"]
|
||||
|
||||
# The below APIs have numeric flow and are currently being stored as neutral models.
|
||||
# These may be changed to summary models with kinds "value-numeric" and "taint-numeric" (or similar) in the future.
|
||||
- ["java.util", "Date", "Date", "(long)", "manual"] # taint-numeric
|
||||
- ["java.util", "Date", "getTime", "()", "manual"] # taint-numeric
|
||||
|
||||
@@ -93,3 +93,4 @@ extensions:
|
||||
extensible: neutralModel
|
||||
data:
|
||||
- ["java.util.stream", "Collectors", "toList", "()", "manual"]
|
||||
- ["java.util.stream", "Collectors", "toSet", "()", "manual"]
|
||||
|
||||
@@ -78,6 +78,14 @@ class WebViewSetWebViewClientMethod extends Method {
|
||||
}
|
||||
}
|
||||
|
||||
/** The method `setAllowContentAccess` of the class `android.webkit.WebSettings` */
|
||||
class AllowContentAccessMethod extends Method {
|
||||
AllowContentAccessMethod() {
|
||||
this.getDeclaringType() instanceof TypeWebSettings and
|
||||
this.hasName("setAllowContentAccess")
|
||||
}
|
||||
}
|
||||
|
||||
/** The method `shouldOverrideUrlLoading` of the class `android.webkit.WebViewClient`. */
|
||||
class ShouldOverrideUrlLoading extends Method {
|
||||
ShouldOverrideUrlLoading() {
|
||||
|
||||
@@ -23,6 +23,19 @@ predicate conditionControlsMethod(MethodAccess ma, Expr e) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node1` to `node2` is a dataflow step through the
|
||||
* `endsWith` method of the `java.lang.String` class.
|
||||
*/
|
||||
private predicate endsWithStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod().getDeclaringType() instanceof TypeString and
|
||||
ma.getMethod().getName() = "endsWith" and
|
||||
ma.getQualifier() = node1.asExpr() and
|
||||
ma = node2.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint tracking configuration for untrusted data flowing to sensitive conditions.
|
||||
*/
|
||||
@@ -32,4 +45,8 @@ class ConditionalBypassFlowConfig extends TaintTracking::Configuration {
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { conditionControlsMethod(_, sink.asExpr()) }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
endsWithStep(node1, node2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
public class PartialPathTraversalGood {
|
||||
public void example(File dir, File parent) throws IOException {
|
||||
if (!dir.getCanonicalPath().toPath().startsWith(parent.getCanonicalPath().toPath())) {
|
||||
if (!dir.getCanonicalPath().startsWith(parent.getCanonicalPath() + File.separator)) {
|
||||
throw new IOException("Invalid directory: " + dir.getCanonicalPath());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ and not just children of <code>parent</code>, which is a security issue.
|
||||
<p>
|
||||
|
||||
In this example, the <code>if</code> statement checks if <code>parent.getCanonicalPath() + File.separator </code>
|
||||
is a prefix of <code>dir.getCanonicalPath()</code>. Because <code>parent.getCanonicalPath().toPath()</code> is
|
||||
is a prefix of <code>dir.getCanonicalPath()</code>. Because <code>parent.getCanonicalPath() + File.separator</code> is
|
||||
indeed slash-terminated, the user supplying <code>dir</code> can only access children of
|
||||
<code>parent</code>, as desired.
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Android can provide access to content providers within a WebView using
|
||||
the <code>setAllowContentAccess</code> setting.</p>
|
||||
|
||||
<p>Allowing access to content providers via <code>content://</code> URLs
|
||||
may allow JavaScript to access protected content.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
If your app does not require access to the <code>content://</code> URL
|
||||
functionality, you should explicitly disable the setting by
|
||||
calling <code>setAllowContentAccess(false)</code> on the settings of the
|
||||
WebView.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following (bad) example, access to <code>content://</code> URLs is explicitly allowed.</p>
|
||||
|
||||
<sample src="ContentAccessEnabled.java"/>
|
||||
|
||||
<p>In the following (good) example, access to <code>content://</code> URLs is explicitly denied.</p>
|
||||
|
||||
<sample src="ContentAccessDisabled.java"/>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Android Documentation: <a href="https://developer.android.com/reference/android/webkit/WebSettings#setAllowContentAccess(boolean)">setAllowContentAccess</a>.
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* @name Android WebView settings allows access to content links
|
||||
* @id java/android/websettings-allow-content-access
|
||||
* @description Access to content providers in a WebView can allow access to protected information by loading content:// links.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @security-severity 6.5
|
||||
* @tags security
|
||||
* external/cwe/cwe-200
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.frameworks.android.WebView
|
||||
|
||||
/** Represents `android.webkit.WebView` and its subclasses. */
|
||||
private class TypeWebViewOrSubclass extends RefType {
|
||||
TypeWebViewOrSubclass() { this.getASupertype*() instanceof TypeWebView }
|
||||
}
|
||||
|
||||
/**
|
||||
* A method access to a getter method which is private.
|
||||
*
|
||||
* In Kotlin, member accesses are translated to getter methods.
|
||||
*/
|
||||
private class PrivateGetterMethodAccess extends MethodAccess {
|
||||
PrivateGetterMethodAccess() {
|
||||
this.getMethod() instanceof GetterMethod and
|
||||
this.getMethod().isPrivate()
|
||||
}
|
||||
}
|
||||
|
||||
/** A source for `android.webkit.WebView` objects. */
|
||||
class WebViewSource extends DataFlow::Node {
|
||||
WebViewSource() {
|
||||
this.getType() instanceof TypeWebViewOrSubclass and
|
||||
// To reduce duplicate results, we only consider WebView objects from
|
||||
// constructor and method calls, or method accesses which are cast to WebView.
|
||||
(
|
||||
this.asExpr() instanceof ClassInstanceExpr or
|
||||
this.asExpr() instanceof MethodAccess or
|
||||
this.asExpr().(CastExpr).getAChildExpr() instanceof MethodAccess
|
||||
) and
|
||||
// Avoid duplicate results from Kotlin member accesses.
|
||||
not this.asExpr() instanceof PrivateGetterMethodAccess
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink representing a call to `android.webkit.WebSettings.setAllowContentAccess` that
|
||||
* disables content access.
|
||||
*/
|
||||
class WebSettingsDisallowContentAccessSink extends DataFlow::Node {
|
||||
WebSettingsDisallowContentAccessSink() {
|
||||
exists(MethodAccess ma |
|
||||
ma.getQualifier() = this.asExpr() and
|
||||
ma.getMethod() instanceof AllowContentAccessMethod and
|
||||
ma.getArgument(0).(CompileTimeConstantExpr).getBooleanValue() = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class WebViewDisallowContentAccessConfiguration extends TaintTracking::Configuration {
|
||||
WebViewDisallowContentAccessConfiguration() { this = "WebViewDisallowContentAccessConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) { node instanceof WebViewSource }
|
||||
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` is a dataflow step that gets the `WebSettings` object
|
||||
* from the `getSettings` method of a `WebView` object.
|
||||
*
|
||||
* This step is only valid when `state1` is empty and `state2` indicates that the `WebSettings` object
|
||||
* has been accessed.
|
||||
*/
|
||||
override predicate isAdditionalTaintStep(
|
||||
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
|
||||
DataFlow::FlowState state2
|
||||
) {
|
||||
state1 instanceof DataFlow::FlowStateEmpty and
|
||||
state2 = "WebSettings" and
|
||||
// settings = webView.getSettings()
|
||||
// ^node2 = ^node1
|
||||
exists(MethodAccess ma |
|
||||
ma = node2.asExpr() and
|
||||
ma.getQualifier() = node1.asExpr() and
|
||||
ma.getMethod() instanceof WebViewGetSettingsMethod
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node, DataFlow::FlowState state) {
|
||||
state = "WebSettings" and
|
||||
node instanceof WebSettingsDisallowContentAccessSink
|
||||
}
|
||||
}
|
||||
|
||||
from Expr e
|
||||
where
|
||||
// explicit: setAllowContentAccess(true)
|
||||
exists(MethodAccess ma |
|
||||
ma = e and
|
||||
ma.getMethod() instanceof AllowContentAccessMethod and
|
||||
ma.getArgument(0).(CompileTimeConstantExpr).getBooleanValue() = true
|
||||
)
|
||||
or
|
||||
// implicit: no setAllowContentAccess(false)
|
||||
exists(WebViewSource source |
|
||||
source.asExpr() = e and
|
||||
not any(WebViewDisallowContentAccessConfiguration cfg).hasFlow(source, _)
|
||||
)
|
||||
select e,
|
||||
"Sensitive information may be exposed via a malicious link due to access to content:// links being allowed in this WebView."
|
||||
@@ -0,0 +1,3 @@
|
||||
WebSettings settings = webview.getSettings();
|
||||
|
||||
settings.setAllowContentAccess(false);
|
||||
@@ -0,0 +1,3 @@
|
||||
WebSettings settings = webview.getSettings();
|
||||
|
||||
settings.setAllowContentAccess(true);
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* Added a new query `java/android/websettings-allow-content-access` to detect Android WebViews which do not disable access to `content://` urls.
|
||||
@@ -12,14 +12,8 @@ edges
|
||||
| ThreadResourceAbuse.java:71:15:71:17 | parameter this [waitTime] : Number | ThreadResourceAbuse.java:74:18:74:25 | this <.field> [waitTime] : Number |
|
||||
| ThreadResourceAbuse.java:74:18:74:25 | this <.field> [waitTime] : Number | ThreadResourceAbuse.java:74:18:74:25 | waitTime |
|
||||
| ThreadResourceAbuse.java:141:27:141:43 | getValue(...) : String | ThreadResourceAbuse.java:144:34:144:42 | delayTime |
|
||||
| ThreadResourceAbuse.java:172:19:172:50 | getHeader(...) : String | ThreadResourceAbuse.java:173:37:173:42 | header : String |
|
||||
| ThreadResourceAbuse.java:172:19:172:50 | getHeader(...) : String | ThreadResourceAbuse.java:176:17:176:26 | retryAfter |
|
||||
| ThreadResourceAbuse.java:173:20:173:43 | parseInt(...) : Number | ThreadResourceAbuse.java:176:17:176:26 | retryAfter |
|
||||
| ThreadResourceAbuse.java:173:37:173:42 | header : String | ThreadResourceAbuse.java:173:20:173:43 | parseInt(...) : Number |
|
||||
| ThreadResourceAbuse.java:206:28:206:56 | getParameter(...) : String | ThreadResourceAbuse.java:207:39:207:52 | uploadDelayStr : String |
|
||||
| ThreadResourceAbuse.java:206:28:206:56 | getParameter(...) : String | ThreadResourceAbuse.java:209:49:209:59 | uploadDelay : Number |
|
||||
| ThreadResourceAbuse.java:207:22:207:53 | parseInt(...) : Number | ThreadResourceAbuse.java:209:49:209:59 | uploadDelay : Number |
|
||||
| ThreadResourceAbuse.java:207:39:207:52 | uploadDelayStr : String | ThreadResourceAbuse.java:207:22:207:53 | parseInt(...) : Number |
|
||||
| ThreadResourceAbuse.java:209:30:209:87 | new UploadListener(...) [slowUploads] : Number | UploadListener.java:28:14:28:19 | parameter this [slowUploads] : Number |
|
||||
| ThreadResourceAbuse.java:209:49:209:59 | uploadDelay : Number | ThreadResourceAbuse.java:209:30:209:87 | new UploadListener(...) [slowUploads] : Number |
|
||||
| ThreadResourceAbuse.java:209:49:209:59 | uploadDelay : Number | UploadListener.java:15:24:15:44 | sleepMilliseconds : Number |
|
||||
@@ -48,12 +42,8 @@ nodes
|
||||
| ThreadResourceAbuse.java:141:27:141:43 | getValue(...) : String | semmle.label | getValue(...) : String |
|
||||
| ThreadResourceAbuse.java:144:34:144:42 | delayTime | semmle.label | delayTime |
|
||||
| ThreadResourceAbuse.java:172:19:172:50 | getHeader(...) : String | semmle.label | getHeader(...) : String |
|
||||
| ThreadResourceAbuse.java:173:20:173:43 | parseInt(...) : Number | semmle.label | parseInt(...) : Number |
|
||||
| ThreadResourceAbuse.java:173:37:173:42 | header : String | semmle.label | header : String |
|
||||
| ThreadResourceAbuse.java:176:17:176:26 | retryAfter | semmle.label | retryAfter |
|
||||
| ThreadResourceAbuse.java:206:28:206:56 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| ThreadResourceAbuse.java:207:22:207:53 | parseInt(...) : Number | semmle.label | parseInt(...) : Number |
|
||||
| ThreadResourceAbuse.java:207:39:207:52 | uploadDelayStr : String | semmle.label | uploadDelayStr : String |
|
||||
| ThreadResourceAbuse.java:209:30:209:87 | new UploadListener(...) [slowUploads] : Number | semmle.label | new UploadListener(...) [slowUploads] : Number |
|
||||
| ThreadResourceAbuse.java:209:49:209:59 | uploadDelay : Number | semmle.label | uploadDelay : Number |
|
||||
| UploadListener.java:15:24:15:44 | sleepMilliseconds : Number | semmle.label | sleepMilliseconds : Number |
|
||||
|
||||
@@ -3,12 +3,8 @@ edges
|
||||
| NFEAndroidDoS.java:13:24:13:61 | getStringExtra(...) : Object | NFEAndroidDoS.java:14:21:14:51 | parseDouble(...) |
|
||||
| NFEAndroidDoS.java:22:21:22:31 | getIntent(...) : Intent | NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object | NFEAndroidDoS.java:23:15:23:40 | parseInt(...) |
|
||||
| NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object | NFEAndroidDoS.java:23:32:23:39 | widthStr : Object |
|
||||
| NFEAndroidDoS.java:23:32:23:39 | widthStr : Object | NFEAndroidDoS.java:23:15:23:40 | parseInt(...) |
|
||||
| NFEAndroidDoS.java:25:22:25:32 | getIntent(...) : Intent | NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object | NFEAndroidDoS.java:26:16:26:42 | parseInt(...) |
|
||||
| NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object | NFEAndroidDoS.java:26:33:26:41 | heightStr : Object |
|
||||
| NFEAndroidDoS.java:26:33:26:41 | heightStr : Object | NFEAndroidDoS.java:26:16:26:42 | parseInt(...) |
|
||||
| NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object | NFEAndroidDoS.java:44:21:44:43 | new Double(...) |
|
||||
| NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object | NFEAndroidDoS.java:47:21:47:47 | valueOf(...) |
|
||||
@@ -19,11 +15,9 @@ nodes
|
||||
| NFEAndroidDoS.java:22:21:22:31 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent |
|
||||
| NFEAndroidDoS.java:22:21:22:55 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:23:15:23:40 | parseInt(...) | semmle.label | parseInt(...) |
|
||||
| NFEAndroidDoS.java:23:32:23:39 | widthStr : Object | semmle.label | widthStr : Object |
|
||||
| NFEAndroidDoS.java:25:22:25:32 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent |
|
||||
| NFEAndroidDoS.java:25:22:25:57 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:26:16:26:42 | parseInt(...) | semmle.label | parseInt(...) |
|
||||
| NFEAndroidDoS.java:26:33:26:41 | heightStr : Object | semmle.label | heightStr : Object |
|
||||
| NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | semmle.label | getIntent(...) : Intent |
|
||||
| NFEAndroidDoS.java:43:24:43:61 | getStringExtra(...) : Object | semmle.label | getStringExtra(...) : Object |
|
||||
| NFEAndroidDoS.java:44:21:44:43 | new Double(...) | semmle.label | new Double(...) |
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Test {
|
||||
|
||||
@@ -9,37 +19,67 @@ public class Test {
|
||||
|
||||
public void test() throws Exception {
|
||||
|
||||
Exception e1 = new RuntimeException((String)source());
|
||||
sink((String)e1.getMessage()); // $hasValueFlow
|
||||
// top 100 JDK APIs tests
|
||||
{
|
||||
Exception e1 = new RuntimeException((String)source());
|
||||
sink((String)e1.getMessage()); // $hasValueFlow
|
||||
|
||||
Exception e2 = new RuntimeException((Throwable)source());
|
||||
sink((Throwable)e2.getCause()); // $hasValueFlow
|
||||
Exception e2 = new RuntimeException((Throwable)source());
|
||||
sink((Throwable)e2.getCause()); // $hasValueFlow
|
||||
|
||||
Exception e3 = new IllegalArgumentException((String)source());
|
||||
sink((String)e3.getMessage()); // $hasValueFlow
|
||||
Exception e3 = new IllegalArgumentException((String)source());
|
||||
sink((String)e3.getMessage()); // $hasValueFlow
|
||||
|
||||
Exception e4 = new IllegalStateException((String)source());
|
||||
sink((String)e4.getMessage()); // $hasValueFlow
|
||||
Exception e4 = new IllegalStateException((String)source());
|
||||
sink((String)e4.getMessage()); // $hasValueFlow
|
||||
|
||||
Throwable t = new Throwable((Throwable)source());
|
||||
sink((Throwable)t.getCause()); // $hasValueFlow
|
||||
Throwable t = new Throwable((Throwable)source());
|
||||
sink((Throwable)t.getCause()); // $hasValueFlow
|
||||
|
||||
Integer x = (Integer)source();
|
||||
int y = x;
|
||||
sink(String.valueOf(y)); // $hasTaintFlow
|
||||
String s2 = (String)source();
|
||||
int i = 0;
|
||||
sink(s2.charAt(i)); // $hasTaintFlow
|
||||
|
||||
String s1 = (String)source();
|
||||
sink(Integer.parseInt(s1)); // $hasTaintFlow
|
||||
ResultSet rs = (ResultSet)source();
|
||||
sink(rs.getString("")); // $hasTaintFlow
|
||||
}
|
||||
|
||||
String s2 = (String)source();
|
||||
int i = 0;
|
||||
sink(s2.charAt(i)); // $hasTaintFlow
|
||||
// top 200 JDK APIs tests
|
||||
{
|
||||
// java.io
|
||||
Exception e1 = new IOException((String)source());
|
||||
sink((String)e1.getMessage()); // $hasValueFlow
|
||||
|
||||
String s3 = (String)source();
|
||||
sink(new BigDecimal(s3)); // $hasTaintFlow
|
||||
File f = (File)source();
|
||||
sink(f.getName()); // $hasTaintFlow
|
||||
|
||||
ResultSet rs = (ResultSet)source();
|
||||
sink(rs.getString("")); // $hasTaintFlow
|
||||
// java.lang
|
||||
Exception e2 = new Exception((String)source());
|
||||
sink((String)e2.getMessage()); // $hasValueFlow
|
||||
|
||||
Exception e3 = new IndexOutOfBoundsException((String)source());
|
||||
sink((String)e3.getMessage()); // $hasValueFlow
|
||||
|
||||
Exception e4 = new RuntimeException((String)source(), (Throwable)source());
|
||||
sink((String)e4.getMessage()); // $hasValueFlow
|
||||
sink((Throwable)e4.getCause()); // $hasValueFlow
|
||||
|
||||
// java.sql
|
||||
Connection con = DriverManager.getConnection("");
|
||||
PreparedStatement ps1 = con.prepareStatement("UPDATE EMPLOYEES SET NAME = ? WHERE ID = ?");
|
||||
ps1.setString(1, (String)source());
|
||||
sink(ps1); // $hasValueFlow
|
||||
|
||||
// java.util.concurrent.atomic
|
||||
AtomicReference ar = new AtomicReference(source());
|
||||
sink(ar.get()); // $hasValueFlow
|
||||
|
||||
// java.util
|
||||
StringJoiner sj1 = new StringJoiner(",");
|
||||
sink(sj1.add((CharSequence)source())); // $hasTaintFlow
|
||||
|
||||
StringJoiner sj2 = (StringJoiner)source();
|
||||
sink(sj2.add("test")); // $hasTaintFlow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,61 @@ predicate topJdkApiName(string apiName) {
|
||||
"java.nio.file.Path#resolve(String)", "java.lang.Enum#toString()",
|
||||
"java.lang.RuntimeException#RuntimeException(Throwable)", "java.util.Collection#size()",
|
||||
"java.lang.String#charAt(int)", "java.util.stream.Stream#forEach(Consumer)",
|
||||
"java.util.Map#isEmpty()", "java.lang.String#valueOf(int)"
|
||||
"java.util.Map#isEmpty()", "java.lang.String#valueOf(int)",
|
||||
// top 200 JDK APIs
|
||||
"java.lang.Integer#intValue()", "java.util.ArrayList#size()",
|
||||
"java.util.ArrayList#ArrayList(int)", "java.util.function.Function#apply(Object)",
|
||||
"java.util.stream.Stream#forEach(Consumer)", "java.util.ArrayList#get(int)",
|
||||
"java.util.Set#iterator()", "java.util.stream.Collectors#toSet()",
|
||||
"java.lang.String#replaceAll(String,String)", "java.lang.String#getBytes(Charset)",
|
||||
"java.util.Objects#requireNonNull(Object)", "java.util.Objects#nonNull(Object)",
|
||||
"java.lang.String#endsWith(String)", "java.lang.AbstractStringBuilder#length()",
|
||||
"java.sql.PreparedStatement#setString(int,String)",
|
||||
"java.util.regex.Pattern#matcher(CharSequence)", "java.nio.file.Path#toString()",
|
||||
"java.time.Instant#now()", "java.io.File#getAbsolutePath()",
|
||||
"java.util.Set#addAll(Collection)", "java.lang.Integer#valueOf(int)",
|
||||
"java.util.HashSet#HashSet(Collection)", "java.lang.Integer#toString(int)",
|
||||
"java.lang.StringBuilder#StringBuilder(String)", "java.lang.Thread#sleep(long)",
|
||||
"java.lang.Thread#currentThread()", "java.util.Date#getTime()",
|
||||
"java.io.Writer#write(String)", "java.lang.String#getBytes()", "java.io.File#exists()",
|
||||
"java.lang.String#toUpperCase()", "java.lang.Long#parseLong(String)",
|
||||
"java.util.Collections#emptyMap()", "java.util.Optional#orElseThrow(Supplier)",
|
||||
"java.util.List#of(Object,Object)", "java.util.concurrent.CountDownLatch#countDown()",
|
||||
"java.lang.Class#isAssignableFrom(Class)",
|
||||
"java.lang.IndexOutOfBoundsException#IndexOutOfBoundsException(String)",
|
||||
"java.lang.Throwable#getCause()", "java.util.Arrays#stream(Object[])",
|
||||
"java.util.function.Supplier#get()", "java.lang.Exception#Exception(String)",
|
||||
"java.util.function.Consumer#accept(Object)", "java.util.stream.Stream#anyMatch(Predicate)",
|
||||
"java.util.List#clear()", "java.io.File#File(File,String)",
|
||||
"java.lang.String#indexOf(String)", "java.util.List#iterator()",
|
||||
"java.util.concurrent.CountDownLatch#CountDownLatch(int)", "java.sql.ResultSet#next()",
|
||||
"java.sql.PreparedStatement#setInt(int,int)",
|
||||
"java.util.concurrent.atomic.AtomicInteger#get()",
|
||||
"java.util.stream.Collectors#toMap(Function,Function)", "java.lang.Math#min(int,int)",
|
||||
"java.lang.Long#equals(Object)", "java.util.Properties#setProperty(String,String)",
|
||||
"java.util.Map#getOrDefault(Object,Object)", "java.lang.System#getProperty(String)",
|
||||
"java.util.stream.Stream#of(Object[])", "java.nio.file.Paths#get(String,String[])",
|
||||
"java.math.BigDecimal#compareTo(BigDecimal)", "java.math.BigDecimal#valueOf(long)",
|
||||
"java.lang.RuntimeException#RuntimeException(String,Throwable)",
|
||||
"java.util.Collection#add(Object)", "java.util.Collections#emptySet()",
|
||||
"java.util.stream.Stream#flatMap(Function)",
|
||||
"java.util.concurrent.atomic.AtomicReference#get()", "java.util.Collection#isEmpty()",
|
||||
"java.lang.StringBuffer#toString()", "java.util.Collections#singleton(Object)",
|
||||
"java.io.File#getName()", "java.time.ZonedDateTime#now()",
|
||||
"java.io.ByteArrayInputStream#ByteArrayInputStream(byte[])", "java.nio.file.Path#toFile()",
|
||||
"java.util.Date#Date(long)", "java.lang.System#nanoTime()",
|
||||
"java.util.Hashtable#put(Object,Object)", "java.util.Map#putAll(Map)",
|
||||
"java.lang.Long#toString()", "java.util.List#toArray(Object[])", "java.io.File#toPath()",
|
||||
"java.util.regex.Matcher#group(int)", "java.time.LocalDate#of(int,int,int)",
|
||||
"java.lang.String#valueOf(long)", "java.math.BigDecimal#valueOf(double)",
|
||||
"java.io.IOException#IOException(String)", "java.text.DateFormat#format(Date)",
|
||||
"java.sql.ResultSet#getInt(String)", "java.util.Map#clear()", "java.util.HashSet#add(Object)",
|
||||
"java.lang.Class#getClassLoader()", "java.lang.Boolean#equals(Object)",
|
||||
"java.lang.String#concat(String)", "java.util.Collections#singletonMap(Object,Object)",
|
||||
"java.util.Collection#iterator()", "java.util.Map#computeIfAbsent(Object,Function)",
|
||||
"java.text.SimpleDateFormat#SimpleDateFormat(String)",
|
||||
"java.util.StringJoiner#add(CharSequence)", "java.lang.Long#longValue()",
|
||||
"java.util.stream.Collectors#joining(CharSequence)"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -88,10 +142,16 @@ class TopJdkApi extends SummarizedCallableBase {
|
||||
/** Holds if this API has a manual MaD model. */
|
||||
predicate hasManualMadModel() { this.hasManualSummary() or this.hasManualNeutral() }
|
||||
/*
|
||||
* Note: the following top-100 APIs are not modeled with MaD:
|
||||
* java.util.stream.Stream#collect(Collector) : handled separately on a case-by-case basis as it is too complex for MaD
|
||||
* java.lang.String#valueOf(Object) : also a complex case; an alias for `Object.toString`, except the dispatch is hidden
|
||||
* java.lang.Throwable#printStackTrace() : should probably not be a general step, but there might be specialised queries that care
|
||||
* Note: the following top JDK APIs are not modeled with MaD:
|
||||
* `java.lang.String#valueOf(Object)`: a complex case; an alias for `Object.toString`, except the dispatch is hidden
|
||||
* `java.lang.System#getProperty(String)`: needs to be modeled by regular CodeQL matching the get and set keys to reduce FPs
|
||||
* `java.lang.Throwable#printStackTrace()`: should probably not be a general step, but there might be specialised queries that care
|
||||
* `java.util.function.Consumer#accept(Object)`: specialized lambda flow
|
||||
* `java.util.function.Function#apply(Object)`: specialized lambda flow
|
||||
* `java.util.function.Supplier#get()`: lambda flow
|
||||
* `java.util.stream.Collectors#joining(CharSequence)`: cannot be modeled completely without a model for `java.util.stream.Stream#collect(Collector)` as well
|
||||
* `java.util.stream.Collectors#toMap(Function,Function)`: specialized collectors flow
|
||||
* `java.util.stream.Stream#collect(Collector)`: handled separately on a case-by-case basis as it is too complex for MaD
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
| java.lang.String#valueOf(Object) | no manual model |
|
||||
| java.lang.System#getProperty(String) | no manual model |
|
||||
| java.lang.Throwable#printStackTrace() | no manual model |
|
||||
| java.util.function.Consumer#accept(Object) | no manual model |
|
||||
| java.util.function.Function#apply(Object) | no manual model |
|
||||
| java.util.function.Supplier#get() | no manual model |
|
||||
| java.util.stream.Collectors#joining(CharSequence) | no manual model |
|
||||
| java.util.stream.Collectors#toMap(Function,Function) | no manual model |
|
||||
| java.util.stream.Stream#collect(Collector) | no manual model |
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.lang.IllegalStateException;
|
||||
import java.lang.IndexOutOfBoundsException;
|
||||
import java.lang.Math;
|
||||
import java.lang.System;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Paths;
|
||||
import java.sql.ResultSet;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.lang.System;
|
||||
import java.lang.IllegalStateException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class TopJdkApisTest { }
|
||||
|
||||
@@ -58,19 +58,19 @@ public class B {
|
||||
// non-whitelisted constructors don't pass taint
|
||||
StringWrapper herring = new StringWrapper(complex);
|
||||
sink(herring);
|
||||
// toString does not pass taint yet
|
||||
// toString does not pass taint yet
|
||||
String valueOfObject = String.valueOf(args);
|
||||
sink(valueOfObject);
|
||||
|
||||
|
||||
|
||||
// tainted equality check with constant
|
||||
boolean cond = "foo" == s;
|
||||
sink(cond);
|
||||
// tainted logic with tainted operand
|
||||
boolean logic = cond && safe();
|
||||
sink(logic);
|
||||
// tainted condition
|
||||
sink(concat.endsWith("I'm tainted"));
|
||||
|
||||
|
||||
// tainted
|
||||
logic = safe() || cond;
|
||||
sink(logic);
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:51:10:51:21 | fluentConcat |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:68:10:68:13 | cond |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:71:10:71:14 | logic |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:73:10:73:39 | endsWith(...) |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:76:10:76:14 | logic |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:79:10:79:14 | logic |
|
||||
| B.java:15:21:15:27 | taint(...) | B.java:87:10:87:16 | trimmed |
|
||||
|
||||
@@ -18,7 +18,7 @@ class ExternalApiUsage {
|
||||
AtomicReference<String> ref = new AtomicReference<>(); // not supported
|
||||
ref.set("foo");
|
||||
|
||||
String.class.isAssignableFrom(Object.class); // parameter with generic type
|
||||
String.class.isAssignableFrom(Object.class); // parameter with generic type, supported as a neutral model
|
||||
|
||||
System.out.println(d);
|
||||
System.out.println(map);
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
| java.lang.Class#isAssignableFrom(Class) | 1 |
|
||||
| java.time.Duration#ofMillis(long) | 1 |
|
||||
| java.util.concurrent.atomic.AtomicReference#set(Object) | 1 |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user