mirror of
https://github.com/github/codeql.git
synced 2026-04-29 10:45:15 +02:00
Merge branch 'main' into unsafeHtmlConstruction
This commit is contained in:
2
.github/actions/fetch-codeql/action.yml
vendored
2
.github/actions/fetch-codeql/action.yml
vendored
@@ -19,4 +19,6 @@ runs:
|
||||
gh extension install github/gh-codeql
|
||||
gh codeql set-channel "$CHANNEL"
|
||||
gh codeql version
|
||||
printf "CODEQL_FETCHED_CODEQL_PATH=" >> "${GITHUB_ENV}"
|
||||
gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_ENV}"
|
||||
gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}"
|
||||
|
||||
@@ -10,6 +10,8 @@ There is [extensive documentation](https://codeql.github.com/docs/) on getting s
|
||||
|
||||
We welcome contributions to our standard library and standard checks. Do you have an idea for a new check, or how to improve an existing query? Then please go ahead and open a pull request! Before you do, though, please take the time to read our [contributing guidelines](CONTRIBUTING.md). You can also consult our [style guides](https://github.com/github/codeql/tree/main/docs) to learn how to format your code for consistency and clarity, how to write query metadata, and how to write query help documentation for your query.
|
||||
|
||||
For information on contributing to CodeQL documentation, see the "[contributing guide](docs/codeql/CONTRIBUTING.md)" for docs.
|
||||
|
||||
## License
|
||||
|
||||
The code in this repository is licensed under the [MIT License](LICENSE) by [GitHub](https://github.com).
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.5.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
3
cpp/ql/lib/change-notes/released/0.5.1.md
Normal file
3
cpp/ql/lib/change-notes/released/0.5.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.0
|
||||
lastReleaseVersion: 0.5.1
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
private import RangeAnalysisStage
|
||||
|
||||
module FloatDelta implements DeltaSig {
|
||||
class Delta = float;
|
||||
|
||||
bindingset[d]
|
||||
bindingset[result]
|
||||
float toFloat(Delta d) { result = d }
|
||||
|
||||
bindingset[d]
|
||||
bindingset[result]
|
||||
int toInt(Delta d) { result = d }
|
||||
|
||||
bindingset[n]
|
||||
bindingset[result]
|
||||
Delta fromInt(int n) { result = n }
|
||||
|
||||
bindingset[f]
|
||||
Delta fromFloat(float f) {
|
||||
result =
|
||||
min(float diff, float res |
|
||||
diff = (res - f) and res = f.ceil()
|
||||
or
|
||||
diff = (f - res) and res = f.floor()
|
||||
|
|
||||
res order by diff
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -14,321 +14,328 @@ private import ModulusAnalysisSpecific::Private
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import RangeUtils
|
||||
private import RangeAnalysisStage
|
||||
|
||||
/**
|
||||
* Holds if `e + delta` equals `v` at `pos`.
|
||||
*/
|
||||
private predicate valueFlowStepSsa(SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta) {
|
||||
semSsaUpdateStep(v, e, delta) and pos.hasReadOfVar(v)
|
||||
or
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = semEqFlowCond(v, e, delta, true, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `add` is the addition of `larg` and `rarg`, neither of which are
|
||||
* `ConstantIntegerExpr`s.
|
||||
*/
|
||||
private predicate nonConstAddition(SemExpr add, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemAddExpr a | a = add |
|
||||
larg = a.getLeftOperand() and
|
||||
rarg = a.getRightOperand()
|
||||
) and
|
||||
not larg instanceof SemConstantIntegerExpr and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not
|
||||
* a `ConstantIntegerExpr`.
|
||||
*/
|
||||
private predicate nonConstSubtraction(SemExpr sub, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemSubExpr s | s = sub |
|
||||
larg = s.getLeftOperand() and
|
||||
rarg = s.getRightOperand()
|
||||
) and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/** Gets an expression that is the remainder modulo `mod` of `arg`. */
|
||||
private SemExpr modExpr(SemExpr arg, int mod) {
|
||||
exists(SemRemExpr rem |
|
||||
result = rem and
|
||||
arg = rem.getLeftOperand() and
|
||||
rem.getRightOperand().(SemConstantIntegerExpr).getIntValue() = mod and
|
||||
mod >= 2
|
||||
)
|
||||
or
|
||||
exists(SemConstantIntegerExpr c |
|
||||
mod = 2.pow([1 .. 30]) and
|
||||
c.getIntValue() = mod - 1 and
|
||||
result.(SemBitAndExpr).hasOperands(arg, c)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on
|
||||
* its `testIsTrue` branch.
|
||||
*/
|
||||
private SemGuard moduloCheck(SemSsaVariable v, int val, int mod, boolean testIsTrue) {
|
||||
exists(SemExpr rem, SemConstantIntegerExpr c, int r, boolean polarity |
|
||||
result.isEquality(rem, c, polarity) and
|
||||
c.getIntValue() = r and
|
||||
rem = modExpr(v.getAUse(), mod) and
|
||||
(
|
||||
testIsTrue = polarity and val = r
|
||||
or
|
||||
testIsTrue = polarity.booleanNot() and
|
||||
mod = 2 and
|
||||
val = 1 - r and
|
||||
(r = 0 or r = 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
|
||||
*/
|
||||
private predicate moduloGuardedRead(SemSsaVariable v, SemSsaReadPosition pos, int val, int mod) {
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = moduloCheck(v, val, mod, testIsTrue) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `factor` is a power of 2 that divides `mask`. */
|
||||
bindingset[mask]
|
||||
private predicate andmaskFactor(int mask, int factor) {
|
||||
mask % factor = 0 and
|
||||
factor = 2.pow([1 .. 30])
|
||||
}
|
||||
|
||||
/** Holds if `e` is evenly divisible by `factor`. */
|
||||
private predicate evenlyDivisibleExpr(SemExpr e, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() |
|
||||
e.(SemMulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2
|
||||
or
|
||||
e.(SemShiftLeftExpr).getRightOperand() = c and factor = 2.pow(k) and k > 0
|
||||
or
|
||||
e.(SemBitAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rix` is the number of input edges to `phi`.
|
||||
*/
|
||||
private predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
|
||||
rix = max(int r | rankedPhiInput(phi, _, _, r))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remainder of `val` modulo `mod`.
|
||||
*
|
||||
* For `mod = 0` the result equals `val` and for `mod > 1` the result is within
|
||||
* the range `[0 .. mod-1]`.
|
||||
*/
|
||||
bindingset[val, mod]
|
||||
private int remainder(int val, int mod) {
|
||||
mod = 0 and result = val
|
||||
or
|
||||
mod > 1 and result = ((val % mod) + mod) % mod
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
|
||||
*/
|
||||
private predicate phiSelfModulus(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod
|
||||
) {
|
||||
exists(SemSsaBound phibound, int v, int m |
|
||||
edge.phiInput(phi, inp) and
|
||||
phibound.getAVariable() = phi and
|
||||
ssaModulus(inp, edge, phibound, v, m) and
|
||||
mod = m.gcd(v) and
|
||||
mod != 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
|
||||
*/
|
||||
private predicate phiModulusInit(SemSsaPhiNode phi, SemBound b, int val, int mod) {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(phi, inp) and
|
||||
ssaModulus(inp, edge, b, val, mod)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate phiModulusRankStep(SemSsaPhiNode phi, SemBound b, int val, int mod, int rix) {
|
||||
/*
|
||||
* base case. If any phi input is equal to `b + val` modulo `mod`, that's a potential congruence
|
||||
* class for the phi node.
|
||||
module ModulusAnalysis<DeltaSig D, BoundSig<D> Bounds, UtilSig<D> U> {
|
||||
/**
|
||||
* Holds if `e + delta` equals `v` at `pos`.
|
||||
*/
|
||||
private predicate valueFlowStepSsa(SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta) {
|
||||
U::semSsaUpdateStep(v, e, D::fromInt(delta)) and pos.hasReadOfVar(v)
|
||||
or
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = U::semEqFlowCond(v, e, D::fromInt(delta), true, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
rix = 0 and
|
||||
phiModulusInit(phi, b, val, mod)
|
||||
or
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int v1, int m1 |
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
|
|
||||
/**
|
||||
* Holds if `add` is the addition of `larg` and `rarg`, neither of which are
|
||||
* `ConstantIntegerExpr`s.
|
||||
*/
|
||||
private predicate nonConstAddition(SemExpr add, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemAddExpr a | a = add |
|
||||
larg = a.getLeftOperand() and
|
||||
rarg = a.getRightOperand()
|
||||
) and
|
||||
not larg instanceof SemConstantIntegerExpr and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not
|
||||
* a `ConstantIntegerExpr`.
|
||||
*/
|
||||
private predicate nonConstSubtraction(SemExpr sub, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemSubExpr s | s = sub |
|
||||
larg = s.getLeftOperand() and
|
||||
rarg = s.getRightOperand()
|
||||
) and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/** Gets an expression that is the remainder modulo `mod` of `arg`. */
|
||||
private SemExpr modExpr(SemExpr arg, int mod) {
|
||||
exists(SemRemExpr rem |
|
||||
result = rem and
|
||||
arg = rem.getLeftOperand() and
|
||||
rem.getRightOperand().(SemConstantIntegerExpr).getIntValue() = mod and
|
||||
mod >= 2
|
||||
)
|
||||
or
|
||||
exists(SemConstantIntegerExpr c |
|
||||
mod = 2.pow([1 .. 30]) and
|
||||
c.getIntValue() = mod - 1 and
|
||||
result.(SemBitAndExpr).hasOperands(arg, c)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on
|
||||
* its `testIsTrue` branch.
|
||||
*/
|
||||
private SemGuard moduloCheck(SemSsaVariable v, int val, int mod, boolean testIsTrue) {
|
||||
exists(SemExpr rem, SemConstantIntegerExpr c, int r, boolean polarity |
|
||||
result.isEquality(rem, c, polarity) and
|
||||
c.getIntValue() = r and
|
||||
rem = modExpr(v.getAUse(), mod) and
|
||||
(
|
||||
testIsTrue = polarity and val = r
|
||||
or
|
||||
testIsTrue = polarity.booleanNot() and
|
||||
mod = 2 and
|
||||
val = 1 - r and
|
||||
(r = 0 or r = 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
|
||||
*/
|
||||
private predicate moduloGuardedRead(SemSsaVariable v, SemSsaReadPosition pos, int val, int mod) {
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = moduloCheck(v, val, mod, testIsTrue) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `factor` is a power of 2 that divides `mask`. */
|
||||
bindingset[mask]
|
||||
private predicate andmaskFactor(int mask, int factor) {
|
||||
mask % factor = 0 and
|
||||
factor = 2.pow([1 .. 30])
|
||||
}
|
||||
|
||||
/** Holds if `e` is evenly divisible by `factor`. */
|
||||
private predicate evenlyDivisibleExpr(SemExpr e, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() |
|
||||
e.(SemMulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2
|
||||
or
|
||||
e.(SemShiftLeftExpr).getRightOperand() = c and factor = 2.pow(k) and k > 0
|
||||
or
|
||||
e.(SemBitAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `rix` is the number of input edges to `phi`.
|
||||
*/
|
||||
private predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
|
||||
rix = max(int r | rankedPhiInput(phi, _, _, r))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remainder of `val` modulo `mod`.
|
||||
*
|
||||
* For `mod = 0` the result equals `val` and for `mod > 1` the result is within
|
||||
* the range `[0 .. mod-1]`.
|
||||
*/
|
||||
bindingset[val, mod]
|
||||
private int remainder(int val, int mod) {
|
||||
mod = 0 and result = val
|
||||
or
|
||||
mod > 1 and result = ((val % mod) + mod) % mod
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
|
||||
*/
|
||||
private predicate phiSelfModulus(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod
|
||||
) {
|
||||
exists(Bounds::SemSsaBound phibound, int v, int m |
|
||||
edge.phiInput(phi, inp) and
|
||||
phibound.getAVariable() = phi and
|
||||
ssaModulus(inp, edge, phibound, v, m) and
|
||||
mod = m.gcd(v) and
|
||||
mod != 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
|
||||
*/
|
||||
private predicate phiModulusInit(SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(phi, inp) and
|
||||
ssaModulus(inp, edge, b, val, mod)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate phiModulusRankStep(
|
||||
SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod, int rix
|
||||
) {
|
||||
/*
|
||||
* Recursive case. If `inp` = `b + v2` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be the congruence class of `v1` modulo
|
||||
* the greatest common denominator of `m1`, `m2`, and `v1 - v2`.
|
||||
* base case. If any phi input is equal to `b + val` modulo `mod`, that's a potential congruence
|
||||
* class for the phi node.
|
||||
*/
|
||||
|
||||
exists(int v2, int m2 |
|
||||
rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
ssaModulus(inp, edge, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2)
|
||||
rix = 0 and
|
||||
phiModulusInit(phi, b, val, mod)
|
||||
or
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int v1, int m1 |
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
|
|
||||
/*
|
||||
* Recursive case. If `inp` = `b + v2` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be the congruence class of `v1` modulo
|
||||
* the greatest common denominator of `m1`, `m2`, and `v1 - v2`.
|
||||
*/
|
||||
|
||||
exists(int v2, int m2 |
|
||||
rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
ssaModulus(inp, edge, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2)
|
||||
)
|
||||
or
|
||||
/*
|
||||
* Recursive case. If `inp` = `phi` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be a congruence class modulo the greatest
|
||||
* common denominator of `m1` and `m2`.
|
||||
*/
|
||||
|
||||
exists(int m2 |
|
||||
rankedPhiInput(phi, inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
phiSelfModulus(phi, inp, edge, m2) and
|
||||
mod = m1.gcd(m2)
|
||||
)
|
||||
)
|
||||
or
|
||||
/*
|
||||
* Recursive case. If `inp` = `phi` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be a congruence class modulo the greatest
|
||||
* common denominator of `m1` and `m2`.
|
||||
*/
|
||||
}
|
||||
|
||||
exists(int m2 |
|
||||
rankedPhiInput(phi, inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
phiSelfModulus(phi, inp, edge, m2) and
|
||||
mod = m1.gcd(m2)
|
||||
/**
|
||||
* Holds if `phi` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate phiModulus(SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
|
||||
exists(int r |
|
||||
maxPhiInputRank(phi, r) and
|
||||
phiModulusRankStep(phi, b, val, mod, r)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `phi` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate phiModulus(SemSsaPhiNode phi, SemBound b, int val, int mod) {
|
||||
exists(int r |
|
||||
maxPhiInputRank(phi, r) and
|
||||
phiModulusRankStep(phi, b, val, mod, r)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate ssaModulus(SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int val, int mod) {
|
||||
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
|
||||
or
|
||||
b.(SemSsaBound).getAVariable() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0
|
||||
or
|
||||
exists(SemExpr e, int val0, int delta |
|
||||
semExprModulus(e, b, val0, mod) and
|
||||
valueFlowStepSsa(v, pos, e, delta) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
moduloGuardedRead(v, pos, val, mod) and b instanceof SemZeroBound
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is equal to `b + val` modulo `mod`.
|
||||
*
|
||||
* There are two cases for the modulus:
|
||||
* - `mod = 0`: The equality `e = b + val` is an ordinary equality.
|
||||
* - `mod > 1`: `val` lies within the range `[0 .. mod-1]`.
|
||||
*/
|
||||
cached
|
||||
predicate semExprModulus(SemExpr e, SemBound b, int val, int mod) {
|
||||
not ignoreExprModulus(e) and
|
||||
(
|
||||
e = b.getExpr(val) and mod = 0
|
||||
/**
|
||||
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate ssaModulus(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, Bounds::SemBound b, int val, int mod
|
||||
) {
|
||||
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
|
||||
or
|
||||
evenlyDivisibleExpr(e, mod) and
|
||||
val = 0 and
|
||||
b instanceof SemZeroBound
|
||||
b.(Bounds::SemSsaBound).getAVariable() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
ssaModulus(v, bb, b, val, mod) and
|
||||
e = v.getAUse() and
|
||||
bb.getAnExpr() = e
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int val0, int delta |
|
||||
semExprModulus(mid, b, val0, mod) and
|
||||
semValueFlowStep(e, mid, delta) and
|
||||
exists(SemExpr e, int val0, int delta |
|
||||
semExprModulus(e, b, val0, mod) and
|
||||
valueFlowStepSsa(v, pos, e, delta) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
exists(SemConditionalExpr cond, int v1, int v2, int m1, int m2 |
|
||||
cond = e and
|
||||
condExprBranchModulus(cond, true, b, v1, m1) and
|
||||
condExprBranchModulus(cond, false, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
)
|
||||
or
|
||||
exists(SemBound b1, SemBound b2, int v1, int v2, int m1, int m2 |
|
||||
addModulus(e, true, b1, v1, m1) and
|
||||
addModulus(e, false, b2, v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 + v2, mod)
|
||||
|
|
||||
b = b1 and b2 instanceof SemZeroBound
|
||||
moduloGuardedRead(v, pos, val, mod) and b instanceof Bounds::SemZeroBound
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is equal to `b + val` modulo `mod`.
|
||||
*
|
||||
* There are two cases for the modulus:
|
||||
* - `mod = 0`: The equality `e = b + val` is an ordinary equality.
|
||||
* - `mod > 1`: `val` lies within the range `[0 .. mod-1]`.
|
||||
*/
|
||||
cached
|
||||
predicate semExprModulus(SemExpr e, Bounds::SemBound b, int val, int mod) {
|
||||
not ignoreExprModulus(e) and
|
||||
(
|
||||
e = b.getExpr(D::fromInt(val)) and mod = 0
|
||||
or
|
||||
b = b2 and b1 instanceof SemZeroBound
|
||||
evenlyDivisibleExpr(e, mod) and
|
||||
val = 0 and
|
||||
b instanceof Bounds::SemZeroBound
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
ssaModulus(v, bb, b, val, mod) and
|
||||
e = v.getAUse() and
|
||||
bb.getAnExpr() = e
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int val0, int delta |
|
||||
semExprModulus(mid, b, val0, mod) and
|
||||
U::semValueFlowStep(e, mid, D::fromInt(delta)) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
exists(SemConditionalExpr cond, int v1, int v2, int m1, int m2 |
|
||||
cond = e and
|
||||
condExprBranchModulus(cond, true, b, v1, m1) and
|
||||
condExprBranchModulus(cond, false, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
)
|
||||
or
|
||||
exists(Bounds::SemBound b1, Bounds::SemBound b2, int v1, int v2, int m1, int m2 |
|
||||
addModulus(e, true, b1, v1, m1) and
|
||||
addModulus(e, false, b2, v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 + v2, mod)
|
||||
|
|
||||
b = b1 and b2 instanceof Bounds::SemZeroBound
|
||||
or
|
||||
b = b2 and b1 instanceof Bounds::SemZeroBound
|
||||
)
|
||||
or
|
||||
exists(int v1, int v2, int m1, int m2 |
|
||||
subModulus(e, true, b, v1, m1) and
|
||||
subModulus(e, false, any(Bounds::SemZeroBound zb), v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 - v2, mod)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(int v1, int v2, int m1, int m2 |
|
||||
subModulus(e, true, b, v1, m1) and
|
||||
subModulus(e, false, any(SemZeroBound zb), v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 - v2, mod)
|
||||
}
|
||||
|
||||
private predicate condExprBranchModulus(
|
||||
SemConditionalExpr cond, boolean branch, Bounds::SemBound b, int val, int mod
|
||||
) {
|
||||
semExprModulus(cond.getBranchExpr(branch), b, val, mod)
|
||||
}
|
||||
|
||||
private predicate addModulus(SemExpr add, boolean isLeft, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstAddition(add, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate condExprBranchModulus(
|
||||
SemConditionalExpr cond, boolean branch, SemBound b, int val, int mod
|
||||
) {
|
||||
semExprModulus(cond.getBranchExpr(branch), b, val, mod)
|
||||
}
|
||||
|
||||
private predicate addModulus(SemExpr add, boolean isLeft, SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstAddition(add, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
private predicate subModulus(SemExpr sub, boolean isLeft, SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstSubtraction(sub, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
|
||||
* in an arbitrary 1-based numbering of the input edges to `phi`.
|
||||
*/
|
||||
private predicate rankedPhiInput(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
edge =
|
||||
rank[r](SemSsaReadPositionPhiInputEdge e |
|
||||
e.phiInput(phi, _)
|
||||
|
|
||||
e order by e.getOrigBlock().getUniqueId()
|
||||
private predicate subModulus(SemExpr sub, boolean isLeft, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstSubtraction(sub, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
|
||||
* in an arbitrary 1-based numbering of the input edges to `phi`.
|
||||
*/
|
||||
private predicate rankedPhiInput(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
edge =
|
||||
rank[r](SemSsaReadPositionPhiInputEdge e |
|
||||
e.phiInput(phi, _)
|
||||
|
|
||||
e order by e.getOrigBlock().getUniqueId()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,830 +1,24 @@
|
||||
/**
|
||||
* Provides classes and predicates for range analysis.
|
||||
*
|
||||
* An inferred bound can either be a specific integer, the abstract value of an
|
||||
* SSA variable, or the abstract value of an interesting expression. The latter
|
||||
* category includes array lengths that are not SSA variables.
|
||||
*
|
||||
* If an inferred bound relies directly on a condition, then this condition is
|
||||
* reported as the reason for the bound.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This library tackles range analysis as a flow problem. Consider e.g.:
|
||||
* ```
|
||||
* len = arr.length;
|
||||
* if (x < len) { ... y = x-1; ... y ... }
|
||||
* ```
|
||||
* In this case we would like to infer `y <= arr.length - 2`, and this is
|
||||
* accomplished by tracking the bound through a sequence of steps:
|
||||
* ```
|
||||
* arr.length --> len = .. --> x < len --> x-1 --> y = .. --> y
|
||||
* ```
|
||||
*
|
||||
* In its simplest form the step relation `E1 --> E2` relates two expressions
|
||||
* such that `E1 <= B` implies `E2 <= B` for any `B` (with a second separate
|
||||
* step relation handling lower bounds). Examples of such steps include
|
||||
* assignments `E2 = E1` and conditions `x <= E1` where `E2` is a use of `x`
|
||||
* guarded by the condition.
|
||||
*
|
||||
* In order to handle subtractions and additions with constants, and strict
|
||||
* comparisons, the step relation is augmented with an integer delta. With this
|
||||
* generalization `E1 --(delta)--> E2` relates two expressions and an integer
|
||||
* such that `E1 <= B` implies `E2 <= B + delta` for any `B`. This corresponds
|
||||
* to the predicate `boundFlowStep`.
|
||||
*
|
||||
* The complete range analysis is then implemented as the transitive closure of
|
||||
* the step relation summing the deltas along the way. If `E1` transitively
|
||||
* steps to `E2`, `delta` is the sum of deltas along the path, and `B` is an
|
||||
* interesting bound equal to the value of `E1` then `E2 <= B + delta`. This
|
||||
* corresponds to the predicate `bounded`.
|
||||
*
|
||||
* Phi nodes need a little bit of extra handling. Consider `x0 = phi(x1, x2)`.
|
||||
* There are essentially two cases:
|
||||
* - If `x1 <= B + d1` and `x2 <= B + d2` then `x0 <= B + max(d1,d2)`.
|
||||
* - If `x1 <= B + d1` and `x2 <= x0 + d2` with `d2 <= 0` then `x0 <= B + d1`.
|
||||
* The first case is for whenever a bound can be proven without taking looping
|
||||
* into account. The second case is relevant when `x2` comes from a back-edge
|
||||
* where we can prove that the variable has been non-increasing through the
|
||||
* loop-iteration as this means that any upper bound that holds prior to the
|
||||
* loop also holds for the variable during the loop.
|
||||
* This generalizes to a phi node with `n` inputs, so if
|
||||
* `x0 = phi(x1, ..., xn)` and `xi <= B + delta` for one of the inputs, then we
|
||||
* also have `x0 <= B + delta` if we can prove either:
|
||||
* - `xj <= B + d` with `d <= delta` or
|
||||
* - `xj <= x0 + d` with `d <= 0`
|
||||
* for each input `xj`.
|
||||
*
|
||||
* As all inferred bounds can be related directly to a path in the source code
|
||||
* the only source of non-termination is if successive redundant (and thereby
|
||||
* increasingly worse) bounds are calculated along a loop in the source code.
|
||||
* We prevent this by weakening the bound to a small finite set of bounds when
|
||||
* a path follows a second back-edge (we postpone weakening till the second
|
||||
* back-edge as a precise bound might require traversing a loop once).
|
||||
*/
|
||||
|
||||
private import RangeAnalysisSpecific as Specific
|
||||
private import RangeAnalysisStage
|
||||
private import RangeAnalysisSpecific
|
||||
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
private import RangeUtils
|
||||
private import SignAnalysisCommon
|
||||
private import ModulusAnalysis
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import experimental.semmle.code.cpp.semantic.SemanticBound as SemanticBound
|
||||
|
||||
cached
|
||||
private module RangeAnalysisCache {
|
||||
cached
|
||||
module RangeAnalysisPublic {
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e`.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*
|
||||
* The reason for the bound is given by `reason` and may be either a condition
|
||||
* or `NoReason` if the bound was proven directly without the use of a bounding
|
||||
* condition.
|
||||
*/
|
||||
cached
|
||||
predicate semBounded(SemExpr e, SemBound b, int delta, boolean upper, SemReason reason) {
|
||||
bounded(e, b, delta, upper, _, _, reason) and
|
||||
bestBound(e, b, delta, upper)
|
||||
}
|
||||
module Bounds implements BoundSig<FloatDelta> {
|
||||
class SemBound instanceof SemanticBound::SemBound {
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
SemExpr getExpr(float delta) { result = super.getExpr(delta) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `guard = boundFlowCond(_, _, _, _, _) or guard = eqFlowCond(_, _, _, _, _)`.
|
||||
*/
|
||||
cached
|
||||
predicate possibleReason(SemGuard guard) {
|
||||
guard = boundFlowCond(_, _, _, _, _) or guard = semEqFlowCond(_, _, _, _, _)
|
||||
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
|
||||
|
||||
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
|
||||
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
}
|
||||
}
|
||||
|
||||
private import RangeAnalysisCache
|
||||
import RangeAnalysisPublic
|
||||
private module CppRangeAnalysis =
|
||||
RangeStage<FloatDelta, Bounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e` and this is the best such delta.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*/
|
||||
private predicate bestBound(SemExpr e, SemBound b, int delta, boolean upper) {
|
||||
delta = min(int d | bounded(e, b, d, upper, _, _, _)) and upper = true
|
||||
or
|
||||
delta = max(int d | bounded(e, b, d, upper, _, _, _)) and upper = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `comp` corresponds to:
|
||||
* - `upper = true` : `v <= e + delta` or `v < e + delta`
|
||||
* - `upper = false` : `v >= e + delta` or `v > e + delta`
|
||||
*/
|
||||
private predicate boundCondition(
|
||||
SemRelationalExpr comp, SemSsaVariable v, SemExpr e, int delta, boolean upper
|
||||
) {
|
||||
comp.getLesserOperand() = semSsaRead(v, delta) and e = comp.getGreaterOperand() and upper = true
|
||||
or
|
||||
comp.getGreaterOperand() = semSsaRead(v, delta) and e = comp.getLesserOperand() and upper = false
|
||||
or
|
||||
exists(SemSubExpr sub, SemConstantIntegerExpr c, int d |
|
||||
// (v - d) - e < c
|
||||
comp.getLesserOperand() = sub and
|
||||
comp.getGreaterOperand() = c and
|
||||
sub.getLeftOperand() = semSsaRead(v, d) and
|
||||
sub.getRightOperand() = e and
|
||||
upper = true and
|
||||
delta = d + c.getIntValue()
|
||||
or
|
||||
// (v - d) - e > c
|
||||
comp.getGreaterOperand() = sub and
|
||||
comp.getLesserOperand() = c and
|
||||
sub.getLeftOperand() = semSsaRead(v, d) and
|
||||
sub.getRightOperand() = e and
|
||||
upper = false and
|
||||
delta = d + c.getIntValue()
|
||||
or
|
||||
// e - (v - d) < c
|
||||
comp.getLesserOperand() = sub and
|
||||
comp.getGreaterOperand() = c and
|
||||
sub.getLeftOperand() = e and
|
||||
sub.getRightOperand() = semSsaRead(v, d) and
|
||||
upper = false and
|
||||
delta = d - c.getIntValue()
|
||||
or
|
||||
// e - (v - d) > c
|
||||
comp.getGreaterOperand() = sub and
|
||||
comp.getLesserOperand() = c and
|
||||
sub.getLeftOperand() = e and
|
||||
sub.getRightOperand() = semSsaRead(v, d) and
|
||||
upper = true and
|
||||
delta = d - c.getIntValue()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `comp` is a comparison between `x` and `y` for which `y - x` has a
|
||||
* fixed value modulo some `mod > 1`, such that the comparison can be
|
||||
* strengthened by `strengthen` when evaluating to `testIsTrue`.
|
||||
*/
|
||||
private predicate modulusComparison(SemRelationalExpr comp, boolean testIsTrue, int strengthen) {
|
||||
exists(
|
||||
SemBound b, int v1, int v2, int mod1, int mod2, int mod, boolean resultIsStrict, int d, int k
|
||||
|
|
||||
// If `x <= y` and `x =(mod) b + v1` and `y =(mod) b + v2` then
|
||||
// `0 <= y - x =(mod) v2 - v1`. By choosing `k =(mod) v2 - v1` with
|
||||
// `0 <= k < mod` we get `k <= y - x`. If the resulting comparison is
|
||||
// strict then the strengthening amount is instead `k - 1` modulo `mod`:
|
||||
// `x < y` means `0 <= y - x - 1 =(mod) k - 1` so `k - 1 <= y - x - 1` and
|
||||
// thus `k - 1 < y - x` with `0 <= k - 1 < mod`.
|
||||
semExprModulus(comp.getLesserOperand(), b, v1, mod1) and
|
||||
semExprModulus(comp.getGreaterOperand(), b, v2, mod2) and
|
||||
mod = mod1.gcd(mod2) and
|
||||
mod != 1 and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
(
|
||||
if comp.isStrict()
|
||||
then resultIsStrict = testIsTrue
|
||||
else resultIsStrict = testIsTrue.booleanNot()
|
||||
) and
|
||||
(
|
||||
resultIsStrict = true and d = 1
|
||||
or
|
||||
resultIsStrict = false and d = 0
|
||||
) and
|
||||
(
|
||||
testIsTrue = true and k = v2 - v1
|
||||
or
|
||||
testIsTrue = false and k = v1 - v2
|
||||
) and
|
||||
strengthen = (((k - d) % mod) + mod) % mod
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition that tests whether `v` is bounded by `e + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `upper = true` : `v <= e + delta`
|
||||
* - `upper = false` : `v >= e + delta`
|
||||
*/
|
||||
private SemGuard boundFlowCond(
|
||||
SemSsaVariable v, SemExpr e, int delta, boolean upper, boolean testIsTrue
|
||||
) {
|
||||
exists(
|
||||
SemRelationalExpr comp, int d1, int d2, int d3, int strengthen, boolean compIsUpper,
|
||||
boolean resultIsStrict
|
||||
|
|
||||
comp = result.asExpr() and
|
||||
boundCondition(comp, v, e, d1, compIsUpper) and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
upper = compIsUpper.booleanXor(testIsTrue.booleanNot()) and
|
||||
(
|
||||
if comp.isStrict()
|
||||
then resultIsStrict = testIsTrue
|
||||
else resultIsStrict = testIsTrue.booleanNot()
|
||||
) and
|
||||
(
|
||||
if
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemIntegerType or
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemAddressType
|
||||
then
|
||||
upper = true and strengthen = -1
|
||||
or
|
||||
upper = false and strengthen = 1
|
||||
else strengthen = 0
|
||||
) and
|
||||
(
|
||||
exists(int k | modulusComparison(comp, testIsTrue, k) and d2 = strengthen * k)
|
||||
or
|
||||
not modulusComparison(comp, testIsTrue, _) and d2 = 0
|
||||
) and
|
||||
// A strict inequality `x < y` can be strengthened to `x <= y - 1`.
|
||||
(
|
||||
resultIsStrict = true and d3 = strengthen
|
||||
or
|
||||
resultIsStrict = false and d3 = 0
|
||||
) and
|
||||
delta = d1 + d2 + d3
|
||||
)
|
||||
or
|
||||
exists(boolean testIsTrue0 |
|
||||
semImplies_v2(result, testIsTrue, boundFlowCond(v, e, delta, upper, testIsTrue0), testIsTrue0)
|
||||
)
|
||||
or
|
||||
result = semEqFlowCond(v, e, delta, true, testIsTrue) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
// guard that tests whether `v2` is bounded by `e + delta + d1 - d2` and
|
||||
// exists a guard `guardEq` such that `v = v2 - d1 + d2`.
|
||||
exists(SemSsaVariable v2, SemGuard guardEq, boolean eqIsTrue, int d1, int d2 |
|
||||
guardEq = semEqFlowCond(v, semSsaRead(v2, d1), d2, true, eqIsTrue) and
|
||||
result = boundFlowCond(v2, e, delta + d1 - d2, upper, testIsTrue) and
|
||||
// guardEq needs to control guard
|
||||
guardEq.directlyControls(result.getBasicBlock(), eqIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TSemReason =
|
||||
TSemNoReason() or
|
||||
TSemCondReason(SemGuard guard) { possibleReason(guard) }
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound. This can either be `CondReason` if the bound
|
||||
* is due to a specific condition, or `NoReason` if the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
abstract class SemReason extends TSemReason {
|
||||
/** Gets a textual representation of this reason. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
class SemNoReason extends SemReason, TSemNoReason {
|
||||
override string toString() { result = "NoReason" }
|
||||
}
|
||||
|
||||
/** A reason for an inferred bound pointing to a condition. */
|
||||
class SemCondReason extends SemReason, TSemCondReason {
|
||||
/** Gets the condition that is the reason for the bound. */
|
||||
SemGuard getCond() { this = TSemCondReason(result) }
|
||||
|
||||
override string toString() { result = getCond().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e + delta` is a valid bound for `v` at `pos`.
|
||||
* - `upper = true` : `v <= e + delta`
|
||||
* - `upper = false` : `v >= e + delta`
|
||||
*/
|
||||
private predicate boundFlowStepSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta, boolean upper, SemReason reason
|
||||
) {
|
||||
semSsaUpdateStep(v, e, delta) and
|
||||
pos.hasReadOfVar(v) and
|
||||
(upper = true or upper = false) and
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = boundFlowCond(v, e, delta, upper, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
|
||||
reason = TSemCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `v != e + delta` at `pos` and `v` is of integral type. */
|
||||
private predicate unequalFlowStepIntegralSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta, SemReason reason
|
||||
) {
|
||||
getTrackedTypeForSsaVariable(v) instanceof SemIntegerType and
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = semEqFlowCond(v, e, delta, false, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue) and
|
||||
reason = TSemCondReason(guard)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that does conversion, boxing, or unboxing
|
||||
*/
|
||||
private class ConvertOrBoxExpr extends SemUnaryExpr {
|
||||
ConvertOrBoxExpr() {
|
||||
this instanceof SemConvertExpr
|
||||
or
|
||||
this instanceof SemBoxExpr
|
||||
or
|
||||
this instanceof SemUnboxExpr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cast that can be ignored for the purpose of range analysis.
|
||||
*/
|
||||
private class SafeCastExpr extends ConvertOrBoxExpr {
|
||||
SafeCastExpr() {
|
||||
conversionCannotOverflow(getTrackedType(pragma[only_bind_into](getOperand())),
|
||||
getTrackedType(this))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `typ` is a small integral type with the given lower and upper bounds.
|
||||
*/
|
||||
private predicate typeBound(SemIntegerType typ, int lowerbound, int upperbound) {
|
||||
exists(int bitSize | bitSize = typ.getByteSize() * 8 |
|
||||
bitSize < 32 and
|
||||
(
|
||||
if typ.isSigned()
|
||||
then (
|
||||
upperbound = 1.bitShiftLeft(bitSize - 1) - 1 and
|
||||
lowerbound = -upperbound - 1
|
||||
) else (
|
||||
lowerbound = 0 and
|
||||
upperbound = 1.bitShiftLeft(bitSize) - 1
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A cast to a small integral type that may overflow or underflow.
|
||||
*/
|
||||
private class NarrowingCastExpr extends ConvertOrBoxExpr {
|
||||
NarrowingCastExpr() {
|
||||
not this instanceof SafeCastExpr and
|
||||
typeBound(getTrackedType(this), _, _)
|
||||
}
|
||||
|
||||
/** Gets the lower bound of the resulting type. */
|
||||
int getLowerBound() { typeBound(getTrackedType(this), result, _) }
|
||||
|
||||
/** Gets the upper bound of the resulting type. */
|
||||
int getUpperBound() { typeBound(getTrackedType(this), _, result) }
|
||||
}
|
||||
|
||||
/** Holds if `e >= 1` as determined by sign analysis. */
|
||||
private predicate strictlyPositiveIntegralExpr(SemExpr e) {
|
||||
semStrictlyPositive(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/** Holds if `e <= -1` as determined by sign analysis. */
|
||||
private predicate strictlyNegativeIntegralExpr(SemExpr e) {
|
||||
semStrictlyNegative(e) and getTrackedType(e) instanceof SemIntegerType
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e1 + delta` is a valid bound for `e2`.
|
||||
* - `upper = true` : `e2 <= e1 + delta`
|
||||
* - `upper = false` : `e2 >= e1 + delta`
|
||||
*/
|
||||
private predicate boundFlowStep(SemExpr e2, SemExpr e1, int delta, boolean upper) {
|
||||
semValueFlowStep(e2, e1, delta) and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
e2.(SafeCastExpr).getOperand() = e1 and
|
||||
delta = 0 and
|
||||
(upper = true or upper = false)
|
||||
or
|
||||
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
|
||||
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
|
||||
not x instanceof SemConstantIntegerExpr and
|
||||
not e1 instanceof SemConstantIntegerExpr and
|
||||
if strictlyPositiveIntegralExpr(x)
|
||||
then upper = false and delta = 1
|
||||
else
|
||||
if semPositive(x)
|
||||
then upper = false and delta = 0
|
||||
else
|
||||
if strictlyNegativeIntegralExpr(x)
|
||||
then upper = true and delta = -1
|
||||
else
|
||||
if semNegative(x)
|
||||
then upper = true and delta = 0
|
||||
else none()
|
||||
)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
// `x instanceof ConstantIntegerExpr` is covered by valueFlowStep
|
||||
not x instanceof SemConstantIntegerExpr and
|
||||
if strictlyPositiveIntegralExpr(x)
|
||||
then upper = true and delta = -1
|
||||
else
|
||||
if semPositive(x)
|
||||
then upper = true and delta = 0
|
||||
else
|
||||
if strictlyNegativeIntegralExpr(x)
|
||||
then upper = false and delta = 1
|
||||
else
|
||||
if semNegative(x)
|
||||
then upper = false and delta = 0
|
||||
else none()
|
||||
)
|
||||
or
|
||||
e2.(SemRemExpr).getRightOperand() = e1 and
|
||||
semPositive(e1) and
|
||||
delta = -1 and
|
||||
upper = true
|
||||
or
|
||||
e2.(SemRemExpr).getLeftOperand() = e1 and semPositive(e1) and delta = 0 and upper = true
|
||||
or
|
||||
e2.(SemBitAndExpr).getAnOperand() = e1 and
|
||||
semPositive(e1) and
|
||||
delta = 0 and
|
||||
upper = true
|
||||
or
|
||||
e2.(SemBitOrExpr).getAnOperand() = e1 and
|
||||
semPositive(e2) and
|
||||
delta = 0 and
|
||||
upper = false
|
||||
or
|
||||
Specific::hasBound(e2, e1, delta, upper)
|
||||
}
|
||||
|
||||
/** Holds if `e2 = e1 * factor` and `factor > 0`. */
|
||||
private predicate boundFlowStepMul(SemExpr e2, SemExpr e1, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 |
|
||||
e2.(SemMulExpr).hasOperands(e1, c) and factor = k
|
||||
or
|
||||
exists(SemShiftLeftExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e2 = e1 / factor` and `factor > 0`.
|
||||
*
|
||||
* This conflates division, right shift, and unsigned right shift and is
|
||||
* therefore only valid for non-negative numbers.
|
||||
*/
|
||||
private predicate boundFlowStepDiv(SemExpr e2, SemExpr e1, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() and k > 0 |
|
||||
exists(SemDivExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = k
|
||||
)
|
||||
or
|
||||
exists(SemShiftRightExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
|
||||
)
|
||||
or
|
||||
exists(SemShiftRightUnsignedExpr e |
|
||||
e = e2 and e.getLeftOperand() = e1 and e.getRightOperand() = c and factor = 2.pow(k)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `v` at `pos`.
|
||||
* - `upper = true` : `v <= b + delta`
|
||||
* - `upper = false` : `v >= b + delta`
|
||||
*/
|
||||
private predicate boundedSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int delta, boolean upper,
|
||||
boolean fromBackEdge, int origdelta, SemReason reason
|
||||
) {
|
||||
exists(SemExpr mid, int d1, int d2, SemReason r1, SemReason r2 |
|
||||
boundFlowStepSsa(v, pos, mid, d1, upper, r1) and
|
||||
bounded(mid, b, d2, upper, fromBackEdge, origdelta, r2) and
|
||||
// upper = true: v <= mid + d1 <= b + d1 + d2 = b + delta
|
||||
// upper = false: v >= mid + d1 >= b + d1 + d2 = b + delta
|
||||
delta = d1 + d2 and
|
||||
(if r1 instanceof SemNoReason then reason = r2 else reason = r1)
|
||||
)
|
||||
or
|
||||
exists(int d, SemReason r1, SemReason r2 |
|
||||
boundedSsa(v, pos, b, d, upper, fromBackEdge, origdelta, r2) or
|
||||
boundedPhi(v, b, d, upper, fromBackEdge, origdelta, r2)
|
||||
|
|
||||
unequalIntegralSsa(v, pos, b, d, r1) and
|
||||
(
|
||||
upper = true and delta = d - 1
|
||||
or
|
||||
upper = false and delta = d + 1
|
||||
) and
|
||||
(
|
||||
reason = r1
|
||||
or
|
||||
reason = r2 and not r2 instanceof SemNoReason
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v != b + delta` at `pos` and `v` is of integral type.
|
||||
*/
|
||||
private predicate unequalIntegralSsa(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, SemBound b, int delta, SemReason reason
|
||||
) {
|
||||
exists(SemExpr e, int d1, int d2 |
|
||||
unequalFlowStepIntegralSsa(v, pos, e, d1, reason) and
|
||||
boundedUpper(e, b, d1) and
|
||||
boundedLower(e, b, d2) and
|
||||
delta = d2 + d1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is an upper bound for `e`.
|
||||
*
|
||||
* This predicate only exists to prevent a bad standard order in `unequalIntegralSsa`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedUpper(SemExpr e, SemBound b, int delta) {
|
||||
bounded(e, b, delta, true, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a lower bound for `e`.
|
||||
*
|
||||
* This predicate only exists to prevent a bad standard order in `unequalIntegralSsa`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate boundedLower(SemExpr e, SemBound b, int delta) {
|
||||
bounded(e, b, delta, false, _, _, _)
|
||||
}
|
||||
|
||||
/** Weakens a delta to lie in the range `[-1..1]`. */
|
||||
bindingset[delta, upper]
|
||||
private int weakenDelta(boolean upper, int delta) {
|
||||
delta in [-1 .. 1] and result = delta
|
||||
or
|
||||
upper = true and result = -1 and delta < -1
|
||||
or
|
||||
upper = false and result = 1 and delta > 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `inp` when used as an input to
|
||||
* `phi` along `edge`.
|
||||
* - `upper = true` : `inp <= b + delta`
|
||||
* - `upper = false` : `inp >= b + delta`
|
||||
*/
|
||||
private predicate boundedPhiInp(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, SemBound b, int delta,
|
||||
boolean upper, boolean fromBackEdge, int origdelta, SemReason reason
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
exists(int d, boolean fromBackEdge0 |
|
||||
boundedSsa(inp, edge, b, d, upper, fromBackEdge0, origdelta, reason)
|
||||
or
|
||||
boundedPhi(inp, b, d, upper, fromBackEdge0, origdelta, reason)
|
||||
or
|
||||
b.(SemSsaBound).getAVariable() = inp and
|
||||
d = 0 and
|
||||
(upper = true or upper = false) and
|
||||
fromBackEdge0 = false and
|
||||
origdelta = 0 and
|
||||
reason = TSemNoReason()
|
||||
|
|
||||
if semBackEdge(phi, inp, edge)
|
||||
then
|
||||
fromBackEdge = true and
|
||||
(
|
||||
fromBackEdge0 = true and delta = weakenDelta(upper, d - origdelta) + origdelta
|
||||
or
|
||||
fromBackEdge0 = false and delta = d
|
||||
)
|
||||
else (
|
||||
delta = d and fromBackEdge = fromBackEdge0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `inp` when used as an input to
|
||||
* `phi` along `edge`.
|
||||
* - `upper = true` : `inp <= b + delta`
|
||||
* - `upper = false` : `inp >= b + delta`
|
||||
*
|
||||
* Equivalent to `boundedPhiInp(phi, inp, edge, b, delta, upper, _, _, _)`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate boundedPhiInp1(
|
||||
SemSsaPhiNode phi, SemBound b, boolean upper, SemSsaVariable inp,
|
||||
SemSsaReadPositionPhiInputEdge edge, int delta
|
||||
) {
|
||||
boundedPhiInp(phi, inp, edge, b, delta, upper, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `phi` is a valid bound for `inp` when used as an input to `phi`
|
||||
* along `edge`.
|
||||
* - `upper = true` : `inp <= phi`
|
||||
* - `upper = false` : `inp >= phi`
|
||||
*/
|
||||
private predicate selfBoundedPhiInp(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, boolean upper
|
||||
) {
|
||||
exists(int d, SemSsaBound phibound |
|
||||
phibound.getAVariable() = phi and
|
||||
boundedPhiInp(phi, inp, edge, phibound, d, upper, _, _, _) and
|
||||
(
|
||||
upper = true and d <= 0
|
||||
or
|
||||
upper = false and d >= 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for some input, `inp`, to `phi`, and
|
||||
* thus a candidate bound for `phi`.
|
||||
* - `upper = true` : `inp <= b + delta`
|
||||
* - `upper = false` : `inp >= b + delta`
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate boundedPhiCand(
|
||||
SemSsaPhiNode phi, boolean upper, SemBound b, int delta, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
boundedPhiInp(phi, _, _, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the candidate bound `b + delta` for `phi` is valid for the phi input
|
||||
* `inp` along `edge`.
|
||||
*/
|
||||
private predicate boundedPhiCandValidForEdge(
|
||||
SemSsaPhiNode phi, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge
|
||||
) {
|
||||
boundedPhiCand(phi, upper, b, delta, fromBackEdge, origdelta, reason) and
|
||||
(
|
||||
exists(int d | boundedPhiInp1(phi, b, upper, inp, edge, d) | upper = true and d <= delta)
|
||||
or
|
||||
exists(int d | boundedPhiInp1(phi, b, upper, inp, edge, d) | upper = false and d >= delta)
|
||||
or
|
||||
selfBoundedPhiInp(phi, inp, edge, upper)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `phi`.
|
||||
* - `upper = true` : `phi <= b + delta`
|
||||
* - `upper = false` : `phi >= b + delta`
|
||||
*/
|
||||
private predicate boundedPhi(
|
||||
SemSsaPhiNode phi, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
forex(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge | edge.phiInput(phi, inp) |
|
||||
boundedPhiCandValidForEdge(phi, b, delta, upper, fromBackEdge, origdelta, reason, inp, edge)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` has an upper (for `upper = true`) or lower
|
||||
* (for `upper = false`) bound of `b`.
|
||||
*/
|
||||
private predicate baseBound(SemExpr e, int b, boolean upper) {
|
||||
Specific::hasConstantBound(e, b, upper)
|
||||
or
|
||||
upper = false and
|
||||
b = 0 and
|
||||
semPositive(e.(SemBitAndExpr).getAnOperand()) and
|
||||
// REVIEW: We let the language opt out here to preserve original results.
|
||||
not Specific::ignoreZeroLowerBound(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value being cast has an upper (for `upper = true`) or lower
|
||||
* (for `upper = false`) bound within the bounds of the resulting type.
|
||||
* For `upper = true` this means that the cast will not overflow and for
|
||||
* `upper = false` this means that the cast will not underflow.
|
||||
*/
|
||||
private predicate safeNarrowingCast(NarrowingCastExpr cast, boolean upper) {
|
||||
exists(int bound | bounded(cast.getOperand(), any(SemZeroBound zb), bound, upper, _, _, _) |
|
||||
upper = true and bound <= cast.getUpperBound()
|
||||
or
|
||||
upper = false and bound >= cast.getLowerBound()
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate boundedCastExpr(
|
||||
NarrowingCastExpr cast, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
bounded(cast.getOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + delta` is a valid bound for `e`.
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*/
|
||||
private predicate bounded(
|
||||
SemExpr e, SemBound b, int delta, boolean upper, boolean fromBackEdge, int origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
not Specific::ignoreExprBound(e) and
|
||||
(
|
||||
e = b.getExpr(delta) and
|
||||
(upper = true or upper = false) and
|
||||
fromBackEdge = false and
|
||||
origdelta = delta and
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
baseBound(e, delta, upper) and
|
||||
b instanceof SemZeroBound and
|
||||
fromBackEdge = false and
|
||||
origdelta = delta and
|
||||
reason = TSemNoReason()
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
boundedSsa(v, bb, b, delta, upper, fromBackEdge, origdelta, reason) and
|
||||
e = v.getAUse() and
|
||||
bb.getBlock() = e.getBasicBlock()
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int d1, int d2 |
|
||||
boundFlowStep(e, mid, d1, upper) and
|
||||
// Constants have easy, base-case bounds, so let's not infer any recursive bounds.
|
||||
not e instanceof SemConstantIntegerExpr and
|
||||
bounded(mid, b, d2, upper, fromBackEdge, origdelta, reason) and
|
||||
// upper = true: e <= mid + d1 <= b + d1 + d2 = b + delta
|
||||
// upper = false: e >= mid + d1 >= b + d1 + d2 = b + delta
|
||||
delta = d1 + d2
|
||||
)
|
||||
or
|
||||
exists(SemSsaPhiNode phi |
|
||||
boundedPhi(phi, b, delta, upper, fromBackEdge, origdelta, reason) and
|
||||
e = phi.getAUse()
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int factor, int d |
|
||||
boundFlowStepMul(e, mid, factor) and
|
||||
not e instanceof SemConstantIntegerExpr and
|
||||
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
|
||||
b instanceof SemZeroBound and
|
||||
delta = d * factor
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int factor, int d |
|
||||
boundFlowStepDiv(e, mid, factor) and
|
||||
not e instanceof SemConstantIntegerExpr and
|
||||
bounded(mid, b, d, upper, fromBackEdge, origdelta, reason) and
|
||||
b instanceof SemZeroBound and
|
||||
d >= 0 and
|
||||
delta = d / factor
|
||||
)
|
||||
or
|
||||
exists(NarrowingCastExpr cast |
|
||||
cast = e and
|
||||
safeNarrowingCast(cast, upper.booleanNot()) and
|
||||
boundedCastExpr(cast, b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
SemConditionalExpr cond, int d1, int d2, boolean fbe1, boolean fbe2, int od1, int od2,
|
||||
SemReason r1, SemReason r2
|
||||
|
|
||||
cond = e and
|
||||
boundedConditionalExpr(cond, b, upper, true, d1, fbe1, od1, r1) and
|
||||
boundedConditionalExpr(cond, b, upper, false, d2, fbe2, od2, r2) and
|
||||
(
|
||||
delta = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1
|
||||
or
|
||||
delta = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2
|
||||
)
|
||||
|
|
||||
upper = true and delta = d1.maximum(d2)
|
||||
or
|
||||
upper = false and delta = d1.minimum(d2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate boundedConditionalExpr(
|
||||
SemConditionalExpr cond, SemBound b, boolean upper, boolean branch, int delta,
|
||||
boolean fromBackEdge, int origdelta, SemReason reason
|
||||
) {
|
||||
bounded(cond.getBranchExpr(branch), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
import CppRangeAnalysis
|
||||
|
||||
@@ -3,86 +3,90 @@
|
||||
*/
|
||||
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import RangeAnalysisStage
|
||||
private import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadCopy(SemExpr e) { none() }
|
||||
module CppLangImpl implements LangSig<FloatDelta> {
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadCopy(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore the bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreExprBound(SemExpr e) { none() }
|
||||
/**
|
||||
* Ignore the bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreExprBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore any inferred zero lower bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreZeroLowerBound(SemExpr e) { none() }
|
||||
/**
|
||||
* Ignore any inferred zero lower bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreZeroLowerBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
|
||||
/**
|
||||
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
|
||||
|
||||
/**
|
||||
* Adds additional results to `ssaRead()` that are specific to Java.
|
||||
*
|
||||
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
|
||||
* in exactly the same way as the old Java implementation. Once the new implementation matches the
|
||||
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
|
||||
* or not they come from a post-increment/decrement expression.
|
||||
*/
|
||||
SemExpr specificSsaRead(SemSsaVariable v, int delta) { none() }
|
||||
/**
|
||||
* Adds additional results to `ssaRead()` that are specific to Java.
|
||||
*
|
||||
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
|
||||
* in exactly the same way as the old Java implementation. Once the new implementation matches the
|
||||
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
|
||||
* or not they come from a post-increment/decrement expression.
|
||||
*/
|
||||
SemExpr specificSsaRead(SemSsaVariable v, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
|
||||
*/
|
||||
predicate hasConstantBound(SemExpr e, int bound, boolean upper) { none() }
|
||||
/**
|
||||
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
|
||||
*/
|
||||
predicate hasConstantBound(SemExpr e, float bound, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
|
||||
*/
|
||||
predicate hasBound(SemExpr e, SemExpr bound, int delta, boolean upper) { none() }
|
||||
/**
|
||||
* Holds if `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
|
||||
*/
|
||||
predicate hasBound(SemExpr e, SemExpr bound, float delta, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the value of `dest` is known to be `src + delta`.
|
||||
*/
|
||||
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, int delta) { none() }
|
||||
/**
|
||||
* Holds if the value of `dest` is known to be `src + delta`.
|
||||
*/
|
||||
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified expression,
|
||||
* if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateType(SemExpr e) { none() }
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified expression,
|
||||
* if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateType(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified source
|
||||
* variable, if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateTypeForSsaVariable(SemSsaVariable var) { none() }
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified source
|
||||
* variable, if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateTypeForSsaVariable(SemSsaVariable var) { none() }
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,133 +3,138 @@
|
||||
*/
|
||||
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import RangeAnalysisSpecific as Specific
|
||||
private import RangeAnalysisSpecific
|
||||
private import RangeAnalysisStage as Range
|
||||
private import ConstantAnalysis
|
||||
|
||||
/**
|
||||
* Gets an expression that equals `v - d`.
|
||||
*/
|
||||
SemExpr semSsaRead(SemSsaVariable v, int delta) {
|
||||
// There are various language-specific extension points that can be removed once we no longer
|
||||
// expect to match the original Java implementation's results exactly.
|
||||
result = v.getAUse() and delta = 0
|
||||
or
|
||||
exists(int d1, SemConstantIntegerExpr c |
|
||||
result.(SemAddExpr).hasOperands(semSsaRead(v, d1), c) and
|
||||
delta = d1 - c.getIntValue() and
|
||||
not Specific::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
exists(SemSubExpr sub, int d1, SemConstantIntegerExpr c |
|
||||
result = sub and
|
||||
sub.getLeftOperand() = semSsaRead(v, d1) and
|
||||
sub.getRightOperand() = c and
|
||||
delta = d1 + c.getIntValue() and
|
||||
not Specific::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
result = v.(SemSsaExplicitUpdate).getSourceExpr() and
|
||||
delta = 0 and
|
||||
not Specific::ignoreSsaReadAssignment(v)
|
||||
or
|
||||
result = Specific::specificSsaRead(v, delta)
|
||||
or
|
||||
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta) and
|
||||
not Specific::ignoreSsaReadCopy(result)
|
||||
or
|
||||
result.(SemStoreExpr).getOperand() = semSsaRead(v, delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition that tests whether `v` equals `e + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `isEq = true` : `v == e + delta`
|
||||
* - `isEq = false` : `v != e + delta`
|
||||
*/
|
||||
SemGuard semEqFlowCond(SemSsaVariable v, SemExpr e, int delta, boolean isEq, boolean testIsTrue) {
|
||||
exists(boolean eqpolarity |
|
||||
result.isEquality(semSsaRead(v, delta), e, eqpolarity) and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
eqpolarity.booleanXor(testIsTrue).booleanNot() = isEq
|
||||
)
|
||||
or
|
||||
exists(boolean testIsTrue0 |
|
||||
semImplies_v2(result, testIsTrue, semEqFlowCond(v, e, delta, isEq, testIsTrue0), testIsTrue0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is an `SsaExplicitUpdate` that equals `e + delta`.
|
||||
*/
|
||||
predicate semSsaUpdateStep(SemSsaExplicitUpdate v, SemExpr e, int delta) {
|
||||
exists(SemExpr defExpr | defExpr = v.getSourceExpr() |
|
||||
defExpr.(SemCopyValueExpr).getOperand() = e and delta = 0
|
||||
module RangeUtil<Range::DeltaSig D, Range::LangSig<D> Lang> implements Range::UtilSig<D> {
|
||||
/**
|
||||
* Gets an expression that equals `v - d`.
|
||||
*/
|
||||
SemExpr semSsaRead(SemSsaVariable v, D::Delta delta) {
|
||||
// There are various language-specific extension points that can be removed once we no longer
|
||||
// expect to match the original Java implementation's results exactly.
|
||||
result = v.getAUse() and delta = D::fromInt(0)
|
||||
or
|
||||
defExpr.(SemStoreExpr).getOperand() = e and delta = 0
|
||||
exists(D::Delta d1, SemConstantIntegerExpr c |
|
||||
result.(SemAddExpr).hasOperands(semSsaRead(v, d1), c) and
|
||||
delta = D::fromFloat(D::toFloat(d1) - c.getIntValue()) and
|
||||
not Lang::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
defExpr.(SemAddOneExpr).getOperand() = e and delta = 1
|
||||
exists(SemSubExpr sub, D::Delta d1, SemConstantIntegerExpr c |
|
||||
result = sub and
|
||||
sub.getLeftOperand() = semSsaRead(v, d1) and
|
||||
sub.getRightOperand() = c and
|
||||
delta = D::fromFloat(D::toFloat(d1) + c.getIntValue()) and
|
||||
not Lang::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
defExpr.(SemSubOneExpr).getOperand() = e and delta = -1
|
||||
result = v.(SemSsaExplicitUpdate).getSourceExpr() and
|
||||
delta = D::fromFloat(0) and
|
||||
not Lang::ignoreSsaReadAssignment(v)
|
||||
or
|
||||
e = defExpr and
|
||||
not (
|
||||
defExpr instanceof SemCopyValueExpr or
|
||||
defExpr instanceof SemStoreExpr or
|
||||
defExpr instanceof SemAddOneExpr or
|
||||
defExpr instanceof SemSubOneExpr
|
||||
) and
|
||||
delta = 0
|
||||
)
|
||||
}
|
||||
result = Lang::specificSsaRead(v, delta)
|
||||
or
|
||||
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta) and
|
||||
not Lang::ignoreSsaReadCopy(result)
|
||||
or
|
||||
result.(SemStoreExpr).getOperand() = semSsaRead(v, delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e1 + delta` equals `e2`.
|
||||
*/
|
||||
predicate semValueFlowStep(SemExpr e2, SemExpr e1, int delta) {
|
||||
e2.(SemCopyValueExpr).getOperand() = e1 and delta = 0
|
||||
or
|
||||
e2.(SemStoreExpr).getOperand() = e1 and delta = 0
|
||||
or
|
||||
e2.(SemAddOneExpr).getOperand() = e1 and delta = 1
|
||||
or
|
||||
e2.(SemSubOneExpr).getOperand() = e1 and delta = -1
|
||||
or
|
||||
Specific::additionalValueFlowStep(e2, e1, delta)
|
||||
or
|
||||
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
|
||||
x.(SemConstantIntegerExpr).getIntValue() = delta
|
||||
)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
x.(SemConstantIntegerExpr).getIntValue() = -delta
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Gets a condition that tests whether `v` equals `e + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `isEq = true` : `v == e + delta`
|
||||
* - `isEq = false` : `v != e + delta`
|
||||
*/
|
||||
SemGuard semEqFlowCond(
|
||||
SemSsaVariable v, SemExpr e, D::Delta delta, boolean isEq, boolean testIsTrue
|
||||
) {
|
||||
exists(boolean eqpolarity |
|
||||
result.isEquality(semSsaRead(v, delta), e, eqpolarity) and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
eqpolarity.booleanXor(testIsTrue).booleanNot() = isEq
|
||||
)
|
||||
or
|
||||
exists(boolean testIsTrue0 |
|
||||
semImplies_v2(result, testIsTrue, semEqFlowCond(v, e, delta, isEq, testIsTrue0), testIsTrue0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified expression's range information.
|
||||
*
|
||||
* Usually, this just `e.getSemType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedType(SemExpr e) {
|
||||
result = Specific::getAlternateType(e)
|
||||
or
|
||||
not exists(Specific::getAlternateType(e)) and result = e.getSemType()
|
||||
}
|
||||
/**
|
||||
* Holds if `v` is an `SsaExplicitUpdate` that equals `e + delta`.
|
||||
*/
|
||||
predicate semSsaUpdateStep(SemSsaExplicitUpdate v, SemExpr e, D::Delta delta) {
|
||||
exists(SemExpr defExpr | defExpr = v.getSourceExpr() |
|
||||
defExpr.(SemCopyValueExpr).getOperand() = e and delta = D::fromFloat(0)
|
||||
or
|
||||
defExpr.(SemStoreExpr).getOperand() = e and delta = D::fromFloat(0)
|
||||
or
|
||||
defExpr.(SemAddOneExpr).getOperand() = e and delta = D::fromFloat(1)
|
||||
or
|
||||
defExpr.(SemSubOneExpr).getOperand() = e and delta = D::fromFloat(-1)
|
||||
or
|
||||
e = defExpr and
|
||||
not (
|
||||
defExpr instanceof SemCopyValueExpr or
|
||||
defExpr instanceof SemStoreExpr or
|
||||
defExpr instanceof SemAddOneExpr or
|
||||
defExpr instanceof SemSubOneExpr
|
||||
) and
|
||||
delta = D::fromFloat(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified source variable's range information.
|
||||
*
|
||||
* Usually, this just `e.getType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedTypeForSsaVariable(SemSsaVariable var) {
|
||||
result = Specific::getAlternateTypeForSsaVariable(var)
|
||||
or
|
||||
not exists(Specific::getAlternateTypeForSsaVariable(var)) and result = var.getType()
|
||||
/**
|
||||
* Holds if `e1 + delta` equals `e2`.
|
||||
*/
|
||||
predicate semValueFlowStep(SemExpr e2, SemExpr e1, D::Delta delta) {
|
||||
e2.(SemCopyValueExpr).getOperand() = e1 and delta = D::fromFloat(0)
|
||||
or
|
||||
e2.(SemStoreExpr).getOperand() = e1 and delta = D::fromFloat(0)
|
||||
or
|
||||
e2.(SemAddOneExpr).getOperand() = e1 and delta = D::fromFloat(1)
|
||||
or
|
||||
e2.(SemSubOneExpr).getOperand() = e1 and delta = D::fromFloat(-1)
|
||||
or
|
||||
Lang::additionalValueFlowStep(e2, e1, delta)
|
||||
or
|
||||
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
|
||||
D::fromInt(x.(SemConstantIntegerExpr).getIntValue()) = delta
|
||||
)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
D::fromInt(-x.(SemConstantIntegerExpr).getIntValue()) = delta
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified expression's range information.
|
||||
*
|
||||
* Usually, this just `e.getSemType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedType(SemExpr e) {
|
||||
result = Lang::getAlternateType(e)
|
||||
or
|
||||
not exists(Lang::getAlternateType(e)) and result = e.getSemType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified source variable's range information.
|
||||
*
|
||||
* Usually, this just `e.getType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedTypeForSsaVariable(SemSsaVariable var) {
|
||||
result = Lang::getAlternateTypeForSsaVariable(var)
|
||||
or
|
||||
not exists(Lang::getAlternateTypeForSsaVariable(var)) and result = var.getType()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,488 +6,494 @@
|
||||
* three-valued domain `{negative, zero, positive}`.
|
||||
*/
|
||||
|
||||
private import RangeAnalysisStage
|
||||
private import SignAnalysisSpecific as Specific
|
||||
private import experimental.semmle.code.cpp.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import RangeUtils
|
||||
private import Sign
|
||||
|
||||
/**
|
||||
* An SSA definition for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*/
|
||||
abstract private class SignDef instanceof SemSsaVariable {
|
||||
final string toString() { result = super.toString() }
|
||||
module SignAnalysis<DeltaSig D, UtilSig<D> Utils> {
|
||||
/**
|
||||
* An SSA definition for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*/
|
||||
abstract private class SignDef instanceof SemSsaVariable {
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/** Gets the possible signs of this SSA definition. */
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is computed based on standard flow. */
|
||||
abstract private class FlowSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
|
||||
private class ExplicitSignDef extends FlowSignDef instanceof SemSsaExplicitUpdate {
|
||||
final override Sign getSign() { result = semExprSign(super.getSourceExpr()) }
|
||||
}
|
||||
|
||||
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
|
||||
private class PhiSignDef extends FlowSignDef instanceof SemSsaPhiNode {
|
||||
final override Sign getSign() {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(this, inp) and
|
||||
result = semSsaSign(inp, edge)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*
|
||||
* Concrete implementations extend one of the following subclasses:
|
||||
* - `ConstantSignExpr`, for expressions with a compile-time constant value.
|
||||
* - `FlowSignExpr`, for expressions whose sign can be computed from the signs of their operands.
|
||||
* - `CustomsignExpr`, for expressions whose sign can be computed by a language-specific
|
||||
* implementation.
|
||||
*
|
||||
* If the same expression matches more than one of the above subclasses, the sign is computed as
|
||||
* follows:
|
||||
* - The sign of a `ConstantSignExpr` is computed solely from `ConstantSignExpr.getSign()`,
|
||||
* regardless of any other subclasses.
|
||||
* - If a non-`ConstantSignExpr` expression matches exactly one of `FlowSignExpr` or
|
||||
* `CustomSignExpr`, the sign is computed by that class' `getSign()` predicate.
|
||||
* - If a non-`ConstantSignExpr` expression matches both `FlowSignExpr` and `CustomSignExpr`, the
|
||||
* sign is the _intersection_ of the signs of those two classes' `getSign()` predicates. Thus,
|
||||
* both classes have the opportunity to _restrict_ the set of possible signs, not to generate new
|
||||
* possible signs.
|
||||
* - If an expression does not match any of the three subclasses, then it can have any sign.
|
||||
*
|
||||
* Note that the `getSign()` predicate is introduced only in subclasses of `SignExpr`.
|
||||
*/
|
||||
abstract class SignExpr instanceof SemExpr {
|
||||
SignExpr() { not Specific::ignoreExprSign(this) }
|
||||
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
/** An expression whose sign is determined by its constant numeric value. */
|
||||
private class ConstantSignExpr extends SignExpr {
|
||||
ConstantSignExpr() {
|
||||
this instanceof SemConstantIntegerExpr or
|
||||
exists(this.(SemNumericLiteralExpr).getApproximateFloatValue())
|
||||
/** Gets the possible signs of this SSA definition. */
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
final override Sign getSign() {
|
||||
exists(int i | this.(SemConstantIntegerExpr).getIntValue() = i |
|
||||
i < 0 and result = TNeg()
|
||||
/** An SSA definition whose sign is computed based on standard flow. */
|
||||
abstract private class FlowSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
|
||||
private class ExplicitSignDef extends FlowSignDef instanceof SemSsaExplicitUpdate {
|
||||
final override Sign getSign() { result = semExprSign(super.getSourceExpr()) }
|
||||
}
|
||||
|
||||
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
|
||||
private class PhiSignDef extends FlowSignDef instanceof SemSsaPhiNode {
|
||||
final override Sign getSign() {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(this, inp) and
|
||||
result = semSsaSign(inp, edge)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An SSA definition whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignDef extends SignDef {
|
||||
abstract override Sign getSign();
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression for which the analysis can compute the sign.
|
||||
*
|
||||
* The actual computation of the sign is done in an override of the `getSign()` predicate. The
|
||||
* charpred of any subclass must _not_ invoke `getSign()`, directly or indirectly. This ensures
|
||||
* that the charpred does not introduce negative recursion. The `getSign()` predicate may be
|
||||
* recursive.
|
||||
*
|
||||
* Concrete implementations extend one of the following subclasses:
|
||||
* - `ConstantSignExpr`, for expressions with a compile-time constant value.
|
||||
* - `FlowSignExpr`, for expressions whose sign can be computed from the signs of their operands.
|
||||
* - `CustomsignExpr`, for expressions whose sign can be computed by a language-specific
|
||||
* implementation.
|
||||
*
|
||||
* If the same expression matches more than one of the above subclasses, the sign is computed as
|
||||
* follows:
|
||||
* - The sign of a `ConstantSignExpr` is computed solely from `ConstantSignExpr.getSign()`,
|
||||
* regardless of any other subclasses.
|
||||
* - If a non-`ConstantSignExpr` expression matches exactly one of `FlowSignExpr` or
|
||||
* `CustomSignExpr`, the sign is computed by that class' `getSign()` predicate.
|
||||
* - If a non-`ConstantSignExpr` expression matches both `FlowSignExpr` and `CustomSignExpr`, the
|
||||
* sign is the _intersection_ of the signs of those two classes' `getSign()` predicates. Thus,
|
||||
* both classes have the opportunity to _restrict_ the set of possible signs, not to generate new
|
||||
* possible signs.
|
||||
* - If an expression does not match any of the three subclasses, then it can have any sign.
|
||||
*
|
||||
* Note that the `getSign()` predicate is introduced only in subclasses of `SignExpr`.
|
||||
*/
|
||||
abstract class SignExpr instanceof SemExpr {
|
||||
SignExpr() { not Specific::ignoreExprSign(this) }
|
||||
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
abstract Sign getSign();
|
||||
}
|
||||
|
||||
/** An expression whose sign is determined by its constant numeric value. */
|
||||
private class ConstantSignExpr extends SignExpr {
|
||||
ConstantSignExpr() {
|
||||
this instanceof SemConstantIntegerExpr or
|
||||
exists(this.(SemNumericLiteralExpr).getApproximateFloatValue())
|
||||
}
|
||||
|
||||
final override Sign getSign() {
|
||||
exists(int i | this.(SemConstantIntegerExpr).getIntValue() = i |
|
||||
i < 0 and result = TNeg()
|
||||
or
|
||||
i = 0 and result = TZero()
|
||||
or
|
||||
i > 0 and result = TPos()
|
||||
)
|
||||
or
|
||||
i = 0 and result = TZero()
|
||||
or
|
||||
i > 0 and result = TPos()
|
||||
)
|
||||
or
|
||||
not exists(this.(SemConstantIntegerExpr).getIntValue()) and
|
||||
exists(float f | f = this.(SemNumericLiteralExpr).getApproximateFloatValue() |
|
||||
f < 0 and result = TNeg()
|
||||
or
|
||||
f = 0 and result = TZero()
|
||||
or
|
||||
f > 0 and result = TPos()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract private class NonConstantSignExpr extends SignExpr {
|
||||
NonConstantSignExpr() { not this instanceof ConstantSignExpr }
|
||||
|
||||
final override Sign getSign() {
|
||||
// The result is the _intersection_ of the signs computed from flow and by the language.
|
||||
(result = this.(FlowSignExpr).getSignRestriction() or not this instanceof FlowSignExpr) and
|
||||
(result = this.(CustomSignExpr).getSignRestriction() or not this instanceof CustomSignExpr)
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression whose sign is computed from the signs of its operands. */
|
||||
abstract private class FlowSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
/** An expression whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
/** An expression whose sign is unknown. */
|
||||
private class UnknownSignExpr extends SignExpr {
|
||||
UnknownSignExpr() {
|
||||
not this instanceof FlowSignExpr and
|
||||
not this instanceof CustomSignExpr and
|
||||
not this instanceof ConstantSignExpr and
|
||||
(
|
||||
// Only track numeric types.
|
||||
getTrackedType(this) instanceof SemNumericType
|
||||
or
|
||||
// Unless the language says to track this expression anyway.
|
||||
Specific::trackUnknownNonNumericExpr(this)
|
||||
)
|
||||
not exists(this.(SemConstantIntegerExpr).getIntValue()) and
|
||||
exists(float f | f = this.(SemNumericLiteralExpr).getApproximateFloatValue() |
|
||||
f < 0 and result = TNeg()
|
||||
or
|
||||
f = 0 and result = TZero()
|
||||
or
|
||||
f > 0 and result = TPos()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
final override Sign getSign() { semAnySign(result) }
|
||||
}
|
||||
abstract private class NonConstantSignExpr extends SignExpr {
|
||||
NonConstantSignExpr() { not this instanceof ConstantSignExpr }
|
||||
|
||||
/**
|
||||
* A `Load` expression whose sign is computed from the sign of its SSA definition, restricted by
|
||||
* inference from any intervening guards.
|
||||
*/
|
||||
class UseSignExpr extends FlowSignExpr {
|
||||
SemSsaVariable v;
|
||||
|
||||
UseSignExpr() { v.getAUse() = this }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
// Propagate via SSA
|
||||
// Propagate the sign from the def of `v`, incorporating any inference from guards.
|
||||
result = semSsaSign(v, any(SemSsaReadPositionBlock bb | bb.getAnExpr() = this))
|
||||
or
|
||||
// No block for this read. Just use the sign of the def.
|
||||
// REVIEW: How can this happen?
|
||||
not exists(SemSsaReadPositionBlock bb | bb.getAnExpr() = this) and
|
||||
result = semSsaDefSign(v)
|
||||
final override Sign getSign() {
|
||||
// The result is the _intersection_ of the signs computed from flow and by the language.
|
||||
(result = this.(FlowSignExpr).getSignRestriction() or not this instanceof FlowSignExpr) and
|
||||
(result = this.(CustomSignExpr).getSignRestriction() or not this instanceof CustomSignExpr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A binary expression whose sign is computed from the signs of its operands. */
|
||||
private class BinarySignExpr extends FlowSignExpr {
|
||||
SemBinaryExpr binary;
|
||||
/** An expression whose sign is computed from the signs of its operands. */
|
||||
abstract private class FlowSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
BinarySignExpr() { binary = this }
|
||||
/** An expression whose sign is computed by a language-specific implementation. */
|
||||
abstract class CustomSignExpr extends NonConstantSignExpr {
|
||||
abstract Sign getSignRestriction();
|
||||
}
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
exists(SemExpr left, SemExpr right |
|
||||
binaryExprOperands(binary, left, right) and
|
||||
/** An expression whose sign is unknown. */
|
||||
private class UnknownSignExpr extends SignExpr {
|
||||
UnknownSignExpr() {
|
||||
not this instanceof FlowSignExpr and
|
||||
not this instanceof CustomSignExpr and
|
||||
not this instanceof ConstantSignExpr and
|
||||
(
|
||||
// Only track numeric types.
|
||||
Utils::getTrackedType(this) instanceof SemNumericType
|
||||
or
|
||||
// Unless the language says to track this expression anyway.
|
||||
Specific::trackUnknownNonNumericExpr(this)
|
||||
)
|
||||
}
|
||||
|
||||
final override Sign getSign() { semAnySign(result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Load` expression whose sign is computed from the sign of its SSA definition, restricted by
|
||||
* inference from any intervening guards.
|
||||
*/
|
||||
class UseSignExpr extends FlowSignExpr {
|
||||
SemSsaVariable v;
|
||||
|
||||
UseSignExpr() { v.getAUse() = this }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
// Propagate via SSA
|
||||
// Propagate the sign from the def of `v`, incorporating any inference from guards.
|
||||
result = semSsaSign(v, any(SemSsaReadPositionBlock bb | bb.getAnExpr() = this))
|
||||
or
|
||||
// No block for this read. Just use the sign of the def.
|
||||
// REVIEW: How can this happen?
|
||||
not exists(SemSsaReadPositionBlock bb | bb.getAnExpr() = this) and
|
||||
result = semSsaDefSign(v)
|
||||
}
|
||||
}
|
||||
|
||||
/** A binary expression whose sign is computed from the signs of its operands. */
|
||||
private class BinarySignExpr extends FlowSignExpr {
|
||||
SemBinaryExpr binary;
|
||||
|
||||
BinarySignExpr() { binary = this }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
exists(SemExpr left, SemExpr right |
|
||||
binaryExprOperands(binary, left, right) and
|
||||
result =
|
||||
semExprSign(pragma[only_bind_out](left))
|
||||
.applyBinaryOp(semExprSign(pragma[only_bind_out](right)), binary.getOpcode())
|
||||
)
|
||||
or
|
||||
exists(SemDivExpr div | div = binary |
|
||||
result = semExprSign(div.getLeftOperand()) and
|
||||
result != TZero() and
|
||||
div.getRightOperand().(SemFloatingPointLiteralExpr).getFloatValue() = 0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binaryExprOperands(SemBinaryExpr binary, SemExpr left, SemExpr right) {
|
||||
binary.getLeftOperand() = left and binary.getRightOperand() = right
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression.
|
||||
*/
|
||||
private class SemCastExpr instanceof SemUnaryExpr {
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
SemCastExpr() {
|
||||
this instanceof SemConvertExpr
|
||||
or
|
||||
this instanceof SemBoxExpr
|
||||
or
|
||||
this instanceof SemUnboxExpr
|
||||
}
|
||||
}
|
||||
|
||||
/** A unary expression whose sign is computed from the sign of its operand. */
|
||||
private class UnarySignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr unary;
|
||||
|
||||
UnarySignExpr() { unary = this and not this instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
result =
|
||||
semExprSign(pragma[only_bind_out](left))
|
||||
.applyBinaryOp(semExprSign(pragma[only_bind_out](right)), binary.getOpcode())
|
||||
)
|
||||
or
|
||||
exists(SemDivExpr div | div = binary |
|
||||
result = semExprSign(div.getLeftOperand()) and
|
||||
result != TZero() and
|
||||
div.getRightOperand().(SemFloatingPointLiteralExpr).getFloatValue() = 0
|
||||
semExprSign(pragma[only_bind_out](unary.getOperand())).applyUnaryOp(unary.getOpcode())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression, whose sign is computed based on
|
||||
* the sign of its operand and the source and destination types.
|
||||
*/
|
||||
abstract private class CastSignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr cast;
|
||||
|
||||
CastSignExpr() { cast = this and cast instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() { result = semExprSign(cast.getOperand()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert` expression.
|
||||
*/
|
||||
private class ConvertSignExpr extends CastSignExpr {
|
||||
override SemConvertExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Box` expression.
|
||||
*/
|
||||
private class BoxSignExpr extends CastSignExpr {
|
||||
override SemBoxExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Unbox` expression.
|
||||
*/
|
||||
private class UnboxSignExpr extends CastSignExpr {
|
||||
override SemUnboxExpr cast;
|
||||
|
||||
UnboxSignExpr() {
|
||||
exists(SemType fromType | fromType = Utils::getTrackedType(cast.getOperand()) |
|
||||
// Only numeric source types are handled here.
|
||||
fromType instanceof SemNumericType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate unknownSign(SemExpr e) { e instanceof UnknownSignExpr }
|
||||
|
||||
/**
|
||||
* Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate lowerBound(
|
||||
SemExpr lowerbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(lowerbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getLesserOperand() = lowerbound and
|
||||
comp.getGreaterOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getGreaterOperand() = lowerbound and
|
||||
comp.getLesserOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate binaryExprOperands(SemBinaryExpr binary, SemExpr left, SemExpr right) {
|
||||
binary.getLeftOperand() = left and binary.getRightOperand() = right
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression.
|
||||
*/
|
||||
private class SemCastExpr extends SemUnaryExpr {
|
||||
SemCastExpr() {
|
||||
this instanceof SemConvertExpr
|
||||
or
|
||||
this instanceof SemBoxExpr
|
||||
or
|
||||
this instanceof SemUnboxExpr
|
||||
}
|
||||
}
|
||||
|
||||
/** A unary expression whose sign is computed from the sign of its operand. */
|
||||
private class UnarySignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr unary;
|
||||
|
||||
UnarySignExpr() { unary = this and not this instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
result = semExprSign(pragma[only_bind_out](unary.getOperand())).applyUnaryOp(unary.getOpcode())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert`, `Box`, or `Unbox` expression, whose sign is computed based on
|
||||
* the sign of its operand and the source and destination types.
|
||||
*/
|
||||
abstract private class CastSignExpr extends FlowSignExpr {
|
||||
SemUnaryExpr cast;
|
||||
|
||||
CastSignExpr() { cast = this and cast instanceof SemCastExpr }
|
||||
|
||||
override Sign getSignRestriction() { result = semExprSign(cast.getOperand()) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Convert` expression.
|
||||
*/
|
||||
private class ConvertSignExpr extends CastSignExpr {
|
||||
override SemConvertExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Box` expression.
|
||||
*/
|
||||
private class BoxSignExpr extends CastSignExpr {
|
||||
override SemBoxExpr cast;
|
||||
}
|
||||
|
||||
/**
|
||||
* An `Unbox` expression.
|
||||
*/
|
||||
private class UnboxSignExpr extends CastSignExpr {
|
||||
override SemUnboxExpr cast;
|
||||
|
||||
UnboxSignExpr() {
|
||||
exists(SemType fromType | fromType = getTrackedType(cast.getOperand()) |
|
||||
// Only numeric source types are handled here.
|
||||
fromType instanceof SemNumericType
|
||||
/**
|
||||
* Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate upperBound(
|
||||
SemExpr upperbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(upperbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getGreaterOperand() = upperbound and
|
||||
comp.getLesserOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getLesserOperand() = upperbound and
|
||||
comp.getGreaterOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate unknownSign(SemExpr e) { e instanceof UnknownSignExpr }
|
||||
/**
|
||||
* Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
|
||||
* restricted to only include bounds for which we might determine a sign. The
|
||||
* boolean `isEq` gives the polarity:
|
||||
* - `isEq = true` : `v = eqbound`
|
||||
* - `isEq = false` : `v != eqbound`
|
||||
*/
|
||||
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
|
||||
exists(SemGuard guard, boolean testIsTrue, boolean polarity |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue) and
|
||||
guard.isEquality(eqbound, Utils::semSsaRead(v, D::fromInt(0)), polarity) and
|
||||
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
|
||||
not unknownSign(eqbound)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `lowerbound` is a lower bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate lowerBound(
|
||||
SemExpr lowerbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(lowerbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getLesserOperand() = lowerbound and
|
||||
comp.getGreaterOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
|
||||
* order for `v` to be positive.
|
||||
*/
|
||||
private predicate posBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
|
||||
* order for `v` to be negative.
|
||||
*/
|
||||
private predicate negBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
|
||||
* can be zero.
|
||||
*/
|
||||
private predicate zeroBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, _)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be positive at `pos`. */
|
||||
private predicate posBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
posBound(bound, v, pos) and TPos() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be negative at `pos`. */
|
||||
private predicate negBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
negBound(bound, v, pos) and TNeg() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be zero at `pos`. */
|
||||
private predicate zeroBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) and TNeg() = semExprSign(bound)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getGreaterOperand() = lowerbound and
|
||||
comp.getLesserOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `upperbound` is an upper bound for `v` at `pos`. This is restricted
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate upperBound(
|
||||
SemExpr upperbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(upperbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getGreaterOperand() = upperbound and
|
||||
comp.getLesserOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
lowerBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getLesserOperand() = upperbound and
|
||||
comp.getGreaterOperand() = semSsaRead(v, 0) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
upperBound(bound, v, pos, _) and TPos() = semExprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, true) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, false) and TZero() != semExprSign(bound)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `eqbound` is an equality/inequality for `v` at `pos`. This is
|
||||
* restricted to only include bounds for which we might determine a sign. The
|
||||
* boolean `isEq` gives the polarity:
|
||||
* - `isEq = true` : `v = eqbound`
|
||||
* - `isEq = false` : `v != eqbound`
|
||||
*/
|
||||
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
|
||||
exists(SemGuard guard, boolean testIsTrue, boolean polarity |
|
||||
/**
|
||||
* Holds if there is a bound that might restrict whether `v` has the sign `s`
|
||||
* at `pos`.
|
||||
*/
|
||||
private predicate hasGuard(SemSsaVariable v, SemSsaReadPosition pos, Sign s) {
|
||||
s = TPos() and posBound(_, v, pos)
|
||||
or
|
||||
s = TNeg() and negBound(_, v, pos)
|
||||
or
|
||||
s = TZero() and zeroBound(_, v, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where the sign
|
||||
* might be ruled out by a guard.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign guardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue) and
|
||||
guard.isEquality(eqbound, semSsaRead(v, 0), polarity) and
|
||||
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
|
||||
not unknownSign(eqbound)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
|
||||
* order for `v` to be positive.
|
||||
*/
|
||||
private predicate posBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
|
||||
* order for `v` to be negative.
|
||||
*/
|
||||
private predicate negBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
|
||||
* can be zero.
|
||||
*/
|
||||
private predicate zeroBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, _)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be positive at `pos`. */
|
||||
private predicate posBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
posBound(bound, v, pos) and TPos() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be negative at `pos`. */
|
||||
private predicate negBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
negBound(bound, v, pos) and TNeg() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be zero at `pos`. */
|
||||
private predicate zeroBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) and TNeg() = semExprSign(bound)
|
||||
or
|
||||
lowerBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, _) and TPos() = semExprSign(bound)
|
||||
or
|
||||
upperBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, true) and TZero() = semExprSign(bound)
|
||||
or
|
||||
eqBound(bound, v, pos, false) and TZero() != semExprSign(bound)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a bound that might restrict whether `v` has the sign `s`
|
||||
* at `pos`.
|
||||
*/
|
||||
private predicate hasGuard(SemSsaVariable v, SemSsaReadPosition pos, Sign s) {
|
||||
s = TPos() and posBound(_, v, pos)
|
||||
or
|
||||
s = TNeg() and negBound(_, v, pos)
|
||||
or
|
||||
s = TZero() and zeroBound(_, v, pos)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where the sign
|
||||
* might be ruled out by a guard.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign guardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where no guard
|
||||
* can rule it out.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign unguardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
not hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at read position `pos`, where a guard could have
|
||||
* ruled out the sign but does not.
|
||||
* This does not check that the definition of `v` also allows the sign.
|
||||
*/
|
||||
private Sign guardedSsaSignOk(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = TPos() and
|
||||
forex(SemExpr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TNeg() and
|
||||
forex(SemExpr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TZero() and
|
||||
forex(SemExpr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v` at `pos`. */
|
||||
private Sign semSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = unguardedSsaSign(v, pos)
|
||||
or
|
||||
result = guardedSsaSign(v, pos) and
|
||||
result = guardedSsaSignOk(v, pos)
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v`. */
|
||||
pragma[nomagic]
|
||||
Sign semSsaDefSign(SemSsaVariable v) { result = v.(SignDef).getSign() }
|
||||
|
||||
/** Gets a possible sign for `e`. */
|
||||
cached
|
||||
Sign semExprSign(SemExpr e) {
|
||||
exists(Sign s | s = e.(SignExpr).getSign() |
|
||||
if
|
||||
getTrackedType(e) instanceof SemUnsignedIntegerType and
|
||||
s = TNeg() and
|
||||
not Specific::ignoreTypeRestrictions(e)
|
||||
then result = TPos()
|
||||
else result = s
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy predicate that holds for any sign. This is added to improve readability
|
||||
* of cases where the sign is unrestricted.
|
||||
*/
|
||||
predicate semAnySign(Sign s) { any() }
|
||||
|
||||
/** Holds if `e` can be positive and cannot be negative. */
|
||||
predicate semPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg()
|
||||
}
|
||||
|
||||
/** Holds if `e` can be negative and cannot be positive. */
|
||||
predicate semNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly positive. */
|
||||
predicate semStrictlyPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TZero()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly negative. */
|
||||
predicate semStrictlyNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TZero()
|
||||
hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at `pos` based on its definition, where no guard
|
||||
* can rule it out.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign unguardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
not hasGuard(v, pos, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a possible sign of `v` at read position `pos`, where a guard could have
|
||||
* ruled out the sign but does not.
|
||||
* This does not check that the definition of `v` also allows the sign.
|
||||
*/
|
||||
private Sign guardedSsaSignOk(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = TPos() and
|
||||
forex(SemExpr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TNeg() and
|
||||
forex(SemExpr bound | negBound(bound, v, pos) | negBoundOk(bound, v, pos))
|
||||
or
|
||||
result = TZero() and
|
||||
forex(SemExpr bound | zeroBound(bound, v, pos) | zeroBoundOk(bound, v, pos))
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v` at `pos`. */
|
||||
private Sign semSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = unguardedSsaSign(v, pos)
|
||||
or
|
||||
result = guardedSsaSign(v, pos) and
|
||||
result = guardedSsaSignOk(v, pos)
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v`. */
|
||||
pragma[nomagic]
|
||||
Sign semSsaDefSign(SemSsaVariable v) { result = v.(SignDef).getSign() }
|
||||
|
||||
/** Gets a possible sign for `e`. */
|
||||
cached
|
||||
Sign semExprSign(SemExpr e) {
|
||||
exists(Sign s | s = e.(SignExpr).getSign() |
|
||||
if
|
||||
Utils::getTrackedType(e) instanceof SemUnsignedIntegerType and
|
||||
s = TNeg() and
|
||||
not Specific::ignoreTypeRestrictions(e)
|
||||
then result = TPos()
|
||||
else result = s
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dummy predicate that holds for any sign. This is added to improve readability
|
||||
* of cases where the sign is unrestricted.
|
||||
*/
|
||||
predicate semAnySign(Sign s) { any() }
|
||||
|
||||
/** Holds if `e` can be positive and cannot be negative. */
|
||||
predicate semPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg()
|
||||
}
|
||||
|
||||
/** Holds if `e` can be negative and cannot be positive. */
|
||||
predicate semNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly positive. */
|
||||
predicate semStrictlyPositive(SemExpr e) {
|
||||
semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TZero()
|
||||
}
|
||||
|
||||
/** Holds if `e` is strictly negative. */
|
||||
predicate semStrictlyNegative(SemExpr e) {
|
||||
semExprSign(e) = TNeg() and
|
||||
not semExprSign(e) = TPos() and
|
||||
not semExprSign(e) = TZero()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.5.1-dev
|
||||
version: 0.5.2-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.5.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `cpp/no-space-for-terminator` and `cpp/uncontrolled-allocation-size` queries have been enhanced with heuristic detection of allocations. These queries now find more results.
|
||||
|
||||
## 0.5.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -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(), "|") + ")\"")
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.5.1
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `cpp/no-space-for-terminator` and `cpp/uncontrolled-allocation-size` queries have been enhanced with heuristic detection of allocations. These queries now find more results.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.0
|
||||
lastReleaseVersion: 0.5.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.5.1-dev
|
||||
version: 0.5.2-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -207,34 +207,35 @@ bad_asts.cpp:
|
||||
# 27| Type = [SpecifiedType] const Point
|
||||
# 27| ValueCategory = lvalue
|
||||
# 28| getStmt(1): [ReturnStmt] return ...
|
||||
# 30| [TopLevelFunction] void Bad::errorExpr()
|
||||
# 30| <params>:
|
||||
# 30| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 31| getStmt(0): [DeclStmt] declaration
|
||||
# 31| getDeclarationEntry(0): [VariableDeclarationEntry] definition of intref
|
||||
# 31| Type = [LValueReferenceType] int &
|
||||
# 31| getVariable().getInitializer(): [Initializer] initializer for intref
|
||||
# 31| getExpr(): [ErrorExpr] <error expr>
|
||||
# 31| Type = [ErroneousType] error
|
||||
# 31| ValueCategory = prvalue
|
||||
# 32| getStmt(1): [DeclStmt] declaration
|
||||
# 32| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
|
||||
# 32| Type = [IntType] int
|
||||
# 32| getVariable().getInitializer(): [Initializer] initializer for x
|
||||
# 32| getExpr(): [ErrorExpr] <error expr>
|
||||
# 32| Type = [ErroneousType] error
|
||||
# 32| ValueCategory = prvalue
|
||||
# 33| getStmt(2): [ExprStmt] ExprStmt
|
||||
# 33| getExpr(): [AssignExpr] ... = ...
|
||||
# 33| Type = [IntType] int
|
||||
# 33| ValueCategory = lvalue
|
||||
# 33| getLValue(): [VariableAccess] x
|
||||
# 33| Type = [IntType] int
|
||||
# 33| ValueCategory = lvalue
|
||||
# 33| getRValue(): [ErrorExpr] <error expr>
|
||||
# 33| Type = [ErroneousType] error
|
||||
# 33| ValueCategory = prvalue(load)
|
||||
# 34| getStmt(3): [ReturnStmt] return ...
|
||||
bad_stmts.cpp:
|
||||
# 5| [TopLevelFunction] void Bad::errorExpr()
|
||||
# 5| <params>:
|
||||
# 5| getEntryPoint(): [BlockStmt] { ... }
|
||||
# 6| getStmt(0): [DeclStmt] declaration
|
||||
# 6| getDeclarationEntry(0): [VariableDeclarationEntry] definition of intref
|
||||
# 6| Type = [LValueReferenceType] int &
|
||||
# 6| getVariable().getInitializer(): [Initializer] initializer for intref
|
||||
# 6| getExpr(): [ErrorExpr] <error expr>
|
||||
# 6| Type = [ErroneousType] error
|
||||
# 6| ValueCategory = prvalue
|
||||
# 7| getStmt(1): [DeclStmt] declaration
|
||||
# 7| getDeclarationEntry(0): [VariableDeclarationEntry] definition of x
|
||||
# 7| Type = [IntType] int
|
||||
# 7| getVariable().getInitializer(): [Initializer] initializer for x
|
||||
# 7| getExpr(): [ErrorExpr] <error expr>
|
||||
# 7| Type = [ErroneousType] error
|
||||
# 7| ValueCategory = prvalue
|
||||
# 8| getStmt(2): [ExprStmt] ExprStmt
|
||||
# 8| getExpr(): [AssignExpr] ... = ...
|
||||
# 8| Type = [IntType] int
|
||||
# 8| ValueCategory = lvalue
|
||||
# 8| getLValue(): [VariableAccess] x
|
||||
# 8| Type = [IntType] int
|
||||
# 8| ValueCategory = lvalue
|
||||
# 8| getRValue(): [ErrorExpr] <error expr>
|
||||
# 8| Type = [ErroneousType] error
|
||||
# 8| ValueCategory = prvalue(load)
|
||||
# 9| getStmt(3): [ReturnStmt] return ...
|
||||
clang.cpp:
|
||||
# 5| [TopLevelFunction] int* globalIntAddress()
|
||||
# 5| <params>:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// semmle-extractor-options: -std=c++17 --expect_errors
|
||||
// semmle-extractor-options: -std=c++17
|
||||
|
||||
// Test cases that illustrate known bad ASTs that we have to work around in IR generation.
|
||||
namespace Bad {
|
||||
@@ -26,10 +26,4 @@ namespace Bad {
|
||||
void CallCopyConstructor(const Point& a) {
|
||||
Point b = a; // Copy constructor contains literal expressions with no values.
|
||||
}
|
||||
|
||||
void errorExpr() {
|
||||
int &intref = 0;
|
||||
int x = 0[0];
|
||||
x = 1[1];
|
||||
}
|
||||
}
|
||||
|
||||
10
cpp/ql/test/library-tests/ir/ir/bad_stmts.cpp
Normal file
10
cpp/ql/test/library-tests/ir/ir/bad_stmts.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// semmle-extractor-options: -std=c++17 --expect_errors
|
||||
|
||||
// Test cases that illustrate known bad ASTs that we have to work around in IR generation.
|
||||
namespace Bad {
|
||||
void errorExpr() {
|
||||
int &intref = 0;
|
||||
int x = 0[0];
|
||||
x = 1[1];
|
||||
}
|
||||
}
|
||||
@@ -98,17 +98,17 @@
|
||||
| bad_asts.cpp:27:15:27:15 | StoreValue | r27_6 |
|
||||
| bad_asts.cpp:27:15:27:15 | Unary | r27_3 |
|
||||
| bad_asts.cpp:27:15:27:15 | Unary | r27_4 |
|
||||
| bad_asts.cpp:30:8:30:16 | ChiPartial | partial:m30_3 |
|
||||
| bad_asts.cpp:30:8:30:16 | ChiTotal | total:m30_2 |
|
||||
| bad_asts.cpp:30:8:30:16 | SideEffect | m30_3 |
|
||||
| bad_asts.cpp:31:10:31:15 | Address | &:r31_1 |
|
||||
| bad_asts.cpp:31:18:31:19 | StoreValue | r31_2 |
|
||||
| bad_asts.cpp:32:9:32:9 | Address | &:r32_1 |
|
||||
| bad_asts.cpp:32:12:32:16 | StoreValue | r32_2 |
|
||||
| bad_asts.cpp:33:5:33:5 | Address | &:r33_3 |
|
||||
| bad_asts.cpp:33:5:33:12 | Address | &:r33_1 |
|
||||
| bad_asts.cpp:33:5:33:12 | Load | ~m30_4 |
|
||||
| bad_asts.cpp:33:5:33:12 | StoreValue | r33_2 |
|
||||
| bad_stmts.cpp:5:8:5:16 | ChiPartial | partial:m5_3 |
|
||||
| bad_stmts.cpp:5:8:5:16 | ChiTotal | total:m5_2 |
|
||||
| bad_stmts.cpp:5:8:5:16 | SideEffect | m5_3 |
|
||||
| bad_stmts.cpp:6:10:6:15 | Address | &:r6_1 |
|
||||
| bad_stmts.cpp:6:18:6:19 | StoreValue | r6_2 |
|
||||
| bad_stmts.cpp:7:9:7:9 | Address | &:r7_1 |
|
||||
| bad_stmts.cpp:7:12:7:16 | StoreValue | r7_2 |
|
||||
| bad_stmts.cpp:8:5:8:5 | Address | &:r8_3 |
|
||||
| bad_stmts.cpp:8:5:8:12 | Address | &:r8_1 |
|
||||
| bad_stmts.cpp:8:5:8:12 | Load | ~m5_4 |
|
||||
| bad_stmts.cpp:8:5:8:12 | StoreValue | r8_2 |
|
||||
| clang.cpp:5:6:5:21 | Address | &:r5_5 |
|
||||
| clang.cpp:5:6:5:21 | ChiPartial | partial:m5_3 |
|
||||
| clang.cpp:5:6:5:21 | ChiTotal | total:m5_2 |
|
||||
|
||||
@@ -120,25 +120,26 @@ bad_asts.cpp:
|
||||
# 26| v26_10(void) = AliasedUse : ~m?
|
||||
# 26| v26_11(void) = ExitFunction :
|
||||
|
||||
# 30| void Bad::errorExpr()
|
||||
# 30| Block 0
|
||||
# 30| v30_1(void) = EnterFunction :
|
||||
# 30| mu30_2(unknown) = AliasedDefinition :
|
||||
# 30| mu30_3(unknown) = InitializeNonLocal :
|
||||
# 31| r31_1(glval<int &>) = VariableAddress[intref] :
|
||||
# 31| r31_2(error) = Error :
|
||||
# 31| mu31_3(int &) = Store[intref] : &:r31_1, r31_2
|
||||
# 32| r32_1(glval<int>) = VariableAddress[x] :
|
||||
# 32| r32_2(error) = Error :
|
||||
# 32| mu32_3(int) = Store[x] : &:r32_1, r32_2
|
||||
# 33| r33_1(glval<error>) = Error :
|
||||
# 33| r33_2(error) = Load[?] : &:r33_1, ~m?
|
||||
# 33| r33_3(glval<int>) = VariableAddress[x] :
|
||||
# 33| mu33_4(int) = Store[x] : &:r33_3, r33_2
|
||||
# 34| v34_1(void) = NoOp :
|
||||
# 30| v30_4(void) = ReturnVoid :
|
||||
# 30| v30_5(void) = AliasedUse : ~m?
|
||||
# 30| v30_6(void) = ExitFunction :
|
||||
bad_stmts.cpp:
|
||||
# 5| void Bad::errorExpr()
|
||||
# 5| Block 0
|
||||
# 5| v5_1(void) = EnterFunction :
|
||||
# 5| mu5_2(unknown) = AliasedDefinition :
|
||||
# 5| mu5_3(unknown) = InitializeNonLocal :
|
||||
# 6| r6_1(glval<int &>) = VariableAddress[intref] :
|
||||
# 6| r6_2(error) = Error :
|
||||
# 6| mu6_3(int &) = Store[intref] : &:r6_1, r6_2
|
||||
# 7| r7_1(glval<int>) = VariableAddress[x] :
|
||||
# 7| r7_2(error) = Error :
|
||||
# 7| mu7_3(int) = Store[x] : &:r7_1, r7_2
|
||||
# 8| r8_1(glval<error>) = Error :
|
||||
# 8| r8_2(error) = Load[?] : &:r8_1, ~m?
|
||||
# 8| r8_3(glval<int>) = VariableAddress[x] :
|
||||
# 8| mu8_4(int) = Store[x] : &:r8_3, r8_2
|
||||
# 9| v9_1(void) = NoOp :
|
||||
# 5| v5_4(void) = ReturnVoid :
|
||||
# 5| v5_5(void) = AliasedUse : ~m?
|
||||
# 5| v5_6(void) = ExitFunction :
|
||||
|
||||
clang.cpp:
|
||||
# 5| int* globalIntAddress()
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import cpp
|
||||
import experimental.semmle.code.cpp.semantic.analysis.ModulusAnalysis
|
||||
import experimental.semmle.code.cpp.semantic.Semantic
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeUtils
|
||||
import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysisSpecific
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysis
|
||||
import semmle.code.cpp.ir.IR as IR
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
module ModulusAnalysisInstantiated =
|
||||
ModulusAnalysis<FloatDelta, Bounds, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
class ModulusAnalysisTest extends InlineExpectationsTest {
|
||||
ModulusAnalysisTest() { this = "ModulusAnalysisTest" }
|
||||
|
||||
@@ -23,7 +30,7 @@ class ModulusAnalysisTest extends InlineExpectationsTest {
|
||||
|
||||
private string getAModString(SemExpr e) {
|
||||
exists(SemBound b, int delta, int mod |
|
||||
semExprModulus(e, b, delta, mod) and
|
||||
ModulusAnalysisInstantiated::semExprModulus(e, b, delta, mod) and
|
||||
result = b.toString() + "," + delta.toString() + "," + mod.toString() and
|
||||
not (delta = 0 and mod = 0)
|
||||
)
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
import cpp
|
||||
import experimental.semmle.code.cpp.semantic.analysis.SignAnalysisCommon
|
||||
import experimental.semmle.code.cpp.semantic.Semantic
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeUtils
|
||||
import experimental.semmle.code.cpp.semantic.analysis.FloatDelta
|
||||
import experimental.semmle.code.cpp.semantic.analysis.RangeAnalysisSpecific
|
||||
import semmle.code.cpp.ir.IR as IR
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
module SignAnalysisInstantiated = SignAnalysis<FloatDelta, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
|
||||
class SignAnalysisTest extends InlineExpectationsTest {
|
||||
SignAnalysisTest() { this = "SignAnalysisTest" }
|
||||
|
||||
@@ -21,4 +26,6 @@ class SignAnalysisTest extends InlineExpectationsTest {
|
||||
}
|
||||
}
|
||||
|
||||
private string getASignString(SemExpr e) { result = strictconcat(semExprSign(e).toString(), "") }
|
||||
private string getASignString(SemExpr e) {
|
||||
result = strictconcat(SignAnalysisInstantiated::semExprSign(e).toString(), "")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
struct Foo
|
||||
struct Allocators
|
||||
{
|
||||
Foo(int x, int y) : m_x(x), m_y(y) {}
|
||||
~Foo() {m_x = m_y = 0;}
|
||||
Allocators(int x, int y) : m_x(x), m_y(y) {}
|
||||
~Allocators() {m_x = m_y = 0;}
|
||||
|
||||
// NB: In Microsoft mode, size_t is predeclared.
|
||||
static void* operator new(size_t sz, int z, int w) { return nullptr; }
|
||||
@@ -13,7 +13,7 @@ struct Foo
|
||||
|
||||
int main()
|
||||
{
|
||||
auto foo = new(11, 22) Foo(33, 44);
|
||||
auto foo = new(11, 22) Allocators(33, 44);
|
||||
delete foo;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
struct Foo {
|
||||
~Foo();
|
||||
struct ArrayDelete {
|
||||
~ArrayDelete();
|
||||
};
|
||||
|
||||
void f() {
|
||||
delete[] (Foo*)nullptr;
|
||||
delete[] (ArrayDelete*)nullptr;
|
||||
}
|
||||
|
||||
@@ -74,8 +74,8 @@ argHasPostUpdate
|
||||
| ir.cpp:625:5:625:5 | s | ArgumentNode is missing PostUpdateNode. |
|
||||
postWithInFlow
|
||||
| VacuousDestructorCall.cpp:10:22:10:22 | i [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:11:4:13 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:17:4:19 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:18:4:20 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:24:4:26 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| assignexpr.cpp:9:4:9:4 | i [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| builtin.c:34:23:34:31 | staticint [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| builtin.c:39:37:39:45 | carry_out [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -1483,17 +1483,17 @@ postWithInFlow
|
||||
| aggregateinitializer.c:3:6:3:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aggregateinitializer.c:3:11:3:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| aggregateinitializer.c:3:11:3:27 | PointerAdd [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:23:3:28 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:31:3:36 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:11:4:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:11:4:13 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:17:4:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:17:4:19 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:30:3:35 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:3:38:3:43 | FieldAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:18:4:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:18:4:20 | m_x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:24:4:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:4:24:4:26 | m_y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:7:56:7:70 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:8:16:10 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:36 | Call [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:36 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:36 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:43 | Call [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:43 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:16:14:16:43 | new [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| allocators.cpp:18:1:18:1 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| assignexpr.cpp:9:4:9:4 | i [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| bad_asts.cpp:10:7:10:23 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -168,4 +168,34 @@ int main(int argc, char **argv) {
|
||||
int i10 = (int) argv[1];
|
||||
printf((char *) i10);
|
||||
printWrapper((char *) i10);
|
||||
|
||||
// BAD: b value comes from argv
|
||||
{
|
||||
char b[64];
|
||||
char *bp = &b[0];
|
||||
char *t;
|
||||
if (0) {
|
||||
t = 0;
|
||||
} else {
|
||||
t = bp;
|
||||
}
|
||||
memcpy(t, argv[1] + 1, 1);
|
||||
printf(bp);
|
||||
printWrapper(bp);
|
||||
}
|
||||
|
||||
// BAD: b value comes from argv
|
||||
{
|
||||
char b[64];
|
||||
char *bp = &b[0];
|
||||
char *t;
|
||||
if (1) {
|
||||
t = ++bp;
|
||||
} else {
|
||||
t = 0;
|
||||
}
|
||||
memcpy(t, argv[1] + 1, 1);
|
||||
printf(bp);
|
||||
printWrapper(bp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,6 +260,30 @@ edges
|
||||
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
|
||||
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
|
||||
| argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | (const char *)... |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | (const char *)... |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | bp indirection |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | bp indirection |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp indirection |
|
||||
| argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp indirection |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | (const char *)... |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | (const char *)... |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | bp indirection |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | bp indirection |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp indirection |
|
||||
| argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp indirection |
|
||||
subpaths
|
||||
| argvLocal.c:102:15:102:16 | i1 indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | ReturnIndirection | argvLocal.c:102:15:102:16 | printWrapper output argument |
|
||||
| argvLocal.c:107:15:107:19 | access to array indirection | argvLocal.c:9:25:9:31 | *correct | argvLocal.c:9:25:9:31 | ReturnIndirection | argvLocal.c:107:15:107:19 | printWrapper output argument |
|
||||
@@ -396,6 +420,22 @@ nodes
|
||||
| argvLocal.c:170:15:170:26 | i10 indirection | semmle.label | i10 indirection |
|
||||
| argvLocal.c:170:24:170:26 | i10 | semmle.label | i10 |
|
||||
| argvLocal.c:170:24:170:26 | i10 | semmle.label | i10 |
|
||||
| argvLocal.c:182:13:182:16 | argv | semmle.label | argv |
|
||||
| argvLocal.c:182:13:182:16 | argv | semmle.label | argv |
|
||||
| argvLocal.c:183:10:183:11 | (const char *)... | semmle.label | (const char *)... |
|
||||
| argvLocal.c:183:10:183:11 | bp | semmle.label | bp |
|
||||
| argvLocal.c:183:10:183:11 | bp indirection | semmle.label | bp indirection |
|
||||
| argvLocal.c:184:16:184:17 | bp | semmle.label | bp |
|
||||
| argvLocal.c:184:16:184:17 | bp | semmle.label | bp |
|
||||
| argvLocal.c:184:16:184:17 | bp indirection | semmle.label | bp indirection |
|
||||
| argvLocal.c:197:13:197:16 | argv | semmle.label | argv |
|
||||
| argvLocal.c:197:13:197:16 | argv | semmle.label | argv |
|
||||
| argvLocal.c:198:10:198:11 | (const char *)... | semmle.label | (const char *)... |
|
||||
| argvLocal.c:198:10:198:11 | bp | semmle.label | bp |
|
||||
| argvLocal.c:198:10:198:11 | bp indirection | semmle.label | bp indirection |
|
||||
| argvLocal.c:199:16:199:17 | bp | semmle.label | bp |
|
||||
| argvLocal.c:199:16:199:17 | bp | semmle.label | bp |
|
||||
| argvLocal.c:199:16:199:17 | bp indirection | semmle.label | bp indirection |
|
||||
#select
|
||||
| argvLocal.c:95:9:95:15 | access to array | argvLocal.c:95:9:95:12 | argv | argvLocal.c:95:9:95:15 | access to array | The value of this argument may come from $@ and is being used as a formatting argument to printf(format). | argvLocal.c:95:9:95:12 | argv | argv |
|
||||
| argvLocal.c:96:15:96:21 | access to array | argvLocal.c:96:15:96:18 | argv | argvLocal.c:96:15:96:21 | access to array | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format). | argvLocal.c:96:15:96:18 | argv | argv |
|
||||
@@ -425,3 +465,7 @@ nodes
|
||||
| argvLocal.c:165:15:165:17 | i91 | argvLocal.c:163:22:163:25 | argv | argvLocal.c:165:15:165:17 | i91 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format). | argvLocal.c:163:22:163:25 | argv | argv |
|
||||
| argvLocal.c:169:18:169:20 | i10 | argvLocal.c:168:18:168:21 | argv | argvLocal.c:169:18:169:20 | i10 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format). | argvLocal.c:168:18:168:21 | argv | argv |
|
||||
| argvLocal.c:170:24:170:26 | i10 | argvLocal.c:168:18:168:21 | argv | argvLocal.c:170:24:170:26 | i10 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format). | argvLocal.c:168:18:168:21 | argv | argv |
|
||||
| argvLocal.c:183:10:183:11 | bp | argvLocal.c:182:13:182:16 | argv | argvLocal.c:183:10:183:11 | bp | The value of this argument may come from $@ and is being used as a formatting argument to printf(format). | argvLocal.c:182:13:182:16 | argv | argv |
|
||||
| argvLocal.c:184:16:184:17 | bp | argvLocal.c:182:13:182:16 | argv | argvLocal.c:184:16:184:17 | bp | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format). | argvLocal.c:182:13:182:16 | argv | argv |
|
||||
| argvLocal.c:198:10:198:11 | bp | argvLocal.c:197:13:197:16 | argv | argvLocal.c:198:10:198:11 | bp | The value of this argument may come from $@ and is being used as a formatting argument to printf(format). | argvLocal.c:197:13:197:16 | argv | argv |
|
||||
| argvLocal.c:199:16:199:17 | bp | argvLocal.c:197:13:197:16 | argv | argvLocal.c:199:16:199:17 | bp | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(correct), which calls printf(format). | argvLocal.c:197:13:197:16 | argv | argv |
|
||||
|
||||
@@ -17,6 +17,24 @@ edges
|
||||
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
|
||||
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address |
|
||||
| test.cpp:38:25:38:42 | (const char *)... | test.cpp:42:14:42:20 | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address indirection |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:52:14:52:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:52:14:52:20 | address indirection |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:56:14:56:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:56:14:56:20 | address indirection |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:60:14:60:20 | address |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | test.cpp:60:14:60:20 | address indirection |
|
||||
subpaths
|
||||
nodes
|
||||
| test.cpp:16:25:16:30 | call to getenv | semmle.label | call to getenv |
|
||||
@@ -34,7 +52,21 @@ nodes
|
||||
| test.cpp:42:14:42:20 | address | semmle.label | address |
|
||||
| test.cpp:42:14:42:20 | address | semmle.label | address |
|
||||
| test.cpp:42:14:42:20 | address indirection | semmle.label | address indirection |
|
||||
| test.cpp:49:25:49:30 | call to getenv | semmle.label | call to getenv |
|
||||
| test.cpp:49:25:49:42 | (const char *)... | semmle.label | (const char *)... |
|
||||
| test.cpp:52:14:52:20 | address | semmle.label | address |
|
||||
| test.cpp:52:14:52:20 | address | semmle.label | address |
|
||||
| test.cpp:52:14:52:20 | address indirection | semmle.label | address indirection |
|
||||
| test.cpp:56:14:56:20 | address | semmle.label | address |
|
||||
| test.cpp:56:14:56:20 | address | semmle.label | address |
|
||||
| test.cpp:56:14:56:20 | address indirection | semmle.label | address indirection |
|
||||
| test.cpp:60:14:60:20 | address | semmle.label | address |
|
||||
| test.cpp:60:14:60:20 | address | semmle.label | address |
|
||||
| test.cpp:60:14:60:20 | address indirection | semmle.label | address indirection |
|
||||
#select
|
||||
| test.cpp:20:7:20:12 | call to strcmp | test.cpp:16:25:16:30 | call to getenv | test.cpp:20:14:20:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:16:25:16:30 | call to getenv | call to getenv |
|
||||
| test.cpp:31:7:31:12 | call to strcmp | test.cpp:27:25:27:30 | call to getenv | test.cpp:31:14:31:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:27:25:27:30 | call to getenv | call to getenv |
|
||||
| test.cpp:42:7:42:12 | call to strcmp | test.cpp:38:25:38:30 | call to getenv | test.cpp:42:14:42:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:38:25:38:30 | call to getenv | call to getenv |
|
||||
| test.cpp:52:7:52:12 | call to strcmp | test.cpp:49:25:49:30 | call to getenv | test.cpp:52:14:52:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:49:25:49:30 | call to getenv | call to getenv |
|
||||
| test.cpp:56:7:56:12 | call to strcmp | test.cpp:49:25:49:30 | call to getenv | test.cpp:56:14:56:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:49:25:49:30 | call to getenv | call to getenv |
|
||||
| test.cpp:60:7:60:12 | call to strcmp | test.cpp:49:25:49:30 | call to getenv | test.cpp:60:14:60:20 | address | Untrusted input $@ might be vulnerable to a spoofing attack. | test.cpp:49:25:49:30 | call to getenv | call to getenv |
|
||||
|
||||
@@ -43,3 +43,27 @@ void processRequest3()
|
||||
isServer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void processRequest4()
|
||||
{
|
||||
const char *address = getenv("SERVERIP");
|
||||
bool cond = false;
|
||||
|
||||
if (strcmp(address, "127.0.0.1")) { cond = true; } // BAD
|
||||
if (strcmp(address, "127_0_0_1")) { cond = true; } // GOOD (not an IP)
|
||||
if (strcmp(address, "127.0.0")) { cond = true; } // GOOD (not an IP)
|
||||
if (strcmp(address, "127.0.0.0.1")) { cond = true; } // GOOD (not an IP)
|
||||
if (strcmp(address, "http://mycompany")) { cond = true; } // BAD
|
||||
if (strcmp(address, "http_//mycompany")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "htt://mycompany")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "httpp://mycompany")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "mycompany.com")) { cond = true; } // BAD
|
||||
if (strcmp(address, "mycompany_com")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "mycompany.c")) { cond = true; } // GOOD (not an address)
|
||||
if (strcmp(address, "mycompany.comm")) { cond = true; } // GOOD (not an address)
|
||||
|
||||
if (cond) {
|
||||
isServer = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Remove operators from the virtualizable type.
|
||||
compatibility: full
|
||||
@@ -92,7 +92,8 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
yield return Tuples.cil_parameter_out(pe);
|
||||
if (p.Attributes.HasFlag(ParameterAttributes.In))
|
||||
yield return Tuples.cil_parameter_in(pe);
|
||||
Attribute.Populate(Context, pe, p.GetCustomAttributes());
|
||||
foreach (var c in Attribute.Populate(Context, pe, p.GetCustomAttributes()))
|
||||
yield return c;
|
||||
}
|
||||
|
||||
yield return Tuples.metadata_handle(this, Context.Assembly, MetadataTokens.GetToken(handle));
|
||||
@@ -205,7 +206,8 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
yield return Tuples.cil_newslot(this);
|
||||
|
||||
// Populate attributes
|
||||
Attribute.Populate(Context, this, md.GetCustomAttributes());
|
||||
foreach (var c in Attribute.Populate(Context, this, md.GetCustomAttributes()))
|
||||
yield return c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.4.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.4.0
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.4.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.4.0
|
||||
lastReleaseVersion: 1.4.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.4.1-dev
|
||||
version: 1.4.2-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.4.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.4.0
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.4.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.4.0
|
||||
lastReleaseVersion: 1.4.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.4.1-dev
|
||||
version: 1.4.2-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
## 0.5.1
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Added library support for generic attributes (also for CIL extracted attributes).
|
||||
* `cil.ConstructedType::getName` was changed to include printing of the type arguments.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Attributes on methods in CIL are now extracted (Bugfix).
|
||||
* Support for `static virtual` and `static abstract` interface members.
|
||||
* Support for *operators* in interface definitions.
|
||||
* C# 11: Added support for the unsigned right shift `>>>` and unsigned right shift assignment `>>>=` operators.
|
||||
* Query id's have been aligned such that they are prefixed with `cs` instead of `csharp`.
|
||||
|
||||
## 0.5.0
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Query id's have been aligned such that they are prefixed with `cs` instead of `csharp`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* C# 11: Added support for the unsigned right shift `>>>` and unsigned right shift assignment `>>>=` operators.
|
||||
14
csharp/ql/lib/change-notes/released/0.5.1.md
Normal file
14
csharp/ql/lib/change-notes/released/0.5.1.md
Normal file
@@ -0,0 +1,14 @@
|
||||
## 0.5.1
|
||||
|
||||
### Major Analysis Improvements
|
||||
|
||||
* Added library support for generic attributes (also for CIL extracted attributes).
|
||||
* `cil.ConstructedType::getName` was changed to include printing of the type arguments.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Attributes on methods in CIL are now extracted (Bugfix).
|
||||
* Support for `static virtual` and `static abstract` interface members.
|
||||
* Support for *operators* in interface definitions.
|
||||
* C# 11: Added support for the unsigned right shift `>>>` and unsigned right shift assignment `>>>=` operators.
|
||||
* Query id's have been aligned such that they are prefixed with `cs` instead of `csharp`.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.0
|
||||
lastReleaseVersion: 0.5.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 0.5.1-dev
|
||||
version: 0.5.2-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -27,3 +27,19 @@ class Attribute extends Element, @cil_attribute {
|
||||
|
||||
override CS::Location getLocation() { result = getDeclaration().getLocation() }
|
||||
}
|
||||
|
||||
/** A generic attribute to a declaration. */
|
||||
class GenericAttribute extends Attribute {
|
||||
private ConstructedType type;
|
||||
|
||||
GenericAttribute() { type = this.getType() }
|
||||
|
||||
/** Gets the total number of type arguments. */
|
||||
int getNumberOfTypeArguments() { result = count(int i | cil_type_argument(type, i, _)) }
|
||||
|
||||
/** Gets the `i`th type argument, if any. */
|
||||
Type getTypeArgument(int i) { result = type.getTypeArgument(i) }
|
||||
|
||||
/** Get a type argument. */
|
||||
Type getATypeArgument() { result = this.getTypeArgument(_) }
|
||||
}
|
||||
|
||||
@@ -28,6 +28,12 @@ class ConstructedGeneric extends Generic, DotNet::ConstructedGeneric {
|
||||
final override Type getTypeArgument(int n) { cil_type_argument(this, n, result) }
|
||||
}
|
||||
|
||||
/** Gets the concatenation of the `getName()` of type arguments. */
|
||||
language[monotonicAggregates]
|
||||
private string getTypeArgumentsNames(ConstructedGeneric cg) {
|
||||
result = strictconcat(Type t, int i | t = cg.getTypeArgument(i) | t.getName(), "," order by i)
|
||||
}
|
||||
|
||||
/** An unbound generic type. */
|
||||
class UnboundGenericType extends UnboundGeneric, Type { }
|
||||
|
||||
@@ -41,6 +47,10 @@ class ConstructedType extends ConstructedGeneric, Type {
|
||||
override predicate isInterface() { this.getUnboundType().isInterface() }
|
||||
|
||||
override predicate isClass() { this.getUnboundType().isClass() }
|
||||
|
||||
final override string getName() {
|
||||
result = this.getUndecoratedName() + "<" + getTypeArgumentsNames(this) + ">"
|
||||
}
|
||||
}
|
||||
|
||||
/** A constructed generic method. */
|
||||
|
||||
@@ -37,8 +37,14 @@ class Attributable extends @attributable {
|
||||
}
|
||||
|
||||
private string getAttributeName(Attribute a) {
|
||||
exists(string type | type = a.getType().getName() |
|
||||
if type.matches("%Attribute") then result = type.prefix(type.length() - 9) else result = type
|
||||
exists(string type, string pattern |
|
||||
type = a.getType().getName() and pattern = "(.*)Attribute(<.*>)?"
|
||||
|
|
||||
type.regexpMatch(pattern) and
|
||||
result = concat(int i | i = [1, 2] | type.regexpCapture(pattern, i) order by i)
|
||||
or
|
||||
not type.regexpMatch(pattern) and
|
||||
result = type
|
||||
)
|
||||
}
|
||||
|
||||
@@ -99,6 +105,31 @@ class Attribute extends TopLevelExprParent, @attribute {
|
||||
override string getAPrimaryQlClass() { result = "Attribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic attribute, for example `[...]` on line 1 in
|
||||
*
|
||||
* ```csharp
|
||||
* [MyGenericAttribute<int>(0)]
|
||||
* public void SomeMethod(string s) { }
|
||||
* ```
|
||||
*/
|
||||
class GenericAttribute extends Attribute {
|
||||
private ConstructedClass type;
|
||||
|
||||
GenericAttribute() { type = this.getType() }
|
||||
|
||||
/** Gets the total number of type arguments. */
|
||||
int getNumberOfTypeArguments() { result = count(int i | type_arguments(_, i, type)) }
|
||||
|
||||
/** Gets the `i`th type argument, if any. */
|
||||
Type getTypeArgument(int i) { result = type.getTypeArgument(i) }
|
||||
|
||||
/** Get a type argument. */
|
||||
Type getATypeArgument() { result = this.getTypeArgument(_) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "GenericAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute with default kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
@@ -110,6 +141,17 @@ class DefaultAttribute extends Attribute, @attribute_default {
|
||||
override string getAPrimaryQlClass() { result = "DefaultAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic attribute with default kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
* [MyAttribute<string>(0)]
|
||||
* int SomeMethod() { return 1; }
|
||||
* ```
|
||||
*/
|
||||
class GenericDefaultAttribute extends GenericAttribute, DefaultAttribute {
|
||||
override string getAPrimaryQlClass() { result = "GenericDefaultAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute with return kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
@@ -123,6 +165,17 @@ class ReturnAttribute extends Attribute, @attribute_return {
|
||||
override string getAPrimaryQlClass() { result = "ReturnAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic attribute with return kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
* [return: MyAttribute<object>(0)]
|
||||
* int SomeMethod() { return 1; }
|
||||
* ```
|
||||
*/
|
||||
class GenericReturnAttribute extends GenericAttribute, ReturnAttribute {
|
||||
override string getAPrimaryQlClass() { result = "GenericReturnAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute with assembly kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
@@ -135,6 +188,16 @@ class AssemblyAttribute extends Attribute, @attribute_assembly {
|
||||
override string getAPrimaryQlClass() { result = "AssemblyAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic attribute with assembly kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
* [assembly: MyAttribute<string>(0)]
|
||||
* ```
|
||||
*/
|
||||
class GenericAssemblyAttribute extends GenericAttribute, AssemblyAttribute {
|
||||
override string getAPrimaryQlClass() { result = "GenericAssemblyAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An attribute with module kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
@@ -146,3 +209,13 @@ class ModuleAttribute extends Attribute, @attribute_module {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ModuleAttribute" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic attribute with module kind, for example `[...]` on line 1 in
|
||||
* ```csharp
|
||||
* [module: MyAttribute<string>(0)]
|
||||
* ```
|
||||
*/
|
||||
class GenericModuleAttribute extends GenericAttribute, ModuleAttribute {
|
||||
override string getAPrimaryQlClass() { result = "GenericModuleAttribute" }
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ pragma[nomagic]
|
||||
private Virtualizable getACompatibleInterfaceMemberAux(Virtualizable m) {
|
||||
result = getACompatibleInterfaceAccessor(m) or
|
||||
result = getACompatibleInterfaceIndexer(m) or
|
||||
result = getACompatibleInterfaceMethod(m)
|
||||
result = getACompatibleRelevantInterfaceMember(m)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -210,11 +210,13 @@ private predicate getACompatibleInterfaceIndexerAux(Indexer i, ValueOrRefType t)
|
||||
t = getAPossibleImplementor(i.getDeclaringType())
|
||||
}
|
||||
|
||||
private Method getACompatibleInterfaceMethod0(Method m, int i) {
|
||||
result = getAnInterfaceMethodCandidate(m) and
|
||||
private RelevantInterfaceMember getACompatibleRelevantInterfaceMember0(
|
||||
RelevantInterfaceMember m, int i
|
||||
) {
|
||||
result = getARelevantInterfaceMemberCandidate(m) and
|
||||
i = -1
|
||||
or
|
||||
result = getACompatibleInterfaceMethod0(m, i - 1) and
|
||||
result = getACompatibleRelevantInterfaceMember0(m, i - 1) and
|
||||
exists(Type t1, Type t2 |
|
||||
t1 = getArgumentOrReturnType(m, i) and
|
||||
t2 = getArgumentOrReturnType(result, i)
|
||||
@@ -223,32 +225,47 @@ private Method getACompatibleInterfaceMethod0(Method m, int i) {
|
||||
)
|
||||
}
|
||||
|
||||
private Method getACompatibleInterfaceMethod(Method m) {
|
||||
result = getACompatibleInterfaceMethod0(m, m.getNumberOfParameters())
|
||||
/**
|
||||
* A class of callables relevant for interface member compatibility.
|
||||
*/
|
||||
private class RelevantInterfaceMember extends Callable {
|
||||
RelevantInterfaceMember() {
|
||||
this instanceof Method or
|
||||
this instanceof Operator
|
||||
}
|
||||
|
||||
predicate isPublic() {
|
||||
this.(Method).isPublic() or
|
||||
this.(Operator).isPublic()
|
||||
}
|
||||
}
|
||||
|
||||
private RelevantInterfaceMember getACompatibleRelevantInterfaceMember(RelevantInterfaceMember m) {
|
||||
result = getACompatibleRelevantInterfaceMember0(m, m.getNumberOfParameters())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an interface method that may potentially be implemented by `m`.
|
||||
* Gets an interface method or operator that may potentially be implemented by `m`.
|
||||
*
|
||||
* That is, a method with the same name, same number of parameters, and declared
|
||||
* in a type that is a possible implementor type for the interface type.
|
||||
*/
|
||||
private Method getAnInterfaceMethodCandidate(Method m) {
|
||||
getAPotentialInterfaceMethodAux(result, m.getDeclaringType(), m.getUndecoratedName(),
|
||||
private RelevantInterfaceMember getARelevantInterfaceMemberCandidate(RelevantInterfaceMember m) {
|
||||
getAPotentialRelevantInterfaceMemberAux(result, m.getDeclaringType(), m.getUndecoratedName(),
|
||||
m.getNumberOfParameters()) and
|
||||
m.isPublic()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate getAPotentialInterfaceMethodAux(
|
||||
Method m, ValueOrRefType t, string name, int params
|
||||
private predicate getAPotentialRelevantInterfaceMemberAux(
|
||||
RelevantInterfaceMember m, ValueOrRefType t, string name, int params
|
||||
) {
|
||||
t = getAPossibleImplementor(m.getDeclaringType()) and
|
||||
name = m.getUndecoratedName() and
|
||||
params = m.getNumberOfParameters()
|
||||
}
|
||||
|
||||
private Type getArgumentOrReturnType(Method m, int i) {
|
||||
private Type getArgumentOrReturnType(RelevantInterfaceMember m, int i) {
|
||||
i = 0 and result = m.getReturnType()
|
||||
or
|
||||
result = m.getParameter(i - 1).getType()
|
||||
|
||||
@@ -184,7 +184,7 @@ private class TOverridable = @virtualizable or @callable_accessor;
|
||||
|
||||
/**
|
||||
* A declaration that can be overridden or implemented. That is, a method,
|
||||
* a property, an indexer, an event, or an accessor.
|
||||
* a property, an indexer, an event, an accessor, or an operator.
|
||||
*
|
||||
* Unlike `Virtualizable`, this class includes accessors.
|
||||
*/
|
||||
@@ -360,7 +360,7 @@ class Overridable extends Declaration, TOverridable {
|
||||
|
||||
/**
|
||||
* A member where the `virtual` modifier is valid. That is, a method,
|
||||
* a property, an indexer, or an event.
|
||||
* a property, an indexer, an event, or an operator.
|
||||
*
|
||||
* Equivalently, these are the members that can be defined in an interface.
|
||||
*
|
||||
|
||||
@@ -45,6 +45,16 @@ module Consistency {
|
||||
) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodeAtPosition`. */
|
||||
predicate uniqueParameterNodeAtPositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Holds if `(c, pos, p)` should be excluded from the consistency test `uniqueParameterNodePosition`. */
|
||||
predicate uniqueParameterNodePositionExclude(DataFlowCallable c, ParameterPosition pos, Node p) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
private class RelevantNode extends Node {
|
||||
@@ -246,6 +256,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodeAtPosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodeAtPositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and
|
||||
msg = "Parameters with overlapping positions."
|
||||
@@ -254,6 +265,7 @@ module Consistency {
|
||||
query predicate uniqueParameterNodePosition(
|
||||
DataFlowCallable c, ParameterPosition pos, Node p, string msg
|
||||
) {
|
||||
not any(ConsistencyConfiguration conf).uniqueParameterNodePositionExclude(c, pos, p) and
|
||||
isParameterNode(p, c, pos) and
|
||||
not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and
|
||||
msg = "Parameter node with multiple positions."
|
||||
|
||||
@@ -39,6 +39,11 @@ module Public {
|
||||
)
|
||||
or
|
||||
exists(ReturnKind rk | this = TReturnSummaryComponent(rk) and result = "return (" + rk + ")")
|
||||
or
|
||||
exists(SummaryComponent::SyntheticGlobal sg |
|
||||
this = TSyntheticGlobalSummaryComponent(sg) and
|
||||
result = "synthetic global (" + sg + ")"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,24 +164,24 @@ module Public {
|
||||
SummaryComponentStack return(ReturnKind rk) { result = singleton(SummaryComponent::return(rk)) }
|
||||
}
|
||||
|
||||
private predicate noComponentSpecificCsv(SummaryComponent sc) {
|
||||
not exists(getComponentSpecificCsv(sc))
|
||||
private predicate noComponentSpecific(SummaryComponent sc) {
|
||||
not exists(getComponentSpecific(sc))
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this component used for flow summaries. */
|
||||
private string getComponentCsv(SummaryComponent sc) {
|
||||
result = getComponentSpecificCsv(sc)
|
||||
private string getComponent(SummaryComponent sc) {
|
||||
result = getComponentSpecific(sc)
|
||||
or
|
||||
noComponentSpecificCsv(sc) and
|
||||
noComponentSpecific(sc) and
|
||||
(
|
||||
exists(ArgumentPosition pos |
|
||||
sc = TParameterSummaryComponent(pos) and
|
||||
result = "Parameter[" + getArgumentPositionCsv(pos) + "]"
|
||||
result = "Parameter[" + getArgumentPosition(pos) + "]"
|
||||
)
|
||||
or
|
||||
exists(ParameterPosition pos |
|
||||
sc = TArgumentSummaryComponent(pos) and
|
||||
result = "Argument[" + getParameterPositionCsv(pos) + "]"
|
||||
result = "Argument[" + getParameterPosition(pos) + "]"
|
||||
)
|
||||
or
|
||||
sc = TReturnSummaryComponent(getReturnValueKind()) and result = "ReturnValue"
|
||||
@@ -184,16 +189,16 @@ module Public {
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this stack used for flow summaries. */
|
||||
string getComponentStackCsv(SummaryComponentStack stack) {
|
||||
string getComponentStack(SummaryComponentStack stack) {
|
||||
exists(SummaryComponent head, SummaryComponentStack tail |
|
||||
head = stack.head() and
|
||||
tail = stack.tail() and
|
||||
result = getComponentStackCsv(tail) + "." + getComponentCsv(head)
|
||||
result = getComponentStack(tail) + "." + getComponent(head)
|
||||
)
|
||||
or
|
||||
exists(SummaryComponent c |
|
||||
stack = TSingletonSummaryComponentStack(c) and
|
||||
result = getComponentCsv(c)
|
||||
result = getComponent(c)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1217,8 +1222,8 @@ module Private {
|
||||
c.relevantSummary(input, output, preservesValue) and
|
||||
csv =
|
||||
c.getCallableCsv() // Callable information
|
||||
+ getComponentStackCsv(input) + ";" // input
|
||||
+ getComponentStackCsv(output) + ";" // output
|
||||
+ getComponentStack(input) + ";" // input
|
||||
+ getComponentStack(output) + ";" // output
|
||||
+ renderKind(preservesValue) + ";" // kind
|
||||
+ renderProvenance(c) // provenance
|
||||
)
|
||||
|
||||
@@ -178,7 +178,7 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) {
|
||||
}
|
||||
|
||||
/** Gets the textual representation of the content in the format used for flow summaries. */
|
||||
private string getContentSpecificCsv(Content c) {
|
||||
private string getContentSpecific(Content c) {
|
||||
c = TElementContent() and result = "Element"
|
||||
or
|
||||
exists(Field f | c = TFieldContent(f) and result = "Field[" + f.getQualifiedName() + "]")
|
||||
@@ -189,8 +189,8 @@ private string getContentSpecificCsv(Content c) {
|
||||
}
|
||||
|
||||
/** Gets the textual representation of a summary component in the format used for flow summaries. */
|
||||
string getComponentSpecificCsv(SummaryComponent sc) {
|
||||
exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecificCsv(c))
|
||||
string getComponentSpecific(SummaryComponent sc) {
|
||||
exists(Content c | sc = TContentSummaryComponent(c) and result = getContentSpecific(c))
|
||||
or
|
||||
sc = TWithoutContentSummaryComponent(_) and result = "WithoutElement"
|
||||
or
|
||||
@@ -204,7 +204,7 @@ string getComponentSpecificCsv(SummaryComponent sc) {
|
||||
}
|
||||
|
||||
/** Gets the textual representation of a parameter position in the format used for flow summaries. */
|
||||
string getParameterPositionCsv(ParameterPosition pos) {
|
||||
string getParameterPosition(ParameterPosition pos) {
|
||||
result = pos.getPosition().toString()
|
||||
or
|
||||
pos.isThisParameter() and
|
||||
@@ -212,7 +212,7 @@ string getParameterPositionCsv(ParameterPosition pos) {
|
||||
}
|
||||
|
||||
/** Gets the textual representation of an argument position in the format used for flow summaries. */
|
||||
string getArgumentPositionCsv(ArgumentPosition pos) {
|
||||
string getArgumentPosition(ArgumentPosition pos) {
|
||||
result = pos.getPosition().toString()
|
||||
or
|
||||
pos.isQualifier() and
|
||||
|
||||
@@ -670,7 +670,7 @@ compiler_generated(unique int id: @modifiable ref);
|
||||
|
||||
@named_exprorstmt = @goto_stmt | @labeled_stmt | @expr;
|
||||
|
||||
@virtualizable = @method | @property | @indexer | @event;
|
||||
@virtualizable = @method | @property | @indexer | @event | @operator;
|
||||
|
||||
exprorstmt_name(
|
||||
unique int parent_id: @named_exprorstmt ref,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Add operators to the virtualizable type.
|
||||
compatibility: full
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.5.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.0
|
||||
|
||||
### New Queries
|
||||
|
||||
3
csharp/ql/src/change-notes/released/0.5.1.md
Normal file
3
csharp/ql/src/change-notes/released/0.5.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.0
|
||||
lastReleaseVersion: 0.5.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 0.5.1-dev
|
||||
version: 0.5.2-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,8 @@ private predicate isOsSpecific(Declaration d) {
|
||||
.matches("%" +
|
||||
[
|
||||
"libobjc", "libproc", "System.Diagnostics.Tracing.XplatEventLogger",
|
||||
"System.Threading.AutoreleasePool"
|
||||
"System.Threading.AutoreleasePool",
|
||||
"System.Diagnostics.Tracing.EventSource.<WriteEventString>"
|
||||
] + "%")
|
||||
}
|
||||
|
||||
|
||||
@@ -472,6 +472,12 @@
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper.LessThan | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper.Swap | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper.SwapIfGreater | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0,!1>.GreaterThan | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0,!1>.LessThan | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0>.GreaterThan | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0>.LessThan | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0>.Swap | parameter | 32 |
|
||||
| Parameter 0 of System.Collections.Generic.GenericArraySortHelper<!0>.SwapIfGreater | parameter | 32 |
|
||||
| Parameter 0 of System.ConsolePal.<UpdatedCachedCursorPosition>g__IncrementX\|113_1 | parameter | 32 |
|
||||
| Parameter 0 of System.ConsolePal.<UpdatedCachedCursorPosition>g__IncrementY\|113_0 | parameter | 32 |
|
||||
| Parameter 0 of System.ConsolePal.GetWindowSize | parameter | 32 |
|
||||
@@ -661,6 +667,10 @@
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.CastHelpers.Element | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.CastHelpers.HashShift | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.CastHelpers.KeyToBucket | parameter | 32 |
|
||||
@@ -672,6 +682,9 @@
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.InteropServices.ComWrappers.GetIUnknownImpl | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.InteropServices.ComWrappers.GetIUnknownImplInternal | parameter | 32 |
|
||||
| Parameter 0 of System.Runtime.InteropServices.MemoryMarshal.CreateReadOnlySpan | parameter | 32 |
|
||||
@@ -737,6 +750,7 @@
|
||||
| Parameter 0 of System.Threading.Thread.VolatileRead | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.Thread.VolatileWrite | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.ThreadLocal.GrowTable | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.ThreadLocal<!0>.GrowTable | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.ThreadPool.GetAvailableThreads | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.ThreadPool.GetAvailableThreadsNative | parameter | 32 |
|
||||
| Parameter 0 of System.Threading.ThreadPool.GetMaxThreads | parameter | 32 |
|
||||
@@ -789,6 +803,7 @@
|
||||
| Parameter 1 of System.Buffers.Binary.BinaryPrimitives.TryReadUInt64BigEndian | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.Binary.BinaryPrimitives.TryReadUInt64LittleEndian | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.MemoryManager.TryGetArray | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.MemoryManager<!0>.TryGetArray | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.StandardFormat.ParseHelper | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.StandardFormat.TryParse | parameter | 32 |
|
||||
| Parameter 1 of System.Buffers.Text.FormattingHelpers.CountDecimalTrailingZeros | parameter | 32 |
|
||||
@@ -835,20 +850,36 @@
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue.TryPeek | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue.TryTake | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue<!0>.SnapForObservation | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue<!0>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue<!0>.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue<!0>.TryPeek | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueue<Object>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueueSegment.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueueSegment.TryPeek | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueueSegment<!0>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.ConcurrentQueueSegment<!0>.TryPeek | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.IProducerConsumerCollection.TryTake | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Concurrent.IProducerConsumerCollection<!0>.TryTake | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.DictionaryEntry.Deconstruct | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.EnumerableHelpers.ToArray | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper.GreaterThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper.LessThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper.Swap | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper.SwapIfGreater | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0,!1>.GreaterThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0,!1>.LessThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0>.GreaterThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0>.LessThan | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0>.Swap | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.GenericArraySortHelper<!0>.SwapIfGreater | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.KeyValuePair.Deconstruct | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.KeyValuePair<String,ResourceSet>.Deconstruct | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Queue.MoveNext | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Queue.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Queue.TryPeek | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Stack.TryPop | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Queue<!0>.MoveNext | parameter | 32 |
|
||||
| Parameter 1 of System.Collections.Generic.Stack<ConsoleKeyInfo>.TryPop | parameter | 32 |
|
||||
| Parameter 1 of System.ConsolePal.GetWindowSize | parameter | 32 |
|
||||
| Parameter 1 of System.ConsolePal.TryGetCachedCursorPosition | parameter | 32 |
|
||||
| Parameter 1 of System.ConsolePal.TryGetCursorPosition | parameter | 32 |
|
||||
@@ -974,6 +1005,21 @@
|
||||
| Parameter 1 of System.Half.TryParse | parameter | 32 |
|
||||
| Parameter 1 of System.HashCode.Initialize | parameter | 32 |
|
||||
| Parameter 1 of System.INumber.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Byte>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Char>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Decimal>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Double>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Half>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Int16>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Int32>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Int64>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<IntPtr>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<SByte>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<Single>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<UInt16>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<UInt32>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<UInt64>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.INumber<UIntPtr>.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.DelegateEnumerator.ShouldIncludeEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.DelegateEnumerator.ShouldRecurseIntoEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.DelegateEnumerator.TransformEntry | parameter | 32 |
|
||||
@@ -983,6 +1029,8 @@
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.FindTransform.BeginInvoke | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.FindTransform.EndInvoke | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable.FindTransform.Invoke | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable<!0>.FindPredicate.Invoke | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerable<!0>.FindTransform.Invoke | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerableFactory.<>c.<DirectoryInfos>b__7_0 | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerableFactory.<>c.<FileInfos>b__6_0 | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerableFactory.<>c.<FileSystemInfos>b__8_0 | parameter | 32 |
|
||||
@@ -999,6 +1047,9 @@
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator.ShouldIncludeEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator.ShouldRecurseIntoEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator.TransformEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator<!0>.ShouldIncludeEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator<!0>.ShouldRecurseIntoEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.Enumeration.FileSystemEnumerator<!0>.TransformEntry | parameter | 32 |
|
||||
| Parameter 1 of System.IO.FileSystem.DirectoryExists | parameter | 32 |
|
||||
| Parameter 1 of System.IO.FileSystem.FileExists | parameter | 32 |
|
||||
| Parameter 1 of System.IO.FileSystemInfo.Init | parameter | 32 |
|
||||
@@ -1073,7 +1124,14 @@
|
||||
| Parameter 1 of System.OrdinalComparer.IsWellKnownOrdinalComparerCore | parameter | 32 |
|
||||
| Parameter 1 of System.ParseNumbers.EatWhiteSpace | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlyMemory.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlyMemory<!0>.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlyMemory<Char>.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan<!0>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan<Byte>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan<Char>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan<Int32>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.ReadOnlySpan<Object>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Reflection.Binder.ReorderArgumentArray | parameter | 32 |
|
||||
| Parameter 1 of System.Reflection.CustomAttribute.FilterCustomAttributeRecord | parameter | 32 |
|
||||
| Parameter 1 of System.Reflection.CustomAttribute.GetPropertyOrFieldData | parameter | 32 |
|
||||
@@ -1107,9 +1165,27 @@
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetNotificationForWaitCompletion | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.SetNotificationForWaitCompletion | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Boolean>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Boolean>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Byte[]>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Byte[]>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String[]>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String[]>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.SetNotificationForWaitCompletion | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<Int32>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<Int32>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Start | parameter | 32 |
|
||||
@@ -1120,6 +1196,13 @@
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.GetStateMachineBox | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<Int32>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<Int32>.Start | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<VoidTaskResult>.SetException | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.QCallAssembly..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.QCallModule..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Runtime.CompilerServices.QCallTypeHandle..ctor | parameter | 32 |
|
||||
@@ -1195,6 +1278,8 @@
|
||||
| Parameter 1 of System.RuntimeType.RuntimeTypeCache.GetMemberList | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.AddSpecialInterface | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.Insert | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.AddSpecialInterface | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.Insert | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeType.SplitName | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeTypeHandle.CopyRuntimeTypeHandles | parameter | 32 |
|
||||
| Parameter 1 of System.RuntimeTypeHandle.GetActivationInfo | parameter | 32 |
|
||||
@@ -1204,6 +1289,13 @@
|
||||
| Parameter 1 of System.Single.TryCreate | parameter | 32 |
|
||||
| Parameter 1 of System.Single.TryParse | parameter | 32 |
|
||||
| Parameter 1 of System.Span..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<!0>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<!1>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<Byte>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<Char>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<GuidResult>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<Int32>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.Span<Object>..ctor | parameter | 32 |
|
||||
| Parameter 1 of System.SpanHelpers.SequenceEqual | parameter | 32 |
|
||||
| Parameter 1 of System.String.Create | parameter | 32 |
|
||||
| Parameter 1 of System.String.IndexOfNewlineChar | parameter | 32 |
|
||||
@@ -1260,9 +1352,13 @@
|
||||
| Parameter 1 of System.Threading.SpinLock.Enter | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.SpinLock.TryEnter | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.IProducerConsumerQueue.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.IProducerConsumerQueue<!0>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.IProducerConsumerQueue<Task>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.MultiProducerMultiConsumerQueue.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.SingleProducerSingleConsumerQueue.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.SingleProducerSingleConsumerQueue.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.SingleProducerSingleConsumerQueue<!0>.TryDequeue | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.SingleProducerSingleConsumerQueue<!0>.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.Task.AddToList | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.Task.CreationOptionsFromContinuationOptions | parameter | 32 |
|
||||
| Parameter 1 of System.Threading.Tasks.Task.ExecuteWithThreadLocal | parameter | 32 |
|
||||
@@ -1310,6 +1406,9 @@
|
||||
| Parameter 1 of System.UIntPtr.TryParse | parameter | 32 |
|
||||
| Parameter 1 of System.Version.TryParse | parameter | 32 |
|
||||
| Parameter 1 of System.WeakReference.TryGetTarget | parameter | 32 |
|
||||
| Parameter 1 of System.WeakReference<AssemblyLoadContext>.TryGetTarget | parameter | 32 |
|
||||
| Parameter 1 of System.WeakReference<CounterGroup>.TryGetTarget | parameter | 32 |
|
||||
| Parameter 1 of System.WeakReference<EventSource>.TryGetTarget | parameter | 32 |
|
||||
| Parameter 1 of System.__DTString.GetRegularToken | parameter | 32 |
|
||||
| Parameter 2 of Internal.Runtime.InteropServices.IClassFactory.CreateInstance | parameter | 32 |
|
||||
| Parameter 2 of Interop.Globalization.GetJapaneseEraStartDate | parameter | 32 |
|
||||
@@ -1370,15 +1469,49 @@
|
||||
| Parameter 2 of System.Char.TryFormat | parameter | 32 |
|
||||
| Parameter 2 of System.Char.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Concurrent.ConcurrentQueue.SnapForObservation | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Concurrent.ConcurrentQueue<!0>.SnapForObservation | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.DictionaryEntry.Deconstruct | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary.CollectionsMarshalHelper.GetValueRefOrAddDefault | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary.Remove | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<!0,!1>.CollectionsMarshalHelper.GetValueRefOrAddDefault | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Int32,ChannelInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Int32,CultureInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Int32,HashSet<Token>>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Int32,String>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Int32,Task>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<MemberInfo,NullabilityState>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Module,NotAnnotatedStatus>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Object,Object>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<ReadOnlyMemory<Char>,ConsoleKeyInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,Assembly>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,Boolean>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,CultureData>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,CultureInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,Int32>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,IntPtr>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,IsolatedComponentLoadContext>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,List<Int32>>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,List<RuntimePropertyInfo>>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,LocalDataStoreSlot>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,Object>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,ResourceLocator>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,ResourceSet>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,String>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,TimeZoneInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<String,Type>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Type,AttributeUsageAttribute>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<Type,TraceLoggingTypeInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<UInt64,Char>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.Dictionary<UInt64,String>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.HashSet.AddIfNotPresent | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.HashSet.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.HashSet<!0>.AddIfNotPresent | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.IDictionary.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.IDictionary<String,String>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.IReadOnlyDictionary.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.KeyValuePair.Deconstruct | parameter | 32 |
|
||||
| Parameter 2 of System.Collections.Generic.KeyValuePair<String,ResourceSet>.Deconstruct | parameter | 32 |
|
||||
| Parameter 2 of System.ComponentModel.DefaultValueAttribute.ctor>g__TryConvertFromInvariantString\|2_0 | parameter | 32 |
|
||||
| Parameter 2 of System.ConsolePal.<TryGetCursorPosition>g__BufferUntil\|83_1 | parameter | 32 |
|
||||
| Parameter 2 of System.Convert.CopyToTempBufferWithoutWhiteSpace | parameter | 32 |
|
||||
@@ -1509,8 +1642,50 @@
|
||||
| Parameter 2 of System.IO.UnmanagedMemoryAccessor.Read | parameter | 32 |
|
||||
| Parameter 2 of System.IO.UnmanagedMemoryAccessor.Write | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Byte>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Char>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<DateOnly>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<DateTime>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<DateTimeOffset>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Decimal>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Double>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Guid>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Half>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Int16>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Int32>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Int64>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<IntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<SByte>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<Single>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<TimeOnly>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<TimeSpan>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<UInt16>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<UInt32>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<UInt64>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.IParseable<UIntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanFormattable.TryFormat | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Byte>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Char>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<DateOnly>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<DateTime>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<DateTimeOffset>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Decimal>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Double>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Guid>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Half>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Int16>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Int32>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Int64>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<IntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<SByte>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<Single>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<TimeOnly>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<TimeSpan>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<UInt16>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<UInt32>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<UInt64>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.ISpanParseable<UIntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.Int16.TryFormat | parameter | 32 |
|
||||
| Parameter 2 of System.Int16.TryParse | parameter | 32 |
|
||||
| Parameter 2 of System.Int32.TryFormat | parameter | 32 |
|
||||
@@ -1550,6 +1725,8 @@
|
||||
| Parameter 2 of System.ParseNumbers.GrabLongs | parameter | 32 |
|
||||
| Parameter 2 of System.ParseNumbers.IsDigit | parameter | 32 |
|
||||
| Parameter 2 of System.ReadOnlyMemory.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 2 of System.ReadOnlyMemory<!0>.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 2 of System.ReadOnlyMemory<Char>.GetObjectStartLength | parameter | 32 |
|
||||
| Parameter 2 of System.Reflection.AssemblyName.EscapeAsciiChar | parameter | 32 |
|
||||
| Parameter 2 of System.Reflection.CustomAttribute.AttributeUsageCheck | parameter | 32 |
|
||||
| Parameter 2 of System.Reflection.CustomAttribute.ParseAttributeUsageAttribute | parameter | 32 |
|
||||
@@ -1610,18 +1787,35 @@
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncIteratorMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Boolean>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<Byte[]>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<String[]>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncTaskMethodBuilder<VoidTaskResult>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<Int32>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.AsyncVoidMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable.Container.FindEntry | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable.Container.TryGetEntry | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable.Container.TryGetValueWorker | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<!0,!1>.Container.FindEntry | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<!0,!1>.Container.TryGetEntry | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<!0,!1>.Container.TryGetValueWorker | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<!0,!1>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<Assembly,DllImportResolver>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ConditionalWeakTable<Object,SerializationInfo>.TryGetValue | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ICastable.IsInstanceOfInterface | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.ICastableHelpers.IsInstanceOfInterface | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<!0>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<Int32>.AwaitUnsafeOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.CompilerServices.PoolingAsyncValueTaskMethodBuilder<VoidTaskResult>.AwaitOnCompleted | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.InteropServices.CollectionsMarshal.GetValueRefOrAddDefault | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.InteropServices.ComTypes.IBindCtx.GetObjectParam | parameter | 32 |
|
||||
| Parameter 2 of System.Runtime.InteropServices.ComTypes.IConnectionPoint.Advise | parameter | 32 |
|
||||
@@ -1724,6 +1918,7 @@
|
||||
| Parameter 2 of System.Text.UTF8Encoding.DecodeFirstRune | parameter | 32 |
|
||||
| Parameter 2 of System.Text.UTF8Encoding.TryGetByteCount | parameter | 32 |
|
||||
| Parameter 2 of System.Text.Unicode.TextSegmentationUtility.DecodeFirstRune.Invoke | parameter | 32 |
|
||||
| Parameter 2 of System.Text.Unicode.TextSegmentationUtility.DecodeFirstRune<!0>.Invoke | parameter | 32 |
|
||||
| Parameter 2 of System.Text.Unicode.Utf8.FromUtf16 | parameter | 32 |
|
||||
| Parameter 2 of System.Text.Unicode.Utf8.ToUtf16 | parameter | 32 |
|
||||
| Parameter 2 of System.Text.Unicode.Utf8Utility.GetPointerToFirstInvalidByte | parameter | 32 |
|
||||
@@ -1748,6 +1943,8 @@
|
||||
| Parameter 2 of System.Threading.SpinLock.TryEnter | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.Tasks.SingleProducerSingleConsumerQueue.EnqueueSlow | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.Tasks.SingleProducerSingleConsumerQueue.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.Tasks.SingleProducerSingleConsumerQueue<!0>.EnqueueSlow | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.Tasks.SingleProducerSingleConsumerQueue<!0>.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.Tasks.Task.CreationOptionsFromContinuationOptions | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.ThreadPool.GetNextConfigUInt32Value | parameter | 32 |
|
||||
| Parameter 2 of System.Threading.ThreadPoolWorkQueue.Dequeue | parameter | 32 |
|
||||
@@ -1810,6 +2007,7 @@
|
||||
| Parameter 3 of System.Byte.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.Char.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.Collections.Concurrent.ConcurrentQueue.SnapForObservation | parameter | 32 |
|
||||
| Parameter 3 of System.Collections.Concurrent.ConcurrentQueue<!0>.SnapForObservation | parameter | 32 |
|
||||
| Parameter 3 of System.Collections.Hashtable.InitHash | parameter | 32 |
|
||||
| Parameter 3 of System.ConsolePal.<TryGetCursorPosition>g__AppendToStdInReaderUntil\|83_2 | parameter | 32 |
|
||||
| Parameter 3 of System.ConsolePal.<TryGetCursorPosition>g__BufferUntil\|83_1 | parameter | 32 |
|
||||
@@ -1868,6 +2066,21 @@
|
||||
| Parameter 3 of System.Half.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.HashCode.Initialize | parameter | 32 |
|
||||
| Parameter 3 of System.INumber.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Byte>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Char>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Decimal>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Double>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Half>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Int16>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Int32>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Int64>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<IntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<SByte>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<Single>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<UInt16>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<UInt32>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<UInt64>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.INumber<UIntPtr>.TryParse | parameter | 32 |
|
||||
| Parameter 3 of System.IO.BufferedStream.WriteToBuffer | parameter | 32 |
|
||||
| Parameter 3 of System.IO.Path.TryJoin | parameter | 32 |
|
||||
| Parameter 3 of System.IO.StdInReader.GetKeyFromCharValue | parameter | 32 |
|
||||
@@ -1947,6 +2160,7 @@
|
||||
| Parameter 3 of System.Resources.ResourceReader.GetResourceData | parameter | 32 |
|
||||
| Parameter 3 of System.Resources.RuntimeResourceSet.ReadValue | parameter | 32 |
|
||||
| Parameter 3 of System.Runtime.CompilerServices.ConditionalWeakTable.Container.TryGetEntry | parameter | 32 |
|
||||
| Parameter 3 of System.Runtime.CompilerServices.ConditionalWeakTable<!0,!1>.Container.TryGetEntry | parameter | 32 |
|
||||
| Parameter 3 of System.Runtime.InteropServices.ComTypes.IMoniker.BindToObject | parameter | 32 |
|
||||
| Parameter 3 of System.Runtime.InteropServices.ComTypes.IMoniker.BindToStorage | parameter | 32 |
|
||||
| Parameter 3 of System.Runtime.InteropServices.ComTypes.IMoniker.ComposeWith | parameter | 32 |
|
||||
@@ -1981,6 +2195,8 @@
|
||||
| Parameter 3 of System.RuntimeType.GetType | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.PopulateLiteralFields | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.PopulateRtFields | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.PopulateLiteralFields | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.PopulateRtFields | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeTypeHandle.GetActivationInfo | parameter | 32 |
|
||||
| Parameter 3 of System.RuntimeTypeHandle.GetTypeByName | parameter | 32 |
|
||||
| Parameter 3 of System.SByte.TryParse | parameter | 32 |
|
||||
@@ -2005,6 +2221,7 @@
|
||||
| Parameter 3 of System.Text.UTF8Encoding.DecodeFirstRune | parameter | 32 |
|
||||
| Parameter 3 of System.Text.UTF8Encoding.EncodeRune | parameter | 32 |
|
||||
| Parameter 3 of System.Text.Unicode.TextSegmentationUtility.DecodeFirstRune.Invoke | parameter | 32 |
|
||||
| Parameter 3 of System.Text.Unicode.TextSegmentationUtility.DecodeFirstRune<!0>.Invoke | parameter | 32 |
|
||||
| Parameter 3 of System.Text.Unicode.Utf8.FromUtf16 | parameter | 32 |
|
||||
| Parameter 3 of System.Text.Unicode.Utf8.ToUtf16 | parameter | 32 |
|
||||
| Parameter 3 of System.Text.Unicode.Utf8Utility.GetPointerToFirstInvalidByte | parameter | 32 |
|
||||
@@ -2015,6 +2232,7 @@
|
||||
| Parameter 3 of System.Threading.SpinLock.ContinueTryEnterWithThreadTracking | parameter | 32 |
|
||||
| Parameter 3 of System.Threading.Tasks.AwaitTaskContinuation.RunCallback | parameter | 32 |
|
||||
| Parameter 3 of System.Threading.Tasks.SingleProducerSingleConsumerQueue.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 3 of System.Threading.Tasks.SingleProducerSingleConsumerQueue<!0>.TryDequeueSlow | parameter | 32 |
|
||||
| Parameter 3 of System.Threading.Tasks.Task.AtomicStateUpdate | parameter | 32 |
|
||||
| Parameter 3 of System.Threading.ThreadPool.GetNextConfigUInt32Value | parameter | 32 |
|
||||
| Parameter 3 of System.TimeOnly.TryParse | parameter | 32 |
|
||||
@@ -2052,6 +2270,7 @@
|
||||
| Parameter 4 of System.Buffers.Text.Utf8Parser.TryParseAsSpecialFloatingPoint | parameter | 32 |
|
||||
| Parameter 4 of System.Buffers.Text.Utf8Parser.TryParseNumber | parameter | 32 |
|
||||
| Parameter 4 of System.Collections.Concurrent.ConcurrentQueue.SnapForObservation | parameter | 32 |
|
||||
| Parameter 4 of System.Collections.Concurrent.ConcurrentQueue<!0>.SnapForObservation | parameter | 32 |
|
||||
| Parameter 4 of System.Collections.Hashtable.InitHash | parameter | 32 |
|
||||
| Parameter 4 of System.ConsolePal.<TryGetCursorPosition>g__AppendToStdInReaderUntil\|83_2 | parameter | 32 |
|
||||
| Parameter 4 of System.ConsolePal.<TryGetCursorPosition>g__BufferUntil\|83_1 | parameter | 32 |
|
||||
@@ -2163,6 +2382,7 @@
|
||||
| Parameter 4 of System.RuntimeFieldHandle.GetValue | parameter | 32 |
|
||||
| Parameter 4 of System.RuntimeType.FilterHelper | parameter | 32 |
|
||||
| Parameter 4 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.PopulateEvents | parameter | 32 |
|
||||
| Parameter 4 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.PopulateEvents | parameter | 32 |
|
||||
| Parameter 4 of System.RuntimeTypeHandle.GetActivationInfo | parameter | 32 |
|
||||
| Parameter 4 of System.TermInfo.ParameterizedStrings.EvaluateInternal | parameter | 32 |
|
||||
| Parameter 4 of System.Text.ASCIIEncoding.GetByteCountFast | parameter | 32 |
|
||||
@@ -2228,6 +2448,8 @@
|
||||
| Parameter 5 of System.RuntimeType.FilterHelper | parameter | 32 |
|
||||
| Parameter 5 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.PopulateProperties | parameter | 32 |
|
||||
| Parameter 5 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache.PopulateRtFields | parameter | 32 |
|
||||
| Parameter 5 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.PopulateProperties | parameter | 32 |
|
||||
| Parameter 5 of System.RuntimeType.RuntimeTypeCache.MemberInfoCache<!0>.PopulateRtFields | parameter | 32 |
|
||||
| Parameter 5 of System.TermInfo.ParameterizedStrings.EvaluateInternal | parameter | 32 |
|
||||
| Parameter 5 of System.Text.ASCIIEncoding.GetBytesFast | parameter | 32 |
|
||||
| Parameter 5 of System.Text.ASCIIEncoding.GetCharsFast | parameter | 32 |
|
||||
@@ -2336,10 +2558,16 @@
|
||||
| Parameter 21 of System.TupleExtensions.Deconstruct | parameter | 32 |
|
||||
| System.ByReference.Value | property | 32 |
|
||||
| System.ByReference.get_Value | method | 32 |
|
||||
| System.ByReference<!0>.get_Value | method | 32 |
|
||||
| System.ByReference<Byte>.get_Value | method | 32 |
|
||||
| System.Collections.Generic.Dictionary.CollectionsMarshalHelper.GetValueRefOrAddDefault | method | 32 |
|
||||
| System.Collections.Generic.Dictionary.FindValue | method | 32 |
|
||||
| System.Collections.Generic.Dictionary.GetBucket | method | 32 |
|
||||
| System.Collections.Generic.Dictionary<!0,!1>.CollectionsMarshalHelper.GetValueRefOrAddDefault | method | 32 |
|
||||
| System.Collections.Generic.Dictionary<!0,!1>.FindValue | method | 32 |
|
||||
| System.Collections.Generic.Dictionary<!0,!1>.GetBucket | method | 32 |
|
||||
| System.Collections.Generic.HashSet.GetBucketRef | method | 32 |
|
||||
| System.Collections.Generic.HashSet<!0>.GetBucketRef | method | 32 |
|
||||
| System.Decimal.AsMutable | method | 32 |
|
||||
| System.Decimal.Max | method | 32 |
|
||||
| System.Decimal.Min | method | 32 |
|
||||
@@ -2350,6 +2578,16 @@
|
||||
| System.ReadOnlySpan.GetPinnableReference | method | 32 |
|
||||
| System.ReadOnlySpan.Item | property | 32 |
|
||||
| System.ReadOnlySpan.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<!0>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<Boolean>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<Byte>.GetPinnableReference | method | 32 |
|
||||
| System.ReadOnlySpan<Byte>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<Char>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<Int32>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<Object>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<SafeWaitHandle>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<String>.get_Item | method | 32 |
|
||||
| System.ReadOnlySpan<WaitHandle>.get_Item | method | 32 |
|
||||
| System.Runtime.CompilerServices.CastHelpers.Element | method | 32 |
|
||||
| System.Runtime.CompilerServices.CastHelpers.LdelemaRef | method | 32 |
|
||||
| System.Runtime.CompilerServices.CastHelpers.TableData | method | 32 |
|
||||
@@ -2371,6 +2609,19 @@
|
||||
| System.Span.GetPinnableReference | method | 32 |
|
||||
| System.Span.Item | property | 32 |
|
||||
| System.Span.get_Item | method | 32 |
|
||||
| System.Span<!0>.get_Item | method | 32 |
|
||||
| System.Span<!1>.get_Item | method | 32 |
|
||||
| System.Span<Byte>.GetPinnableReference | method | 32 |
|
||||
| System.Span<Byte>.get_Item | method | 32 |
|
||||
| System.Span<Char>.GetPinnableReference | method | 32 |
|
||||
| System.Span<Char>.get_Item | method | 32 |
|
||||
| System.Span<EventPipeProviderConfigurationNative>.GetPinnableReference | method | 32 |
|
||||
| System.Span<EventPipeProviderConfigurationNative>.get_Item | method | 32 |
|
||||
| System.Span<IOVector>.get_Item | method | 32 |
|
||||
| System.Span<Int32>.get_Item | method | 32 |
|
||||
| System.Span<IntPtr>.get_Item | method | 32 |
|
||||
| System.Span<Object>.get_Item | method | 32 |
|
||||
| System.Span<SafeWaitHandle>.get_Item | method | 32 |
|
||||
| System.SpanHelpers.Add | method | 32 |
|
||||
| System.String.GetPinnableReference | method | 32 |
|
||||
| System.String.GetRawStringData | method | 32 |
|
||||
|
||||
23
csharp/ql/test/library-tests/csharp11/GenericAttribute.cs
Normal file
23
csharp/ql/test/library-tests/csharp11/GenericAttribute.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
[assembly: MyGenericAttribute<int>()]
|
||||
[module: MyGeneric2<object, object>()]
|
||||
|
||||
public class MyGenericAttribute<T> : Attribute { }
|
||||
public class MyGeneric2Attribute<T, U> : Attribute { }
|
||||
|
||||
public class TestGenericAttribute
|
||||
{
|
||||
|
||||
[MyGenericAttribute<int>()]
|
||||
public void M1() { }
|
||||
|
||||
[MyGeneric<string>()]
|
||||
public void M2() { }
|
||||
|
||||
[MyGeneric2<int, string>()]
|
||||
public void M3() { }
|
||||
|
||||
[return: MyGeneric<object>()]
|
||||
public int M4() { return 0; }
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
public interface INumber<T> where T : INumber<T>
|
||||
{
|
||||
static abstract T operator ++(T other);
|
||||
|
||||
static virtual T operator --(T other) => other;
|
||||
|
||||
static abstract T Add(T left, T right);
|
||||
|
||||
static virtual T Subtract(T left, T right) => left;
|
||||
|
||||
static T Zero() => default(T);
|
||||
}
|
||||
|
||||
public class Complex : INumber<Complex>
|
||||
{
|
||||
public double Real { get; private set; } = 0.0;
|
||||
public double Imaginary { get; private set; } = 0.0;
|
||||
|
||||
public Complex() { }
|
||||
|
||||
public static Complex Zero() => new Complex();
|
||||
|
||||
public static Complex operator ++(Complex other) =>
|
||||
new Complex { Real = other.Real + 1.0, Imaginary = other.Imaginary };
|
||||
|
||||
public static Complex operator --(Complex other) =>
|
||||
new Complex { Real = other.Real - 1.0, Imaginary = other.Imaginary };
|
||||
|
||||
public static Complex Add(Complex left, Complex right) =>
|
||||
new Complex { Real = left.Real + right.Real, Imaginary = left.Imaginary + right.Imaginary };
|
||||
|
||||
public static Complex Subtract(Complex left, Complex right) =>
|
||||
new Complex { Real = left.Real - right.Real, Imaginary = left.Imaginary - right.Imaginary };
|
||||
}
|
||||
16
csharp/ql/test/library-tests/csharp11/cil/Assembly.cs_
Normal file
16
csharp/ql/test/library-tests/csharp11/cil/Assembly.cs_
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Assembly
|
||||
{
|
||||
public class MyAssemblyGeneric1Attribute<T> : Attribute { }
|
||||
public class MyAssemblyGeneric2Attribute<T, U> : Attribute { }
|
||||
|
||||
public class TestAssemblyGenericAttribute
|
||||
{
|
||||
[MyAssemblyGeneric1Attribute<object>()]
|
||||
public void M1() { }
|
||||
|
||||
[MyAssemblyGeneric2<int, string>()]
|
||||
public void M2() { }
|
||||
}
|
||||
}
|
||||
BIN
csharp/ql/test/library-tests/csharp11/cil/assembly.dll
Normal file
BIN
csharp/ql/test/library-tests/csharp11/cil/assembly.dll
Normal file
Binary file not shown.
7
csharp/ql/test/library-tests/csharp11/cil/class1.cs
Normal file
7
csharp/ql/test/library-tests/csharp11/cil/class1.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
using Assembly;
|
||||
|
||||
public class Class1
|
||||
{
|
||||
public static void Main(string[] args) { }
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
| assembly.dll:0:0:0:0 | [MyAssemblyGeneric1Attribute<Object>(...)] | MyAssemblyGeneric1Attribute<Object> | 1 | (Object) |
|
||||
| assembly.dll:0:0:0:0 | [MyAssemblyGeneric2Attribute<Int32,String>(...)] | MyAssemblyGeneric2Attribute<Int32,String> | 2 | (Int32,String) |
|
||||
@@ -0,0 +1,9 @@
|
||||
import semmle.code.cil.CIL
|
||||
|
||||
private string getTypeArguments(GenericAttribute a) {
|
||||
result = "(" + concat(Type t | t = a.getATypeArgument() | t.getName(), ",") + ")"
|
||||
}
|
||||
|
||||
from GenericAttribute a
|
||||
where a.getFile().getStem() = "assembly"
|
||||
select a, a.getType().getName(), a.getNumberOfTypeArguments(), getTypeArguments(a)
|
||||
1
csharp/ql/test/library-tests/csharp11/cil/options
Normal file
1
csharp/ql/test/library-tests/csharp11/cil/options
Normal file
@@ -0,0 +1 @@
|
||||
semmle-extractor-options: --cil
|
||||
@@ -0,0 +1,6 @@
|
||||
| GenericAttribute.cs:3:12:3:34 | [assembly: MyGeneric<Int32>(...)] | MyGenericAttribute<Int32> | 1 | (Int32) | GenericAssemblyAttribute |
|
||||
| GenericAttribute.cs:4:10:4:35 | [module: MyGeneric2<Object,Object>(...)] | MyGeneric2Attribute<Object,Object> | 2 | (Object) | GenericModuleAttribute |
|
||||
| GenericAttribute.cs:12:6:12:28 | [MyGeneric<Int32>(...)] | MyGenericAttribute<Int32> | 1 | (Int32) | GenericDefaultAttribute |
|
||||
| GenericAttribute.cs:15:6:15:22 | [MyGeneric<String>(...)] | MyGenericAttribute<String> | 1 | (String) | GenericDefaultAttribute |
|
||||
| GenericAttribute.cs:18:6:18:28 | [MyGeneric2<Int32,String>(...)] | MyGeneric2Attribute<Int32,String> | 2 | (Int32,String) | GenericDefaultAttribute |
|
||||
| GenericAttribute.cs:21:14:21:30 | [return: MyGeneric<Object>(...)] | MyGenericAttribute<Object> | 1 | (Object) | GenericReturnAttribute |
|
||||
10
csharp/ql/test/library-tests/csharp11/genericAttribute.ql
Normal file
10
csharp/ql/test/library-tests/csharp11/genericAttribute.ql
Normal file
@@ -0,0 +1,10 @@
|
||||
import csharp
|
||||
|
||||
private string getTypeArguments(GenericAttribute a) {
|
||||
result = "(" + concat(Type t | t = a.getATypeArgument() | t.getName(), ",") + ")"
|
||||
}
|
||||
|
||||
from GenericAttribute a
|
||||
where a.getFile().getStem() = "GenericAttribute"
|
||||
select a, a.getType().getName(), a.getNumberOfTypeArguments(), getTypeArguments(a),
|
||||
a.getAPrimaryQlClass()
|
||||
@@ -0,0 +1,20 @@
|
||||
interfacemembers
|
||||
| INumber<> | StaticInterfaceMembers.cs:3:32:3:33 | ++ | abstract |
|
||||
| INumber<> | StaticInterfaceMembers.cs:3:32:3:33 | ++ | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:3:32:3:33 | ++ | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:5:31:5:32 | -- | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:5:31:5:32 | -- | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:5:31:5:32 | -- | virtual |
|
||||
| INumber<> | StaticInterfaceMembers.cs:7:23:7:25 | Add | abstract |
|
||||
| INumber<> | StaticInterfaceMembers.cs:7:23:7:25 | Add | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:7:23:7:25 | Add | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:9:22:9:29 | Subtract | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:9:22:9:29 | Subtract | static |
|
||||
| INumber<> | StaticInterfaceMembers.cs:9:22:9:29 | Subtract | virtual |
|
||||
| INumber<> | StaticInterfaceMembers.cs:11:14:11:17 | Zero | public |
|
||||
| INumber<> | StaticInterfaceMembers.cs:11:14:11:17 | Zero | static |
|
||||
implements
|
||||
| StaticInterfaceMembers.cs:23:36:23:37 | ++ | StaticInterfaceMembers.cs:3:32:3:33 | ++ |
|
||||
| StaticInterfaceMembers.cs:26:36:26:37 | -- | StaticInterfaceMembers.cs:5:31:5:32 | -- |
|
||||
| StaticInterfaceMembers.cs:29:27:29:29 | Add | StaticInterfaceMembers.cs:7:23:7:25 | Add |
|
||||
| StaticInterfaceMembers.cs:32:27:32:34 | Subtract | StaticInterfaceMembers.cs:9:22:9:29 | Subtract |
|
||||
@@ -0,0 +1,18 @@
|
||||
import csharp
|
||||
|
||||
query predicate interfacemembers(string interface, Member m, string modifier) {
|
||||
exists(Interface i |
|
||||
i.isUnboundDeclaration() and
|
||||
i.getFile().getStem() = "StaticInterfaceMembers" and
|
||||
i.getName() = interface and
|
||||
m = i.getAMember() and
|
||||
modifier = m.getAModifier().getName()
|
||||
)
|
||||
}
|
||||
|
||||
query predicate implements(Overridable o, Virtualizable v) {
|
||||
v.getFile().getStem() = "StaticInterfaceMembers" and
|
||||
(v.isVirtual() or v.isAbstract()) and
|
||||
v.isStatic() and
|
||||
v.getAnImplementor() = o
|
||||
}
|
||||
@@ -7,6 +7,7 @@ type NUL && "%CODEQL_DIST%\codeql" database index-files ^
|
||||
--include-extension=.xml ^
|
||||
--size-limit 10m ^
|
||||
--language xml ^
|
||||
--working-dir=. ^
|
||||
-- ^
|
||||
"%CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE%" ^
|
||||
>nul 2>&1
|
||||
|
||||
@@ -9,6 +9,7 @@ set -eu
|
||||
--include-extension=.xml \
|
||||
--size-limit 10m \
|
||||
--language xml \
|
||||
--working-dir=. \
|
||||
-- \
|
||||
"$CODEQL_EXTRACTOR_CSHARP_WIP_DATABASE" \
|
||||
> /dev/null 2>&1
|
||||
|
||||
18
docs/codeql/CONTRIBUTING.md
Normal file
18
docs/codeql/CONTRIBUTING.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Contributing to CodeQL docs
|
||||
|
||||
We welcome contributions to our CodeQL docs. Want to improve existing docs or add new information you think would be helpful? Then please go ahead and open a pull request!
|
||||
|
||||
## Contributing to CodeQL docs on `codeql.github.com`
|
||||
|
||||
To make changes to the documentation on [codeql.github.com](https://codeql.github.com/docs/codeql-overview/), you can make changes to the documentation files using the GitHub UI, a codespace, or a local text editor, and then open a pull request for review. For more information about the format and structure of the CodeQL documentation on [codeql.github.com](https://codeql.github.com/docs/codeql-overview/), please see the [README](docs/codeql/README.rst).
|
||||
|
||||
## Contributing to CodeQL CLI docs on `docs.github.com`
|
||||
|
||||
We are in the process of moving all documentation about the CodeQL CLI from [github/codeql](docs/codeql) to the public [github/docs](https://github.com/github/docs) repository so that this documentation is published on the [GitHub Docs](https://docs.github.com/en/code-security/code-scanning) site. This includes all articles that are currently published under "[Using the CodeQL CLI](https://codeql.github.com/docs/codeql-cli/using-the-codeql-cli/)" and "[CodeQL CLI reference](https://codeql.github.com/docs/codeql-cli/codeql-cli-reference/)" categories on the CodeQL microsite. This will make it easier for code scanning users to find information about using CodeQL to query their codebases.
|
||||
|
||||
**Note**: For a brief time, we will have source files for CodeQL CLI documentation in two locations. During this period we will not accept changes to the old files in the `codeql` repository, only to the new files in the `docs` repository.
|
||||
|
||||
To contribute to these docs, which are located in the [`code-scanning`](https://github.com/github/docs/tree/main/content/code-security/code-scanning) directory, please refer to the [CONTRIBUTING.md](CONTRIBUTING.md) file in the `docs` repository.
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ see https://docutils.sourceforge.io/rst.html.
|
||||
|
||||
For more information on Sphinx, see https://www.sphinx-doc.org.
|
||||
|
||||
For information on contributing to CodeQL documentation, see the `contributing guide </CONTRIBUTING.md>`__.
|
||||
|
||||
Project structure
|
||||
*****************
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ In summary, the kinds of expressions are:
|
||||
- These refer to predicates.
|
||||
- They can be simple :ref:`names <names>` or names with arities (for example in an :ref:`alias <aliases>`
|
||||
definition), or :ref:`selections <selections>`.
|
||||
- **signature expressions**
|
||||
- These refer to module signatures, type signatures, or predicate signatures.
|
||||
- They can be simple :ref:`names <names>`, names with arities, :ref:`selections <selections>`,
|
||||
or :ref:`instantiations <parameterized-modules>`.
|
||||
|
||||
.. _names:
|
||||
|
||||
@@ -164,14 +168,27 @@ As in many other programming languages, a namespace is a mapping from **keys** t
|
||||
**entities**. A key is a kind of identifier, for example a name, and a QL entity is
|
||||
a :ref:`module <modules>`, a :ref:`type <types>`, or a :ref:`predicate <predicates>`.
|
||||
|
||||
Each module in QL has three namespaces:
|
||||
Each module in QL has six namespaces:
|
||||
|
||||
- The **module namespace**, where the keys are module names and the entities are modules.
|
||||
- The **type namespace**, where the keys are type names and the entities are types.
|
||||
- The **predicate namespace**, where the keys are pairs of predicate names and arities,
|
||||
and the entities are predicates.
|
||||
- The **module signature namespace**, where the keys are module signature names and the entities are module signatures.
|
||||
- The **type signature namespace**, where the keys are type signature names and the entities are type signatures.
|
||||
- The **predicate signature namespace**, where the keys are pairs of predicate signature names and arities,
|
||||
and the entities are predicate signatures.
|
||||
|
||||
It's important to know that there is no relation between names in different namespaces.
|
||||
The six namespaces of any module are not completely independent of each other:
|
||||
|
||||
- No keys may be shared between the **module namespace** and the **module signature namespace**.
|
||||
- No keys may be shared between the **type namespace** and the **type signature namespace**.
|
||||
- No keys may be shared between the **module namespace** and the **type signature namespace**.
|
||||
- No keys may be shared between the **type namespace** and the **module signature namespace**.
|
||||
- No keys may be shared between the **predicate namespace** and the **predicate signature namespace**.
|
||||
- No keys may be shared between the **module signature namespace** and the **type signature namespace**.
|
||||
|
||||
There is no relation between names in namespaces of different modules.
|
||||
For example, two different modules can define a predicate ``getLocation()`` without confusion. As long as
|
||||
it's clear which namespace you are in, the QL compiler resolves the name to the correct predicate.
|
||||
|
||||
@@ -182,11 +199,12 @@ The namespaces containing all the built-in entities are called **global namespac
|
||||
and are automatically available in any module.
|
||||
In particular:
|
||||
|
||||
- The **global module namespace** is empty.
|
||||
- The **global module namespace** has a single entry ``QlBuiltins``.
|
||||
- The **global type namespace** has entries for the :ref:`primitive types <primitive-types>` ``int``, ``float``,
|
||||
``string``, ``boolean``, and ``date``, as well as any :ref:`database types <database-types>` defined in the database schema.
|
||||
- The **global predicate namespace** includes all the `built-in predicates <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#built-ins>`_,
|
||||
as well as any :ref:`database predicates <database-predicates>`.
|
||||
- The **global signature namespaces** are empty.
|
||||
|
||||
In practice, this means that you can use the built-in types and predicates directly in a QL module (without
|
||||
importing any libraries). You can also use any database predicates and types directly—these depend on the
|
||||
|
||||
@@ -86,13 +86,25 @@ use information from the database.
|
||||
Name resolution
|
||||
---------------
|
||||
|
||||
All modules have three environments that dictate name resolution. These are multimaps of keys to declarations.
|
||||
All modules have six environments that dictate name resolution. These are multimaps of keys to declarations.
|
||||
|
||||
The environments are:
|
||||
|
||||
- The *module environment*, whose keys are module names and whose values are modules.
|
||||
- The *type environment*, whose keys are type names and whose values are types.
|
||||
- The *predicate environment*, whose keys are pairs of predicate names and arities and whose values are predicates.
|
||||
- The *module signature environment*, whose keys are module signature names and whose values are module signatures.
|
||||
- The *type signature environment*, whose keys are type signature names and whose values are type signatures.
|
||||
- The *predicate signature environment*, whose keys are pairs of predicate signature names and arities and whose values are predicate signatures.
|
||||
|
||||
For each module, some namespaces are enforced to be disjoint:
|
||||
|
||||
- No keys may be shared between the **module namespace** and the **module signature namespace**.
|
||||
- No keys may be shared between the **type namespace** and the **type signature namespace**.
|
||||
- No keys may be shared between the **module namespace** and the **type signature namespace**.
|
||||
- No keys may be shared between the **type namespace** and the **module signature namespace**.
|
||||
- No keys may be shared between the **predicate namespace** and the **predicate signature namespace**.
|
||||
- No keys may be shared between the **module signature namespace** and the **type signature namespace**.
|
||||
|
||||
If not otherwise specified, then the environment for a piece of syntax is the same as the environment of its enclosing syntax.
|
||||
|
||||
@@ -108,12 +120,14 @@ A *definite* environment has at most one entry for each key. Resolution is uniqu
|
||||
Global environments
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The global module environment is empty.
|
||||
The global module environment has a single entry ``QlBuiltins``.
|
||||
|
||||
The global type environment has entries for the primitive types ``int``, ``float``, ``string``, ``boolean``, and ``date``, as well as any types defined in the database schema.
|
||||
|
||||
The global predicate environment includes all the built-in classless predicates, as well as any extensional predicates declared in the database schema.
|
||||
|
||||
The three global signature environments are empty.
|
||||
|
||||
The program is invalid if any of these environments is not definite.
|
||||
|
||||
Module environments
|
||||
|
||||
@@ -148,6 +148,7 @@ and the CodeQL library pack ``codeql/javascript-all`` (`changelog <https://githu
|
||||
EJS, templating language
|
||||
electron, Runtime environment
|
||||
express, Server
|
||||
Fastify, Server
|
||||
handlebars, templating language
|
||||
hapi, Server
|
||||
hogan, templating language
|
||||
@@ -166,6 +167,7 @@ and the CodeQL library pack ``codeql/javascript-all`` (`changelog <https://githu
|
||||
react, HTML framework
|
||||
react native, HTML framework
|
||||
request, Network communicator
|
||||
restify, Server
|
||||
sequelize, Database
|
||||
socket.io, Network communicator
|
||||
sqlite3, Database
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.4.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.4.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
3
go/ql/lib/change-notes/released/0.4.1.md
Normal file
3
go/ql/lib/change-notes/released/0.4.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.4.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.4.0
|
||||
lastReleaseVersion: 0.4.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-all
|
||||
version: 0.4.1-dev
|
||||
version: 0.4.2-dev
|
||||
groups: go
|
||||
dbscheme: go.dbscheme
|
||||
extractor: go
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import go
|
||||
private import semmle.go.dataflow.DataFlowForStringsNewReplacer
|
||||
|
||||
/** Provides predicates and classes for working with string operations. */
|
||||
module StringOps {
|
||||
@@ -162,6 +163,114 @@ module StringOps {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression that is equivalent to `strings.ReplaceAll(s, old, new)`.
|
||||
*
|
||||
* Extend this class to refine existing API models. If you want to model new APIs,
|
||||
* extend `StringOps::ReplaceAll::Range` instead.
|
||||
*/
|
||||
class ReplaceAll extends DataFlow::Node instanceof ReplaceAll::Range {
|
||||
/**
|
||||
* Gets the `old` in `strings.ReplaceAll(s, old, new)`.
|
||||
*/
|
||||
string getReplacedString() { result = super.getReplacedString() }
|
||||
}
|
||||
|
||||
/** Provides predicates and classes for working with prefix checks. */
|
||||
module ReplaceAll {
|
||||
/**
|
||||
* An expression that is equivalent to `strings.ReplaceAll(s, old, new)`.
|
||||
*
|
||||
* Extend this class to model new APIs. If you want to refine existing API models, extend
|
||||
* `StringOps::ReplaceAll` instead.
|
||||
*/
|
||||
abstract class Range extends DataFlow::Node {
|
||||
/**
|
||||
* Gets the `old` in `strings.ReplaceAll(s, old, new)`.
|
||||
*/
|
||||
abstract string getReplacedString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `strings.ReplaceAll` or `strings.Replace` with a negative `n`
|
||||
* so that all instances are replaced.
|
||||
*/
|
||||
private class StringsReplaceAll extends Range, DataFlow::CallNode {
|
||||
StringsReplaceAll() {
|
||||
exists(string name | this.getTarget().hasQualifiedName("strings", name) |
|
||||
name = "ReplaceAll"
|
||||
or
|
||||
name = "Replace" and
|
||||
this.getArgument(3).getNumericValue() < 0
|
||||
)
|
||||
}
|
||||
|
||||
override string getReplacedString() { result = this.getArgument(1).getStringValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `strings.NewReplacer`.
|
||||
*/
|
||||
private class StringsNewReplacerCall extends DataFlow::CallNode {
|
||||
StringsNewReplacerCall() { this.getTarget().hasQualifiedName("strings", "NewReplacer") }
|
||||
|
||||
/**
|
||||
* Gets an argument to this call corresponding to a string that will be
|
||||
* replaced.
|
||||
*/
|
||||
DataFlow::Node getAReplacedArgument() {
|
||||
exists(int n | n % 2 = 0 and result = this.getArgument(n))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A configuration for tracking flow from a call to `strings.NewReplacer` to
|
||||
* the receiver of a call to `strings.Replacer.Replace` or
|
||||
* `strings.Replacer.WriteString`.
|
||||
*/
|
||||
private class StringsNewReplacerConfiguration extends DataFlowForStringsNewReplacer::Configuration {
|
||||
StringsNewReplacerConfiguration() { this = "StringsNewReplacerConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
source instanceof StringsNewReplacerCall
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
sink = call.getReceiver() and
|
||||
call.getTarget().hasQualifiedName("strings", "Replacer", ["Replace", "WriteString"])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `strings.Replacer.Replace` or `strings.Replacer.WriteString`.
|
||||
*/
|
||||
private class StringsReplacerReplaceOrWriteString extends Range {
|
||||
string replacedString;
|
||||
|
||||
StringsReplacerReplaceOrWriteString() {
|
||||
exists(
|
||||
StringsNewReplacerConfiguration config, StringsNewReplacerCall source,
|
||||
DataFlow::Node sink, DataFlow::MethodCallNode call
|
||||
|
|
||||
config.hasFlow(source, sink) and
|
||||
sink = call.getReceiver() and
|
||||
replacedString = source.getAReplacedArgument().getStringValue() and
|
||||
(
|
||||
call.getTarget().hasQualifiedName("strings", "Replacer", "Replace") and
|
||||
this = call.getResult()
|
||||
or
|
||||
call.getTarget().hasQualifiedName("strings", "Replacer", "WriteString") and
|
||||
this = call.getArgument(1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override string getReplacedString() { result = replacedString }
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides predicates and classes for working with Printf-style formatters. */
|
||||
module Formatting {
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Provides a library for local (intra-procedural) and global (inter-procedural)
|
||||
* data flow analysis: deciding whether data can flow from a _source_ to a
|
||||
* _sink_.
|
||||
*
|
||||
* Unless configured otherwise, _flow_ means that the exact value of
|
||||
* the source may reach the sink. We do not track flow across pointer
|
||||
* dereferences or array indexing. To track these types of flow, where the
|
||||
* exact value may not be preserved, import
|
||||
* `semmle.code.go.dataflow.TaintTracking`.
|
||||
*
|
||||
* To use global (interprocedural) data flow, extend the class
|
||||
* `DataFlow::Configuration` as documented on that class. To use local
|
||||
* (intraprocedural) data flow, invoke `DataFlow::localFlow` or
|
||||
* `DataFlow::LocalFlowStep` with arguments of type `DataFlow::Node`.
|
||||
*/
|
||||
|
||||
import go
|
||||
|
||||
/**
|
||||
* Provides a library for local (intra-procedural) and global (inter-procedural)
|
||||
* data flow analysis.
|
||||
*/
|
||||
module DataFlowForStringsNewReplacer {
|
||||
import semmle.go.dataflow.internal.DataFlowImplForStringsNewReplacer
|
||||
import Properties
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user