Merge branch 'main' into call-graph-code

This commit is contained in:
Rasmus Wriedt Larsen
2023-01-20 15:11:49 +01:00
244 changed files with 37914 additions and 2761 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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(), "|") + ")\"")
)
}

View File

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

View File

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

View 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];
}
}

View File

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

View File

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

View File

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

View File

@@ -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(), "")
}

View File

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

View File

@@ -1,7 +1,7 @@
struct Foo {
~Foo();
struct ArrayDelete {
~ArrayDelete();
};
void f() {
delete[] (Foo*)nullptr;
delete[] (ArrayDelete*)nullptr;
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
description: Remove operators from the virtualizable type.
compatibility: full

View File

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

View File

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

View File

@@ -0,0 +1,5 @@
---
category: minorAnalysis
---
* Support for `static virtual` and `static abstract` interface members.
* Support for *operators* in interface definitions.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Attributes on methods in CIL are now extracted (Bugfix).

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
description: Add operators to the virtualizable type.
compatibility: full

File diff suppressed because it is too large Load Diff

View File

@@ -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>"
] + "%")
}

View File

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

View 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; }
}

View File

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

View 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() { }
}
}

Binary file not shown.

View File

@@ -0,0 +1,7 @@
using System;
using Assembly;
public class Class1
{
public static void Main(string[] args) { }
}

View File

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

View File

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

View File

@@ -0,0 +1 @@
semmle-extractor-options: --cil

View File

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

View 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()

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {
/**

View File

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

View File

@@ -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"] }
}
/**

View File

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

View File

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

View File

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

View File

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

View File

@@ -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
1 package sink source summary sink:bean-validation sink:create-file sink:fragment-injection sink:groovy sink:header-splitting sink:information-leak sink:intent-start sink:jdbc-url sink:jexl sink:jndi-injection sink:ldap sink:logging sink:mvel sink:ognl-injection sink:open-url sink:pending-intent-sent sink:regex-use sink:regex-use[-1] sink:regex-use[0] sink:regex-use[] sink:regex-use[f-1] sink:regex-use[f1] sink:regex-use[f] sink:set-hostname-verifier sink:sql sink:ssti sink:url-open-stream sink:url-redirect sink:write-file sink:xpath sink:xslt sink:xss source:android-external-storage-dir source:android-widget source:contentprovider source:remote summary:taint summary:value
38 jakarta.ws.rs.container 9 9
39 jakarta.ws.rs.core 2 149 2 94 55
40 java.beans 1 1
41 java.io 37 40 42 15 22 40 41 1
42 java.lang 13 75 76 8 4 1 56 53 19 23
java.math 1 1
43 java.net 10 3 7 10 3 7
44 java.nio 15 16 13 2 16
45 java.sql 11 1 2 4 7 1 1
46 java.util 44 461 465 34 5 2 1 2 36 38 425 427
47 javax.faces.context 2 7 2 7
48 javax.jms 9 57 9 57
49 javax.json 123 100 23

View File

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

View File

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

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added `AllowContentAccessMethod` to represent the `setAllowContentAccess` method of the `android.webkit.WebSettings` class.

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View 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

View File

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

View File

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

View File

@@ -93,3 +93,4 @@ extensions:
extensible: neutralModel
data:
- ["java.util.stream", "Collectors", "toList", "()", "manual"]
- ["java.util.stream", "Collectors", "toSet", "()", "manual"]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
WebSettings settings = webview.getSettings();
settings.setAllowContentAccess(false);

View File

@@ -0,0 +1,3 @@
WebSettings settings = webview.getSettings();
settings.setAllowContentAccess(true);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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