mirror of
https://github.com/github/codeql.git
synced 2026-05-20 22:27:18 +02:00
Compare commits
32 Commits
aml-auto-e
...
codeql-cli
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12ca801ecf | ||
|
|
a9d80a5a48 | ||
|
|
950445500a | ||
|
|
d6b8f25312 | ||
|
|
9745073024 | ||
|
|
9fd9a04c2f | ||
|
|
e8d13d156d | ||
|
|
b2431d0b50 | ||
|
|
d8b352c2e6 | ||
|
|
623ba7926f | ||
|
|
99d9fe14c8 | ||
|
|
7c12139c9e | ||
|
|
7dc5bdafe3 | ||
|
|
2aec53b7fb | ||
|
|
e2caf3e8c0 | ||
|
|
3b4ad3c4f1 | ||
|
|
f318dd5e0e | ||
|
|
ef98ce16f8 | ||
|
|
711e769382 | ||
|
|
b951e94d85 | ||
|
|
f20825ae55 | ||
|
|
b21883292d | ||
|
|
a904438828 | ||
|
|
5cd4e0d3b1 | ||
|
|
40b1825ef1 | ||
|
|
9a3b540551 | ||
|
|
fb12d85d3c | ||
|
|
0479a59640 | ||
|
|
5e06277b38 | ||
|
|
d0ecb9f54b | ||
|
|
badb2b7f13 | ||
|
|
0f63bc077f |
@@ -462,6 +462,9 @@
|
||||
],
|
||||
"SSA C#": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
|
||||
"csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll",
|
||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll",
|
||||
"swift/ql/lib/codeql/swift/dataflow/internal/SsaImplCommon.qll"
|
||||
@@ -581,22 +584,22 @@
|
||||
],
|
||||
"Swift declarations test file": [
|
||||
"swift/ql/test/extractor-tests/declarations/declarations.swift",
|
||||
"swift/ql/test/library-tests/ast/declarations.swift"
|
||||
"swift/ql/test/library-tests/parent/declarations.swift"
|
||||
],
|
||||
"Swift statements test file": [
|
||||
"swift/ql/test/extractor-tests/statements/statements.swift",
|
||||
"swift/ql/test/library-tests/ast/statements.swift"
|
||||
"swift/ql/test/library-tests/parent/statements.swift"
|
||||
],
|
||||
"Swift expressions test file": [
|
||||
"swift/ql/test/extractor-tests/expressions/expressions.swift",
|
||||
"swift/ql/test/library-tests/ast/expressions.swift"
|
||||
"swift/ql/test/library-tests/parent/expressions.swift"
|
||||
],
|
||||
"Swift patterns test file": [
|
||||
"swift/ql/test/extractor-tests/patterns/patterns.swift",
|
||||
"swift/ql/test/library-tests/ast/patterns.swift"
|
||||
"swift/ql/test/library-tests/parent/patterns.swift"
|
||||
],
|
||||
"IncompleteMultiCharacterSanitization JS/Ruby": [
|
||||
"javascript/ql/lib/semmle/javascript/security/IncompleteMultiCharacterSanitizationQuery.qll",
|
||||
"ruby/ql/lib/codeql/ruby/security/IncompleteMultiCharacterSanitizationQuery.qll"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
## 0.3.5
|
||||
|
||||
## 0.3.4
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added support for getting the link targets of global and namespace variables.
|
||||
* Added a `BlockAssignExpr` class, which models a `memcpy`-like operation used in compiler generated copy/move constructors and assignment operations.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Added a `BlockAssignExpr` class, which models a `memcpy`-like operation used in compiler generated copy/move constructors and assignment operations.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* All deprecated predicates/classes/modules that have been deprecated for over a year have been
|
||||
deleted.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Added support for getting the link targets of global and namespace variables.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
15
cpp/ql/lib/change-notes/released/0.3.4.md
Normal file
15
cpp/ql/lib/change-notes/released/0.3.4.md
Normal file
@@ -0,0 +1,15 @@
|
||||
## 0.3.4
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### New Features
|
||||
|
||||
* Added support for getting the link targets of global and namespace variables.
|
||||
* Added a `BlockAssignExpr` class, which models a `memcpy`-like operation used in compiler generated copy/move constructors and assignment operations.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
|
||||
1
cpp/ql/lib/change-notes/released/0.3.5.md
Normal file
1
cpp/ql/lib/change-notes/released/0.3.5.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.3.5
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.3.3
|
||||
lastReleaseVersion: 0.3.5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.3.4-dev
|
||||
version: 0.3.5
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -404,10 +404,7 @@ class Class extends UserType {
|
||||
* compiled for. For this reason, the `is_pod_class` predicate is
|
||||
* generated by the extractor.
|
||||
*/
|
||||
predicate isPod() { is_pod_class(underlyingElement(this)) }
|
||||
|
||||
/** DEPRECATED: Alias for isPod */
|
||||
deprecated predicate isPOD() { this.isPod() }
|
||||
predicate isPOD() { is_pod_class(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this class, struct or union is a standard-layout class
|
||||
|
||||
@@ -79,17 +79,17 @@ predicate isAggregateType03(Type t) {
|
||||
* user-defined copy assignment operator and no user-defined destructor.
|
||||
* A POD class is a class that is either a POD-struct or a POD-union.
|
||||
*/
|
||||
predicate isPodClass03(Class c) {
|
||||
predicate isPODClass03(Class c) {
|
||||
isAggregateClass03(c) and
|
||||
not exists(Variable v |
|
||||
v.getDeclaringType() = c and
|
||||
not v.isStatic()
|
||||
|
|
||||
not isPodType03(v.getType())
|
||||
not isPODType03(v.getType())
|
||||
or
|
||||
exists(ArrayType at |
|
||||
at = v.getType() and
|
||||
not isPodType03(at.getBaseType())
|
||||
not isPODType03(at.getBaseType())
|
||||
)
|
||||
or
|
||||
v.getType() instanceof ReferenceType
|
||||
@@ -104,9 +104,6 @@ predicate isPodClass03(Class c) {
|
||||
)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for isPodClass03 */
|
||||
deprecated predicate isPODClass03 = isPodClass03/1;
|
||||
|
||||
/**
|
||||
* Holds if `t` is a POD type, according to the rules specified in
|
||||
* C++03 3.9(10):
|
||||
@@ -115,17 +112,14 @@ deprecated predicate isPODClass03 = isPodClass03/1;
|
||||
* such types and cv-qualified versions of these types (3.9.3) are
|
||||
* collectively called POD types.
|
||||
*/
|
||||
predicate isPodType03(Type t) {
|
||||
predicate isPODType03(Type t) {
|
||||
exists(Type ut | ut = t.getUnderlyingType() |
|
||||
isScalarType03(ut)
|
||||
or
|
||||
isPodClass03(ut)
|
||||
isPODClass03(ut)
|
||||
or
|
||||
exists(ArrayType at | at = ut and isPodType03(at.getBaseType()))
|
||||
exists(ArrayType at | at = ut and isPODType03(at.getBaseType()))
|
||||
or
|
||||
isPodType03(ut.(SpecifiedType).getUnspecifiedType())
|
||||
isPODType03(ut.(SpecifiedType).getUnspecifiedType())
|
||||
)
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for isPodType03 */
|
||||
deprecated predicate isPODType03 = isPodType03/1;
|
||||
|
||||
@@ -238,7 +238,7 @@ predicate dependsOnTransitive(DependsSource src, Element dest) {
|
||||
/**
|
||||
* A dependency that targets a TypeDeclarationEntry.
|
||||
*/
|
||||
private predicate dependsOnTde(Element src, Type t, TypeDeclarationEntry dest) {
|
||||
private predicate dependsOnTDE(Element src, Type t, TypeDeclarationEntry dest) {
|
||||
dependsOnTransitive(src, t) and
|
||||
getDeclarationEntries(t, dest)
|
||||
}
|
||||
@@ -247,8 +247,8 @@ private predicate dependsOnTde(Element src, Type t, TypeDeclarationEntry dest) {
|
||||
* A dependency that targets a visible TypeDeclarationEntry.
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate dependsOnVisibleTde(Element src, Type t, TypeDeclarationEntry dest) {
|
||||
dependsOnTde(src, t, dest) and
|
||||
private predicate dependsOnVisibleTDE(Element src, Type t, TypeDeclarationEntry dest) {
|
||||
dependsOnTDE(src, t, dest) and
|
||||
exists(File g | g = dest.getFile() |
|
||||
exists(File f | f = src.getFile() | f.getAnIncludedFile*() = g)
|
||||
)
|
||||
@@ -260,8 +260,8 @@ private predicate dependsOnVisibleTde(Element src, Type t, TypeDeclarationEntry
|
||||
private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest) {
|
||||
exists(Type t |
|
||||
// dependency from a Type use -> unique visible TDE
|
||||
dependsOnVisibleTde(src, t, dest) and
|
||||
strictcount(TypeDeclarationEntry alt | dependsOnVisibleTde(src, t, alt)) = 1
|
||||
dependsOnVisibleTDE(src, t, dest) and
|
||||
strictcount(TypeDeclarationEntry alt | dependsOnVisibleTDE(src, t, alt)) = 1
|
||||
)
|
||||
or
|
||||
exists(TypedefType mid |
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import semmle.code.cpp.Macro
|
||||
|
||||
/** A macro defining NULL. */
|
||||
class NullMacro extends Macro {
|
||||
NullMacro() { this.getHead() = "NULL" }
|
||||
class NULLMacro extends Macro {
|
||||
NULLMacro() { this.getHead() = "NULL" }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for NullMacro */
|
||||
deprecated class NULLMacro = NullMacro;
|
||||
|
||||
/** A use of the NULL macro. */
|
||||
class NULL extends Literal {
|
||||
NULL() { exists(NullMacro nm | this = nm.getAnInvocation().getAnExpandedElement()) }
|
||||
NULL() { exists(NULLMacro nm | this = nm.getAnInvocation().getAnExpandedElement()) }
|
||||
}
|
||||
|
||||
@@ -474,7 +474,7 @@ module FlowVar_internal {
|
||||
}
|
||||
|
||||
/** Type-specialized version of `getEnclosingElement`. */
|
||||
private ControlFlowNode getCfnParent(ControlFlowNode node) { result = node.getEnclosingElement() }
|
||||
private ControlFlowNode getCFNParent(ControlFlowNode node) { result = node.getEnclosingElement() }
|
||||
|
||||
/**
|
||||
* A for-loop or while-loop whose condition is always true upon entry but not
|
||||
@@ -526,7 +526,7 @@ module FlowVar_internal {
|
||||
}
|
||||
|
||||
private predicate bbInLoopCondition(BasicBlock bb) {
|
||||
getCfnParent*(bb.getANode()) = this.(Loop).getCondition()
|
||||
getCFNParent*(bb.getANode()) = this.(Loop).getCondition()
|
||||
}
|
||||
|
||||
private predicate bbInLoop(BasicBlock bb) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import SsaInternals as Ssa
|
||||
|
||||
class BasicBlock = IRBlock;
|
||||
|
||||
class SourceVariable = Ssa::SourceVariable;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock extends IRBlock {
|
||||
ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction }
|
||||
}
|
||||
|
||||
predicate variableWrite = Ssa::variableWrite/4;
|
||||
|
||||
predicate variableRead = Ssa::variableRead/4;
|
||||
@@ -1,10 +1,10 @@
|
||||
import SsaImplCommon
|
||||
private import cpp as Cpp
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import DataFlowUtil
|
||||
private import DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.code.cpp.models.interfaces.Allocation as Alloc
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
|
||||
private import SsaImplCommon as SsaImplCommon
|
||||
|
||||
private module SourceVariables {
|
||||
private newtype TSourceVariable =
|
||||
@@ -38,6 +38,8 @@ private module SourceVariables {
|
||||
}
|
||||
}
|
||||
|
||||
import SourceVariables
|
||||
|
||||
cached
|
||||
private newtype TDefOrUse =
|
||||
TExplicitDef(Instruction store) { explicitWrite(_, store, _) } or
|
||||
@@ -84,7 +86,7 @@ abstract class Def extends DefOrUse {
|
||||
Instruction getInstruction() { result = store }
|
||||
|
||||
/** Gets the variable that is defined by this definition. */
|
||||
abstract SourceVariables::SourceVariable getSourceVariable();
|
||||
abstract SourceVariable getSourceVariable();
|
||||
|
||||
/** Holds if this definition is guaranteed to happen. */
|
||||
abstract predicate isCertain();
|
||||
@@ -101,10 +103,10 @@ abstract class Def extends DefOrUse {
|
||||
private class ExplicitDef extends Def, TExplicitDef {
|
||||
ExplicitDef() { this = TExplicitDef(store) }
|
||||
|
||||
override SourceVariables::SourceVariable getSourceVariable() {
|
||||
override SourceVariable getSourceVariable() {
|
||||
exists(VariableInstruction var |
|
||||
explicitWrite(_, this.getInstruction(), var) and
|
||||
result.(SourceVariables::SourceIRVariable).getIRVariable() = var.getIRVariable()
|
||||
result.(SourceIRVariable).getIRVariable() = var.getIRVariable()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -114,11 +116,11 @@ private class ExplicitDef extends Def, TExplicitDef {
|
||||
private class ParameterDef extends Def, TInitializeParam {
|
||||
ParameterDef() { this = TInitializeParam(store) }
|
||||
|
||||
override SourceVariables::SourceVariable getSourceVariable() {
|
||||
result.(SourceVariables::SourceIRVariable).getIRVariable() =
|
||||
override SourceVariable getSourceVariable() {
|
||||
result.(SourceIRVariable).getIRVariable() =
|
||||
store.(InitializeParameterInstruction).getIRVariable()
|
||||
or
|
||||
result.(SourceVariables::SourceIRVariableIndirection).getUnderlyingIRVariable() =
|
||||
result.(SourceIRVariableIndirection).getUnderlyingIRVariable() =
|
||||
store.(InitializeIndirectionInstruction).getIRVariable()
|
||||
}
|
||||
|
||||
@@ -136,7 +138,7 @@ abstract class Use extends DefOrUse {
|
||||
override string toString() { result = "Use" }
|
||||
|
||||
/** Gets the variable that is used by this use. */
|
||||
abstract SourceVariables::SourceVariable getSourceVariable();
|
||||
abstract SourceVariable getSourceVariable();
|
||||
|
||||
override IRBlock getBlock() { result = use.getUse().getBlock() }
|
||||
|
||||
@@ -146,14 +148,12 @@ abstract class Use extends DefOrUse {
|
||||
private class ExplicitUse extends Use, TExplicitUse {
|
||||
ExplicitUse() { this = TExplicitUse(use) }
|
||||
|
||||
override SourceVariables::SourceVariable getSourceVariable() {
|
||||
override SourceVariable getSourceVariable() {
|
||||
exists(VariableInstruction var |
|
||||
use.getDef() = var and
|
||||
if use.getUse() instanceof ReadSideEffectInstruction
|
||||
then
|
||||
result.(SourceVariables::SourceIRVariableIndirection).getUnderlyingIRVariable() =
|
||||
var.getIRVariable()
|
||||
else result.(SourceVariables::SourceIRVariable).getIRVariable() = var.getIRVariable()
|
||||
then result.(SourceIRVariableIndirection).getUnderlyingIRVariable() = var.getIRVariable()
|
||||
else result.(SourceIRVariable).getIRVariable() = var.getIRVariable()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -161,11 +161,10 @@ private class ExplicitUse extends Use, TExplicitUse {
|
||||
private class ReturnParameterIndirection extends Use, TReturnParamIndirection {
|
||||
ReturnParameterIndirection() { this = TReturnParamIndirection(use) }
|
||||
|
||||
override SourceVariables::SourceVariable getSourceVariable() {
|
||||
override SourceVariable getSourceVariable() {
|
||||
exists(ReturnIndirectionInstruction ret |
|
||||
returnParameterIndirection(use, ret) and
|
||||
result.(SourceVariables::SourceIRVariableIndirection).getUnderlyingIRVariable() =
|
||||
ret.getIRVariable()
|
||||
result.(SourceIRVariableIndirection).getUnderlyingIRVariable() = ret.getIRVariable()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -611,45 +610,27 @@ private module Cached {
|
||||
|
||||
import Cached
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
class BasicBlock = IRBlock;
|
||||
|
||||
class SourceVariable = SourceVariables::SourceVariable;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock extends IRBlock {
|
||||
ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
|
||||
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
|
||||
*/
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
exists(Def def |
|
||||
def.hasIndexInBlock(bb, i) and
|
||||
v = def.getSourceVariable() and
|
||||
(if def.isCertain() then certain = true else certain = false)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
|
||||
* `certain` is `true` if the read is guaranteed. For C++, this is always the case.
|
||||
*/
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(Use use |
|
||||
use.hasIndexInBlock(bb, i) and
|
||||
v = use.getSourceVariable() and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
/**
|
||||
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
|
||||
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
|
||||
*/
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
exists(Def def |
|
||||
def.hasIndexInBlock(bb, i) and
|
||||
v = def.getSourceVariable() and
|
||||
(if def.isCertain() then certain = true else certain = false)
|
||||
)
|
||||
}
|
||||
|
||||
import SsaImplCommon::Make<SsaInput>
|
||||
/**
|
||||
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
|
||||
* `certain` is `true` if the read is guaranteed. For C++, this is always the case.
|
||||
*/
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(Use use |
|
||||
use.hasIndexInBlock(bb, i) and
|
||||
v = use.getSourceVariable() and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ private ControlFlowNode mostRecentSideEffect(ControlFlowNode node) {
|
||||
|
||||
/** Used to represent the "global value number" of an expression. */
|
||||
cached
|
||||
private newtype GvnBase =
|
||||
private newtype GVNBase =
|
||||
GVN_IntConst(int val, Type t) { mk_IntConst(val, t, _) } or
|
||||
GVN_FloatConst(float val, Type t) { mk_FloatConst(val, t, _) } or
|
||||
// If the local variable does not have a defining value, then
|
||||
@@ -221,8 +221,8 @@ private newtype GvnBase =
|
||||
* expression with this `GVN` and using its `toString` and `getLocation`
|
||||
* methods.
|
||||
*/
|
||||
class GVN extends GvnBase {
|
||||
GVN() { this instanceof GvnBase }
|
||||
class GVN extends GVNBase {
|
||||
GVN() { this instanceof GVNBase }
|
||||
|
||||
/** Gets an expression that has this GVN. */
|
||||
Expr getAnExpr() { this = globalValueNumber(result) }
|
||||
|
||||
@@ -63,17 +63,17 @@ class VariableDeclarationLine extends TVariableDeclarationInfo {
|
||||
/**
|
||||
* Gets a `VariableDeclarationEntry` on this line.
|
||||
*/
|
||||
VariableDeclarationEntry getAVde() { vdeInfo(result, c, f, line) }
|
||||
VariableDeclarationEntry getAVDE() { vdeInfo(result, c, f, line) }
|
||||
|
||||
/**
|
||||
* Gets the start column of the first `VariableDeclarationEntry` on this line.
|
||||
*/
|
||||
int getStartColumn() { result = min(this.getAVde().getLocation().getStartColumn()) }
|
||||
int getStartColumn() { result = min(this.getAVDE().getLocation().getStartColumn()) }
|
||||
|
||||
/**
|
||||
* Gets the end column of the last `VariableDeclarationEntry` on this line.
|
||||
*/
|
||||
int getEndColumn() { result = max(this.getAVde().getLocation().getEndColumn()) }
|
||||
int getEndColumn() { result = max(this.getAVDE().getLocation().getEndColumn()) }
|
||||
|
||||
/**
|
||||
* Gets the rank of this `VariableDeclarationLine` in its file and class
|
||||
@@ -134,13 +134,13 @@ class VariableDeclarationGroup extends VariableDeclarationLine {
|
||||
count(VariableDeclarationLine l |
|
||||
l = this.getProximateNext*()
|
||||
|
|
||||
l.getAVde().getVariable().getName()
|
||||
l.getAVDE().getVariable().getName()
|
||||
)
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
this.getCount() = 1 and
|
||||
result = "declaration of " + this.getAVde().getVariable().getName()
|
||||
result = "declaration of " + this.getAVDE().getVariable().getName()
|
||||
or
|
||||
this.getCount() > 1 and
|
||||
result = "group of " + this.getCount() + " fields here"
|
||||
|
||||
@@ -29,4 +29,7 @@ where
|
||||
n = strictcount(ComplexStmt s | s = b.getAStmt()) and
|
||||
n > 3 and
|
||||
complexStmt = b.getAStmt()
|
||||
select b, "Block with too many statements (" + n.toString() + " complex statements in the block)."
|
||||
select b,
|
||||
"Block with too many statements (" + n.toString() +
|
||||
" complex statements in the block). Complex statements at: $@", complexStmt,
|
||||
complexStmt.toString()
|
||||
|
||||
@@ -110,4 +110,4 @@ where
|
||||
emptyBlock(s, eb) and
|
||||
not emptyBlockContainsNonchild(eb) and
|
||||
not lineComment(eb)
|
||||
select eb, "Empty block without comment."
|
||||
select eb, "Empty block without comment"
|
||||
|
||||
@@ -16,7 +16,7 @@ import cpp
|
||||
class JumpTarget extends Stmt {
|
||||
JumpTarget() { exists(GotoStmt g | g.getTarget() = this) }
|
||||
|
||||
FunctionDeclarationEntry getFde() { result.getBlock() = this.getParentStmt+() }
|
||||
FunctionDeclarationEntry getFDE() { result.getBlock() = this.getParentStmt+() }
|
||||
|
||||
predicate isForward() {
|
||||
exists(GotoStmt g | g.getTarget() = this |
|
||||
@@ -33,8 +33,8 @@ class JumpTarget extends Stmt {
|
||||
|
||||
from FunctionDeclarationEntry fde, int nforward, int nbackward
|
||||
where
|
||||
nforward = strictcount(JumpTarget t | t.getFde() = fde and t.isForward()) and
|
||||
nbackward = strictcount(JumpTarget t | t.getFde() = fde and t.isBackward()) and
|
||||
nforward = strictcount(JumpTarget t | t.getFDE() = fde and t.isForward()) and
|
||||
nbackward = strictcount(JumpTarget t | t.getFDE() = fde and t.isBackward()) and
|
||||
nforward != 1 and
|
||||
nbackward != 1
|
||||
select fde,
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
## 0.3.4
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Cleartext storage of sensitive information in buffer" (`cpp/cleartext-storage-buffer`) query has been improved to produce fewer false positives.
|
||||
|
||||
## 0.3.2
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -12,4 +12,4 @@
|
||||
import CommentedOutCode
|
||||
|
||||
from CommentedOutCode comment
|
||||
select comment, "This comment appears to contain commented-out code."
|
||||
select comment, "This comment appears to contain commented-out code"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* @name Sign check of bitwise operation
|
||||
* @description Checking the sign of the result of a bitwise operation may yield unexpected results.
|
||||
* @description Checking the sign of a bitwise operation often has surprising
|
||||
* edge cases.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision high
|
||||
@@ -25,4 +26,4 @@ where
|
||||
forall(int op | op = lhs.(BitwiseAndExpr).getAnOperand().getValue().toInt() | op < 0) and
|
||||
// exception for cases involving macros
|
||||
not e.isAffectedByMacro()
|
||||
select e, "Potentially unsafe sign check of a bitwise operation."
|
||||
select e, "Potential unsafe sign check of a bitwise operation."
|
||||
|
||||
@@ -21,4 +21,4 @@ where
|
||||
FloatingPointType and
|
||||
not ro.getAnOperand().isConstant() and // comparisons to constants generate too many false positives
|
||||
not left.(VariableAccess).getTarget() = right.(VariableAccess).getTarget() // skip self comparison
|
||||
select ro, "Equality checks on floating point values can yield unexpected results."
|
||||
select ro, "Equality test on floating point values may not behave as expected."
|
||||
|
||||
@@ -13,11 +13,10 @@
|
||||
|
||||
import cpp
|
||||
|
||||
from EnumSwitch es, float missing, float total, EnumConstant case
|
||||
from EnumSwitch es, float missing, float total
|
||||
where
|
||||
not es.hasDefaultCase() and
|
||||
missing = count(es.getAMissingCase()) and
|
||||
total = missing + count(es.getASwitchCase()) and
|
||||
missing / total < 0.3 and
|
||||
case = es.getAMissingCase()
|
||||
select es, "Switch statement does not have a case for $@.", case, case.getName()
|
||||
missing / total < 0.3
|
||||
select es, "Switch statement is missing case for " + es.getAMissingCase().getName()
|
||||
|
||||
@@ -163,19 +163,46 @@ TGlobalAddress globalAddress(Instruction instr) {
|
||||
result = globalAddress(instr.(PointerOffsetInstruction).getLeft())
|
||||
}
|
||||
|
||||
/** Gets a `StoreInstruction` that may be executed after executing `store`. */
|
||||
pragma[inline]
|
||||
StoreInstruction getAStoreStrictlyAfter(StoreInstruction store) {
|
||||
exists(IRBlock block, int index1, int index2 |
|
||||
block.getInstruction(index1) = store and
|
||||
block.getInstruction(index2) = result and
|
||||
index2 > index1
|
||||
/**
|
||||
* Gets a first `StoreInstruction` that writes to address `globalAddress` reachable
|
||||
* from `block`.
|
||||
*/
|
||||
StoreInstruction getFirstStore(IRBlock block, TGlobalAddress globalAddress) {
|
||||
1 = getStoreRank(result, block, globalAddress)
|
||||
or
|
||||
not exists(getStoreRank(_, block, globalAddress)) and
|
||||
result = getFirstStore(block.getASuccessor(), globalAddress)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rank of `store` in block `block` (i.e., a rank of `1` means that it is the
|
||||
* first `store` to write to `globalAddress`, a rank of `2` means it's the second, etc.)
|
||||
*/
|
||||
int getStoreRank(StoreInstruction store, IRBlock block, TGlobalAddress globalAddress) {
|
||||
blockStoresToAddress(block, _, store, globalAddress) and
|
||||
store =
|
||||
rank[result](StoreInstruction anotherStore, int i |
|
||||
blockStoresToAddress(_, i, anotherStore, globalAddress)
|
||||
|
|
||||
anotherStore order by i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a next subsequent `StoreInstruction` to write to `globalAddress`
|
||||
* after `store` has done so.
|
||||
*/
|
||||
StoreInstruction getANextStoreTo(StoreInstruction store, TGlobalAddress globalAddress) {
|
||||
exists(IRBlock block, int rnk |
|
||||
rnk = getStoreRank(store, block, globalAddress) and
|
||||
rnk + 1 = getStoreRank(result, block, globalAddress)
|
||||
)
|
||||
or
|
||||
exists(IRBlock block1, IRBlock block2 |
|
||||
store.getBlock() = block1 and
|
||||
result.getBlock() = block2 and
|
||||
block1.getASuccessor+() = block2
|
||||
exists(IRBlock block, int rnk, IRBlock succ |
|
||||
rnk = getStoreRank(store, block, globalAddress) and
|
||||
not rnk + 1 = getStoreRank(_, block, globalAddress) and
|
||||
succ = block.getASuccessor() and
|
||||
result = getFirstStore(succ, globalAddress)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -192,7 +219,7 @@ predicate stackAddressEscapes(
|
||||
stackPointerFlowsToUse(store.getSourceValue(), vai)
|
||||
) and
|
||||
// Ensure there's no subsequent store that overrides the global address.
|
||||
not globalAddress = globalAddress(getAStoreStrictlyAfter(store).getDestinationAddress())
|
||||
not exists(getANextStoreTo(store, globalAddress))
|
||||
}
|
||||
|
||||
predicate blockStoresToAddress(
|
||||
|
||||
@@ -13,7 +13,7 @@ import SAL
|
||||
|
||||
from Parameter p, Call c, Expr arg
|
||||
where
|
||||
any(SalNotNull a).getDeclaration() = p and
|
||||
any(SALNotNull a).getDeclaration() = p and
|
||||
c.getTarget() = p.getFunction() and
|
||||
arg = c.getArgument(p.getIndex()) and
|
||||
nullValue(arg)
|
||||
|
||||
@@ -18,7 +18,7 @@ from Function f, FunctionCall call
|
||||
where
|
||||
call.getTarget() = f and
|
||||
call instanceof ExprInVoidContext and
|
||||
any(SalCheckReturn a).getDeclaration() = f and
|
||||
any(SALCheckReturn a).getDeclaration() = f and
|
||||
not getOptions().okToIgnoreReturnValue(call)
|
||||
select call, "Return value of $@ discarded although a SAL annotation " + "requires inspecting it.",
|
||||
f, f.getName()
|
||||
|
||||
@@ -11,7 +11,7 @@ import SAL
|
||||
|
||||
/** Holds if `e` has SAL annotation `name`. */
|
||||
predicate hasAnnotation(DeclarationEntry e, string name) {
|
||||
exists(SalAnnotation a |
|
||||
exists(SALAnnotation a |
|
||||
a.getMacro().getName() = name and
|
||||
a.getDeclarationEntry() = e
|
||||
)
|
||||
@@ -21,7 +21,7 @@ predicate hasAnnotation(DeclarationEntry e, string name) {
|
||||
predicate inheritsDeclAnnotations(DeclarationEntry e) {
|
||||
// Is directly annotated
|
||||
e.isDefinition() and
|
||||
exists(SalAnnotation a | a.getMacro().getName() = "_Use_decl_annotations_" |
|
||||
exists(SALAnnotation a | a.getMacro().getName() = "_Use_decl_annotations_" |
|
||||
a.getDeclarationEntry() = e
|
||||
)
|
||||
or
|
||||
|
||||
@@ -8,8 +8,8 @@ import cpp
|
||||
/**
|
||||
* A SAL macro defined in `sal.h` or a similar header file.
|
||||
*/
|
||||
class SalMacro extends Macro {
|
||||
SalMacro() {
|
||||
class SALMacro extends Macro {
|
||||
SALMacro() {
|
||||
this.getFile().getBaseName() =
|
||||
["sal.h", "specstrings_strict.h", "specstrings.h", "w32p.h", "minwindef.h"] and
|
||||
(
|
||||
@@ -22,18 +22,15 @@ class SalMacro extends Macro {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalMacro */
|
||||
deprecated class SALMacro = SalMacro;
|
||||
|
||||
pragma[noinline]
|
||||
private predicate isTopLevelMacroAccess(MacroAccess ma) { not exists(ma.getParentInvocation()) }
|
||||
|
||||
/**
|
||||
* An invocation of a SAL macro (excluding invocations inside other macros).
|
||||
*/
|
||||
class SalAnnotation extends MacroInvocation {
|
||||
SalAnnotation() {
|
||||
this.getMacro() instanceof SalMacro and
|
||||
class SALAnnotation extends MacroInvocation {
|
||||
SALAnnotation() {
|
||||
this.getMacro() instanceof SALMacro and
|
||||
isTopLevelMacroAccess(this)
|
||||
}
|
||||
|
||||
@@ -50,29 +47,23 @@ class SalAnnotation extends MacroInvocation {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalAnnotation */
|
||||
deprecated class SALAnnotation = SalAnnotation;
|
||||
|
||||
/**
|
||||
* A SAL macro indicating that the return value of a function should always be
|
||||
* checked.
|
||||
*/
|
||||
class SalCheckReturn extends SalAnnotation {
|
||||
SalCheckReturn() {
|
||||
this.getMacro().(SalMacro).getName() = ["_Check_return_", "_Must_inspect_result_"]
|
||||
class SALCheckReturn extends SALAnnotation {
|
||||
SALCheckReturn() {
|
||||
this.getMacro().(SALMacro).getName() = ["_Check_return_", "_Must_inspect_result_"]
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalCheckReturn */
|
||||
deprecated class SALCheckReturn = SalCheckReturn;
|
||||
|
||||
/**
|
||||
* A SAL macro indicating that a pointer variable or return value should not be
|
||||
* `NULL`.
|
||||
*/
|
||||
class SalNotNull extends SalAnnotation {
|
||||
SalNotNull() {
|
||||
exists(SalMacro m | m = this.getMacro() |
|
||||
class SALNotNull extends SALAnnotation {
|
||||
SALNotNull() {
|
||||
exists(SALMacro m | m = this.getMacro() |
|
||||
not m.getName().matches("%\\_opt\\_%") and
|
||||
(
|
||||
m.getName().matches("_In%") or
|
||||
@@ -89,15 +80,12 @@ class SalNotNull extends SalAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalNotNull */
|
||||
deprecated class SALNotNull = SalNotNull;
|
||||
|
||||
/**
|
||||
* A SAL macro indicating that a value may be `NULL`.
|
||||
*/
|
||||
class SalMaybeNull extends SalAnnotation {
|
||||
SalMaybeNull() {
|
||||
exists(SalMacro m | m = this.getMacro() |
|
||||
class SALMaybeNull extends SALAnnotation {
|
||||
SALMaybeNull() {
|
||||
exists(SALMacro m | m = this.getMacro() |
|
||||
m.getName().matches("%\\_opt\\_%") or
|
||||
m.getName().matches("\\_Ret_maybenull\\_%") or
|
||||
m.getName() = "_Result_nullonfailure_"
|
||||
@@ -105,17 +93,14 @@ class SalMaybeNull extends SalAnnotation {
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalMaybeNull */
|
||||
deprecated class SALMaybeNull = SalMaybeNull;
|
||||
|
||||
/**
|
||||
* A parameter annotated by one or more SAL annotations.
|
||||
*/
|
||||
class SalParameter extends Parameter {
|
||||
class SALParameter extends Parameter {
|
||||
/** One of this parameter's annotations. */
|
||||
SalAnnotation a;
|
||||
SALAnnotation a;
|
||||
|
||||
SalParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
|
||||
SALParameter() { annotatesAt(a, this.getADeclarationEntry(), _, _) }
|
||||
|
||||
predicate isIn() { a.getMacroName().toLowerCase().matches("%\\_in%") }
|
||||
|
||||
@@ -124,17 +109,14 @@ class SalParameter extends Parameter {
|
||||
predicate isInOut() { a.getMacroName().toLowerCase().matches("%\\_inout%") }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalParameter */
|
||||
deprecated class SALParameter = SalParameter;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation details
|
||||
/**
|
||||
* Holds if `a` annotates the declaration entry `d` and
|
||||
* its start position is the `idx`th position in `file` that holds a SAL element.
|
||||
*/
|
||||
private predicate annotatesAt(SalAnnotation a, DeclarationEntry d, File file, int idx) {
|
||||
annotatesAtPosition(a.(SalElement).getStartPosition(), d, file, idx)
|
||||
private predicate annotatesAt(SALAnnotation a, DeclarationEntry d, File file, int idx) {
|
||||
annotatesAtPosition(a.(SALElement).getStartPosition(), d, file, idx)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -145,12 +127,12 @@ private predicate annotatesAt(SalAnnotation a, DeclarationEntry d, File file, in
|
||||
// For performance reasons, do not mention the annotation itself here,
|
||||
// but compute with positions instead. This performs better on databases
|
||||
// with many annotations at the same position.
|
||||
private predicate annotatesAtPosition(SalPosition pos, DeclarationEntry d, File file, int idx) {
|
||||
private predicate annotatesAtPosition(SALPosition pos, DeclarationEntry d, File file, int idx) {
|
||||
pos = salRelevantPositionAt(file, idx) and
|
||||
salAnnotationPos(pos) and
|
||||
(
|
||||
// Base case: `pos` right before `d`
|
||||
d.(SalElement).getStartPosition() = salRelevantPositionAt(file, idx + 1)
|
||||
d.(SALElement).getStartPosition() = salRelevantPositionAt(file, idx + 1)
|
||||
or
|
||||
// Recursive case: `pos` right before some annotation on `d`
|
||||
annotatesAtPosition(_, d, file, idx + 1)
|
||||
@@ -161,10 +143,10 @@ private predicate annotatesAtPosition(SalPosition pos, DeclarationEntry d, File
|
||||
* A SAL element, that is, a SAL annotation or a declaration entry
|
||||
* that may have SAL annotations.
|
||||
*/
|
||||
library class SalElement extends Element {
|
||||
SalElement() {
|
||||
containsSalAnnotation(this.(DeclarationEntry).getFile()) or
|
||||
this instanceof SalAnnotation
|
||||
library class SALElement extends Element {
|
||||
SALElement() {
|
||||
containsSALAnnotation(this.(DeclarationEntry).getFile()) or
|
||||
this instanceof SALAnnotation
|
||||
}
|
||||
|
||||
predicate hasStartPosition(File file, int line, int col) {
|
||||
@@ -191,28 +173,25 @@ library class SalElement extends Element {
|
||||
)
|
||||
}
|
||||
|
||||
SalPosition getStartPosition() {
|
||||
SALPosition getStartPosition() {
|
||||
exists(File file, int line, int col |
|
||||
this.hasStartPosition(file, line, col) and
|
||||
result = MkSalPosition(file, line, col)
|
||||
result = MkSALPosition(file, line, col)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for SalElement */
|
||||
deprecated class SALElement = SalElement;
|
||||
|
||||
/** Holds if `file` contains a SAL annotation. */
|
||||
pragma[noinline]
|
||||
private predicate containsSalAnnotation(File file) { any(SalAnnotation a).getFile() = file }
|
||||
private predicate containsSALAnnotation(File file) { any(SALAnnotation a).getFile() = file }
|
||||
|
||||
/**
|
||||
* A source-file position of a `SALElement`. Unlike location, this denotes a
|
||||
* point in the file rather than a range.
|
||||
*/
|
||||
private newtype SalPosition =
|
||||
MkSalPosition(File file, int line, int col) {
|
||||
exists(SalElement e |
|
||||
private newtype SALPosition =
|
||||
MkSALPosition(File file, int line, int col) {
|
||||
exists(SALElement e |
|
||||
e.hasStartPosition(file, line, col)
|
||||
or
|
||||
e.hasEndPosition(file, line, col)
|
||||
@@ -221,18 +200,18 @@ private newtype SalPosition =
|
||||
|
||||
/** Holds if `pos` is the start position of a SAL annotation. */
|
||||
pragma[noinline]
|
||||
private predicate salAnnotationPos(SalPosition pos) {
|
||||
any(SalAnnotation a).(SalElement).getStartPosition() = pos
|
||||
private predicate salAnnotationPos(SALPosition pos) {
|
||||
any(SALAnnotation a).(SALElement).getStartPosition() = pos
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `idx`th position in `file` that holds a SAL element,
|
||||
* ordering positions lexicographically by their start line and start column.
|
||||
*/
|
||||
private SalPosition salRelevantPositionAt(File file, int idx) {
|
||||
private SALPosition salRelevantPositionAt(File file, int idx) {
|
||||
result =
|
||||
rank[idx](SalPosition pos, int line, int col |
|
||||
pos = MkSalPosition(file, line, col)
|
||||
rank[idx](SALPosition pos, int line, int col |
|
||||
pos = MkSALPosition(file, line, col)
|
||||
|
|
||||
pos order by line, col
|
||||
)
|
||||
|
||||
@@ -24,7 +24,7 @@ where
|
||||
if e = DefinitionInSnapshot()
|
||||
then defined = ""
|
||||
else
|
||||
if e = SuggestiveSalAnnotation()
|
||||
if e = SuggestiveSALAnnotation()
|
||||
then defined = "externally defined (SAL) "
|
||||
else defined = "externally defined (CSV) "
|
||||
)
|
||||
|
||||
@@ -149,7 +149,7 @@ newtype Evidence =
|
||||
* The function is externally defined, but the parameter has an `_out` SAL annotation which
|
||||
* suggests that it is initialized in the function.
|
||||
*/
|
||||
SuggestiveSalAnnotation() or
|
||||
SuggestiveSALAnnotation() or
|
||||
/**
|
||||
* We have been given a CSV file which indicates this parameter is conditionally initialized.
|
||||
*/
|
||||
@@ -198,8 +198,8 @@ class InitializationFunction extends Function {
|
||||
or
|
||||
// If we have no definition, we look at SAL annotations
|
||||
not this.hasDefinition() and
|
||||
this.getParameter(i).(SalParameter).isOut() and
|
||||
evidence = SuggestiveSalAnnotation()
|
||||
this.getParameter(i).(SALParameter).isOut() and
|
||||
evidence = SuggestiveSALAnnotation()
|
||||
or
|
||||
// We have some external information that this function conditionally initializes
|
||||
not this.hasDefinition() and
|
||||
|
||||
@@ -19,8 +19,8 @@ import DataFlow::PathGraph
|
||||
/**
|
||||
* A configuration for tracking XML objects and their states.
|
||||
*/
|
||||
class XxeConfiguration extends DataFlow::Configuration {
|
||||
XxeConfiguration() { this = "XXEConfiguration" }
|
||||
class XXEConfiguration extends DataFlow::Configuration {
|
||||
XXEConfiguration() { this = "XXEConfiguration" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node, string flowstate) {
|
||||
any(XmlLibrary l).configurationSource(node, flowstate)
|
||||
@@ -45,7 +45,7 @@ class XxeConfiguration extends DataFlow::Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
from XxeConfiguration conf, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
from XXEConfiguration conf, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"This $@ is not configured to prevent an XML external entity (XXE) attack.", source, "XML parser"
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The alert message of many queries have been changed to make the message consistent with other languages.
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 0.3.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Cleartext storage of sensitive information in buffer" (`cpp/cleartext-storage-buffer`) query has been improved to produce fewer false positives.
|
||||
1
cpp/ql/src/change-notes/released/0.3.4.md
Normal file
1
cpp/ql/src/change-notes/released/0.3.4.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.3.4
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.3.2
|
||||
lastReleaseVersion: 0.3.4
|
||||
|
||||
@@ -16,17 +16,17 @@ import cpp
|
||||
// pointers. This will obviously not catch code that uses inline assembly to achieve
|
||||
// self-modification, nor will it spot the use of OS mechanisms to write into process
|
||||
// memory (such as WriteProcessMemory under Windows).
|
||||
predicate maybeSmcConversion(Type t1, Type t2) {
|
||||
predicate maybeSMCConversion(Type t1, Type t2) {
|
||||
t1 instanceof FunctionPointerType and
|
||||
t2 instanceof PointerType and
|
||||
not t2 instanceof FunctionPointerType and
|
||||
not t2 instanceof VoidPointerType
|
||||
or
|
||||
maybeSmcConversion(t2, t1)
|
||||
maybeSMCConversion(t2, t1)
|
||||
}
|
||||
|
||||
from Expr e
|
||||
where
|
||||
e.fromSource() and
|
||||
maybeSmcConversion(e.getUnderlyingType(), e.getActualType())
|
||||
maybeSMCConversion(e.getUnderlyingType(), e.getActualType())
|
||||
select e, "AV Rule 2: There shall not be any self-modifying code."
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.3.3-dev
|
||||
version: 0.3.4
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import cpp
|
||||
|
||||
from Class c, boolean ispod
|
||||
where if c.isPod() then ispod = true else ispod = false
|
||||
where if c.isPOD() then ispod = true else ispod = false
|
||||
select c, ispod
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import semmle.code.cpp.PODType03
|
||||
|
||||
from Class c, boolean ispod
|
||||
where if isPodClass03(c) then ispod = true else ispod = false
|
||||
where if isPODClass03(c) then ispod = true else ispod = false
|
||||
select c, ispod
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Microsoft.SAL
|
||||
|
||||
from SalAnnotation a
|
||||
from SALAnnotation a
|
||||
select a, a.getDeclaration()
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
| empty_block.cpp:9:10:9:11 | { ... } | Empty block without comment. |
|
||||
| empty_block.cpp:12:10:13:3 | { ... } | Empty block without comment. |
|
||||
| empty_block.cpp:20:10:21:3 | { ... } | Empty block without comment. |
|
||||
| empty_block.cpp:9:10:9:11 | { ... } | Empty block without comment |
|
||||
| empty_block.cpp:12:10:13:3 | { ... } | Empty block without comment |
|
||||
| empty_block.cpp:20:10:21:3 | { ... } | Empty block without comment |
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
| test2.cpp:37:1:37:39 | // int myFunction() { return myValue; } | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:39:1:39:45 | // int myFunction() const { return myValue; } | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:41:1:41:54 | // int myFunction() const noexcept { return myValue; } | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:43:1:43:18 | // #define MYMACRO | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:45:1:45:23 | // #include "include.h" | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:47:1:51:2 | /*\n#ifdef\nvoid myFunction();\n#endif\n*/ | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:59:1:59:24 | // #if(defined(MYMACRO)) | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:63:1:63:15 | // #pragma once | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:65:1:65:17 | // # pragma once | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:67:1:67:19 | /*#error"myerror"*/ | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:91:1:95:2 | /*\n#ifdef MYMACRO\n\t// ...\n#endif // #ifdef MYMACRO\n*/ | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:107:21:107:43 | // #include "config2.h" | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:115:16:115:35 | /* #ifdef MYMACRO */ | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:117:1:117:24 | // commented_out_code(); | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:120:2:120:25 | // commented_out_code(); | This comment appears to contain commented-out code. |
|
||||
| test.c:2:1:2:22 | // commented out code; | This comment appears to contain commented-out code. |
|
||||
| test.c:4:1:7:8 | // some; | This comment appears to contain commented-out code. |
|
||||
| test.c:9:1:13:8 | // also; | This comment appears to contain commented-out code. |
|
||||
| test.c:21:1:26:2 | /*\n some;\n commented;\n out;\n code;\n*/ | This comment appears to contain commented-out code. |
|
||||
| test.c:28:1:34:2 | /*\n also;\n this\n is;\n commented-out\n code;\n*/ | This comment appears to contain commented-out code. |
|
||||
| test2.cpp:37:1:37:39 | // int myFunction() { return myValue; } | This comment appears to contain commented-out code |
|
||||
| test2.cpp:39:1:39:45 | // int myFunction() const { return myValue; } | This comment appears to contain commented-out code |
|
||||
| test2.cpp:41:1:41:54 | // int myFunction() const noexcept { return myValue; } | This comment appears to contain commented-out code |
|
||||
| test2.cpp:43:1:43:18 | // #define MYMACRO | This comment appears to contain commented-out code |
|
||||
| test2.cpp:45:1:45:23 | // #include "include.h" | This comment appears to contain commented-out code |
|
||||
| test2.cpp:47:1:51:2 | /*\n#ifdef\nvoid myFunction();\n#endif\n*/ | This comment appears to contain commented-out code |
|
||||
| test2.cpp:59:1:59:24 | // #if(defined(MYMACRO)) | This comment appears to contain commented-out code |
|
||||
| test2.cpp:63:1:63:15 | // #pragma once | This comment appears to contain commented-out code |
|
||||
| test2.cpp:65:1:65:17 | // # pragma once | This comment appears to contain commented-out code |
|
||||
| test2.cpp:67:1:67:19 | /*#error"myerror"*/ | This comment appears to contain commented-out code |
|
||||
| test2.cpp:91:1:95:2 | /*\n#ifdef MYMACRO\n\t// ...\n#endif // #ifdef MYMACRO\n*/ | This comment appears to contain commented-out code |
|
||||
| test2.cpp:107:21:107:43 | // #include "config2.h" | This comment appears to contain commented-out code |
|
||||
| test2.cpp:115:16:115:35 | /* #ifdef MYMACRO */ | This comment appears to contain commented-out code |
|
||||
| test2.cpp:117:1:117:24 | // commented_out_code(); | This comment appears to contain commented-out code |
|
||||
| test2.cpp:120:2:120:25 | // commented_out_code(); | This comment appears to contain commented-out code |
|
||||
| test.c:2:1:2:22 | // commented out code; | This comment appears to contain commented-out code |
|
||||
| test.c:4:1:7:8 | // some; | This comment appears to contain commented-out code |
|
||||
| test.c:9:1:13:8 | // also; | This comment appears to contain commented-out code |
|
||||
| test.c:21:1:26:2 | /*\n some;\n commented;\n out;\n code;\n*/ | This comment appears to contain commented-out code |
|
||||
| test.c:28:1:34:2 | /*\n also;\n this\n is;\n commented-out\n code;\n*/ | This comment appears to contain commented-out code |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
| bsc.cpp:2:10:2:32 | ... > ... | Potentially unsafe sign check of a bitwise operation. |
|
||||
| bsc.cpp:6:10:6:32 | ... > ... | Potentially unsafe sign check of a bitwise operation. |
|
||||
| bsc.cpp:18:10:18:28 | ... > ... | Potentially unsafe sign check of a bitwise operation. |
|
||||
| bsc.cpp:22:10:22:28 | ... < ... | Potentially unsafe sign check of a bitwise operation. |
|
||||
| bsc.cpp:2:10:2:32 | ... > ... | Potential unsafe sign check of a bitwise operation. |
|
||||
| bsc.cpp:6:10:6:32 | ... > ... | Potential unsafe sign check of a bitwise operation. |
|
||||
| bsc.cpp:18:10:18:28 | ... > ... | Potential unsafe sign check of a bitwise operation. |
|
||||
| bsc.cpp:22:10:22:28 | ... < ... | Potential unsafe sign check of a bitwise operation. |
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
| c.c:10:5:10:10 | ... == ... | Equality checks on floating point values can yield unexpected results. |
|
||||
| c.c:14:5:14:14 | ... == ... | Equality checks on floating point values can yield unexpected results. |
|
||||
| c.c:16:5:16:12 | ... == ... | Equality checks on floating point values can yield unexpected results. |
|
||||
| c.c:17:5:17:12 | ... == ... | Equality checks on floating point values can yield unexpected results. |
|
||||
| c.c:10:5:10:10 | ... == ... | Equality test on floating point values may not behave as expected. |
|
||||
| c.c:14:5:14:14 | ... == ... | Equality test on floating point values may not behave as expected. |
|
||||
| c.c:16:5:16:12 | ... == ... | Equality test on floating point values may not behave as expected. |
|
||||
| c.c:17:5:17:12 | ... == ... | Equality test on floating point values may not behave as expected. |
|
||||
|
||||
@@ -64,6 +64,10 @@ edges
|
||||
| test.cpp:201:5:201:17 | EnterFunction: maybe_deref_p | test.cpp:201:5:201:17 | VariableAddress: maybe_deref_p |
|
||||
| test.cpp:210:3:210:9 | Call: call to escape1 | test.cpp:201:5:201:17 | EnterFunction: maybe_deref_p |
|
||||
| test.cpp:210:3:210:9 | Call: call to escape1 | test.cpp:201:5:201:17 | VariableAddress: maybe_deref_p |
|
||||
| test.cpp:234:3:234:13 | Store: ... = ... | test.cpp:238:3:238:9 | Call: call to escape2 |
|
||||
| test.cpp:238:3:238:9 | Call: call to escape2 | test.cpp:239:17:239:17 | Load: p |
|
||||
| test.cpp:263:3:263:13 | Store: ... = ... | test.cpp:267:3:267:9 | Call: call to escape3 |
|
||||
| test.cpp:267:3:267:9 | Call: call to escape3 | test.cpp:268:17:268:17 | Load: p |
|
||||
#select
|
||||
| test.cpp:15:16:15:16 | Load: p | test.cpp:10:3:10:13 | Store: ... = ... | test.cpp:15:16:15:16 | Load: p | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:9:7:9:7 | x | x | test.cpp:10:3:10:13 | Store: ... = ... | here |
|
||||
| test.cpp:24:16:24:16 | Load: p | test.cpp:10:3:10:13 | Store: ... = ... | test.cpp:24:16:24:16 | Load: p | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:9:7:9:7 | x | x | test.cpp:10:3:10:13 | Store: ... = ... | here |
|
||||
@@ -90,3 +94,5 @@ edges
|
||||
| test.cpp:180:14:180:19 | Load: * ... | test.cpp:154:3:154:22 | Store: ... = ... | test.cpp:180:14:180:19 | Load: * ... | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:133:7:133:8 | b2 | b2 | test.cpp:154:3:154:22 | Store: ... = ... | here |
|
||||
| test.cpp:181:13:181:20 | Load: access to array | test.cpp:155:3:155:21 | Store: ... = ... | test.cpp:181:13:181:20 | Load: access to array | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:134:7:134:8 | b3 | b3 | test.cpp:155:3:155:21 | Store: ... = ... | here |
|
||||
| test.cpp:182:14:182:19 | Load: * ... | test.cpp:156:3:156:25 | Store: ... = ... | test.cpp:182:14:182:19 | Load: * ... | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:134:7:134:8 | b3 | b3 | test.cpp:156:3:156:25 | Store: ... = ... | here |
|
||||
| test.cpp:239:17:239:17 | Load: p | test.cpp:234:3:234:13 | Store: ... = ... | test.cpp:239:17:239:17 | Load: p | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:232:7:232:7 | x | x | test.cpp:234:3:234:13 | Store: ... = ... | here |
|
||||
| test.cpp:268:17:268:17 | Load: p | test.cpp:263:3:263:13 | Store: ... = ... | test.cpp:268:17:268:17 | Load: p | Stack variable $@ escapes $@ and is used after it has expired. | test.cpp:260:7:260:7 | x | x | test.cpp:263:3:263:13 | Store: ... = ... | here |
|
||||
|
||||
@@ -209,4 +209,61 @@ int maybe_deref_p(bool b) {
|
||||
int field_indirect_maybe_bad(bool b) {
|
||||
escape1();
|
||||
return maybe_deref_p(b);
|
||||
}
|
||||
|
||||
// These next tests cover subsequent stores to the same address in the same basic block.
|
||||
|
||||
static struct S100 s102;
|
||||
|
||||
void not_escape1() {
|
||||
int x;
|
||||
s102.p = &x;
|
||||
s102.p = nullptr;
|
||||
}
|
||||
|
||||
void calls_not_escape1() {
|
||||
not_escape1();
|
||||
int x = *s102.p; // GOOD
|
||||
}
|
||||
|
||||
static struct S100 s103;
|
||||
|
||||
void escape2() {
|
||||
int x;
|
||||
s103.p = nullptr;
|
||||
s103.p = &x;
|
||||
}
|
||||
|
||||
void calls_escape2() {
|
||||
escape2();
|
||||
int x = *s103.p; // BAD
|
||||
}
|
||||
|
||||
bool unknown();
|
||||
static struct S100 s104;
|
||||
|
||||
void not_escape2() {
|
||||
int x;
|
||||
s104.p = &x;
|
||||
if(unknown()) { }
|
||||
s104.p = nullptr;
|
||||
}
|
||||
|
||||
void calls_not_escape2() {
|
||||
not_escape2();
|
||||
int x = *s104.p; // GOOD
|
||||
}
|
||||
|
||||
static struct S100 s105;
|
||||
|
||||
void escape3() {
|
||||
int x;
|
||||
s105.p = nullptr;
|
||||
if(unknown()) { }
|
||||
s105.p = &x;
|
||||
}
|
||||
|
||||
void calls_escape3() {
|
||||
escape3();
|
||||
int x = *s105.p; // BAD
|
||||
}
|
||||
@@ -35,10 +35,3 @@ options:
|
||||
the code (for example if it uses inaccessible dependencies).
|
||||
type: string
|
||||
pattern: "^(false|true)$"
|
||||
cil:
|
||||
title: Whether to enable CIL extraction.
|
||||
description: >
|
||||
A value indicating, whether CIL extraction should be enabled.
|
||||
The default is 'true'.
|
||||
type: string
|
||||
pattern: "^(false|true)$"
|
||||
|
||||
@@ -9,7 +9,7 @@ Microsoft.Extensions.Caching.Memory,,,46,,,,,,,,,,,,45,1
|
||||
Microsoft.Extensions.Configuration,,,83,,,,,,,,,,,,80,3
|
||||
Microsoft.Extensions.DependencyInjection,,,62,,,,,,,,,,,,62,
|
||||
Microsoft.Extensions.DependencyModel,,,12,,,,,,,,,,,,12,
|
||||
Microsoft.Extensions.FileProviders,,,16,,,,,,,,,,,,16,
|
||||
Microsoft.Extensions.FileProviders,,,15,,,,,,,,,,,,15,
|
||||
Microsoft.Extensions.FileSystemGlobbing,,,15,,,,,,,,,,,,13,2
|
||||
Microsoft.Extensions.Hosting,,,17,,,,,,,,,,,,16,1
|
||||
Microsoft.Extensions.Http,,,10,,,,,,,,,,,,10,
|
||||
@@ -24,5 +24,5 @@ Microsoft.Win32,,,8,,,,,,,,,,,,8,
|
||||
MySql.Data.MySqlClient,48,,,,,,,,,,48,,,,,
|
||||
Newtonsoft.Json,,,91,,,,,,,,,,,,73,18
|
||||
ServiceStack,194,,7,27,,,,,,75,92,,,,7,
|
||||
System,65,4,12081,,8,8,9,,4,,33,3,1,3,10139,1942
|
||||
System,43,4,11809,,1,1,1,,4,,33,3,1,3,9867,1942
|
||||
Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,,,
|
||||
|
||||
|
@@ -8,7 +8,7 @@ C# framework & library support
|
||||
|
||||
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
|
||||
`ServiceStack <https://servicestack.net/>`_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
|
||||
System,"``System.*``, ``System``",4,12081,65,7
|
||||
Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``, ``Windows.Security.Cryptography.Core``",,555,138,
|
||||
Totals,,4,12643,397,7
|
||||
System,"``System.*``, ``System``",4,11809,43,7
|
||||
Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``, ``Windows.Security.Cryptography.Core``",,554,138,
|
||||
Totals,,4,12370,375,7
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Introduce '--cil' flag in the comments. This does not make any changes to the dbscheme.
|
||||
compatibility: full
|
||||
@@ -18,11 +18,14 @@ namespace Semmle.Extraction.CSharp
|
||||
if (args.Length > 0 && args[0] == "--dotnetexec")
|
||||
{
|
||||
var compilerRegEx = new Regex(@"csc\.exe|mcs\.exe|csc\.dll", RegexOptions.Compiled);
|
||||
for (var i = 1; i < args.Length; i++)
|
||||
var cil = args.Length > 1 && args[1] == "--cil";
|
||||
for (var i = cil ? 2 : 1; i < args.Length; i++)
|
||||
{
|
||||
if (compilerRegEx.IsMatch(args[i]))
|
||||
{
|
||||
var argsList = new List<string>();
|
||||
if (cil)
|
||||
argsList.Add("--cil");
|
||||
argsList.Add("--compiler");
|
||||
argsList.Add(args[i]);
|
||||
if (i + 1 < args.Length)
|
||||
|
||||
@@ -56,6 +56,7 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
CSharp.Extractor.SetInvariantCulture();
|
||||
|
||||
var options = Options.Create(args);
|
||||
// options.CIL = true; // To do: Enable this
|
||||
|
||||
if (options.Help)
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Semmle.Extraction.Tests
|
||||
{
|
||||
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
|
||||
Assert.True(options.Cache);
|
||||
Assert.True(options.CIL);
|
||||
Assert.False(options.CIL);
|
||||
Assert.Null(options.Framework);
|
||||
Assert.Null(options.CompilerName);
|
||||
Assert.Empty(options.CompilerArguments);
|
||||
@@ -51,20 +51,10 @@ namespace Semmle.Extraction.Tests
|
||||
[Fact]
|
||||
public void CIL()
|
||||
{
|
||||
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
|
||||
options = CSharp.Options.CreateWithEnvironment(new string[] { "--cil" });
|
||||
Assert.True(options.CIL);
|
||||
|
||||
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_CIL", "false");
|
||||
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
|
||||
options = CSharp.Options.CreateWithEnvironment(new string[] { "--cil", "--nocil" });
|
||||
Assert.False(options.CIL);
|
||||
|
||||
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_CIL", "true");
|
||||
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
|
||||
Assert.True(options.CIL);
|
||||
|
||||
Environment.SetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_OPTION_CIL", null);
|
||||
options = CSharp.Options.CreateWithEnvironment(Array.Empty<string>());
|
||||
Assert.True(options.CIL);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -131,6 +121,22 @@ namespace Semmle.Extraction.Tests
|
||||
Assert.Equal("foo", options.Framework);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnvironmentVariables()
|
||||
{
|
||||
Environment.SetEnvironmentVariable("LGTM_INDEX_EXTRACTOR", "--cil c");
|
||||
options = CSharp.Options.CreateWithEnvironment(new string[] { "a", "b" });
|
||||
Assert.True(options.CIL);
|
||||
Assert.Equal("a", options.CompilerArguments[0]);
|
||||
Assert.Equal("b", options.CompilerArguments[1]);
|
||||
Assert.Equal("c", options.CompilerArguments[2]);
|
||||
|
||||
Environment.SetEnvironmentVariable("LGTM_INDEX_EXTRACTOR", "");
|
||||
Environment.SetEnvironmentVariable("LGTM_INDEX_EXTRACTOR", "--nocil");
|
||||
options = CSharp.Options.CreateWithEnvironment(new string[] { "--cil" });
|
||||
Assert.False(options.CIL);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StandaloneDefaults()
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Semmle.Extraction
|
||||
/// <summary>
|
||||
/// Holds if CIL should be extracted.
|
||||
/// </summary>
|
||||
public bool CIL { get; private set; } = true;
|
||||
public bool CIL { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Holds if assemblies shouldn't be extracted twice.
|
||||
@@ -50,6 +50,7 @@ namespace Semmle.Extraction
|
||||
/// </summary>
|
||||
public bool QlTest { get; private set; } = false;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The compression algorithm used for trap files.
|
||||
/// </summary>
|
||||
@@ -72,9 +73,6 @@ namespace Semmle.Extraction
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case "cil":
|
||||
CIL = Boolean.Parse(value);
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -99,6 +97,9 @@ namespace Semmle.Extraction
|
||||
case "cache":
|
||||
Cache = value;
|
||||
return true;
|
||||
case "cil":
|
||||
CIL = value;
|
||||
return true;
|
||||
case "pdb":
|
||||
PDB = value;
|
||||
CIL = true;
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Semmle.Util
|
||||
bool HandleOption(string key, string value);
|
||||
|
||||
/// <summary>
|
||||
/// Handle a flag of the form "--cache" or "--nocache"
|
||||
/// Handle a flag of the form "--cil" or "--nocil"
|
||||
/// </summary>
|
||||
/// <param name="key">The name of the flag. This is case sensitive.</param>
|
||||
/// <param name="value">True if set, or false if prefixed by "--no"</param>
|
||||
@@ -40,7 +40,6 @@ namespace Semmle.Util
|
||||
|
||||
public static class OptionsExtensions
|
||||
{
|
||||
private static readonly string[] ExtractorOptions = new[] { "trap_compression", "cil" };
|
||||
private static string? GetExtractorOption(string name) =>
|
||||
Environment.GetEnvironmentVariable($"CODEQL_EXTRACTOR_CSHARP_OPTION_{name.ToUpper()}");
|
||||
|
||||
@@ -48,14 +47,12 @@ namespace Semmle.Util
|
||||
{
|
||||
var extractorOptions = new List<string>();
|
||||
|
||||
foreach (var option in ExtractorOptions)
|
||||
var trapCompression = GetExtractorOption("trap_compression");
|
||||
if (!string.IsNullOrEmpty(trapCompression))
|
||||
{
|
||||
var value = GetExtractorOption(option);
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
extractorOptions.Add($"--{option}:{value}");
|
||||
}
|
||||
extractorOptions.Add($"--trap_compression:{trapCompression}");
|
||||
}
|
||||
|
||||
return extractorOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.2.5
|
||||
|
||||
## 1.2.4
|
||||
|
||||
## 1.2.3
|
||||
|
||||
## 1.2.2
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
## 1.2.4
|
||||
@@ -0,0 +1 @@
|
||||
## 1.2.5
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.2.3
|
||||
lastReleaseVersion: 1.2.5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.2.4-dev
|
||||
version: 1.2.5
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.2.5
|
||||
|
||||
## 1.2.4
|
||||
|
||||
## 1.2.3
|
||||
|
||||
## 1.2.2
|
||||
|
||||
@@ -15,7 +15,7 @@ import experimental.code.csharp.Cryptography.NonCryptographicHashes
|
||||
|
||||
from Variable v, Literal l, LoopStmt loop, Expr additional_xor
|
||||
where
|
||||
maybeUsedInFnvFunction(v, _, _, loop) and
|
||||
maybeUsedInFNVFunction(v, _, _, loop) and
|
||||
(
|
||||
exists(BitwiseXorExpr xor2 | xor2.getAnOperand() = l and additional_xor = xor2 |
|
||||
loop.getAControlFlowExitNode().getASuccessor*() = xor2.getAControlFlowNode() and
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
## 1.2.4
|
||||
@@ -0,0 +1 @@
|
||||
## 1.2.5
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.2.3
|
||||
lastReleaseVersion: 1.2.5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.2.4-dev
|
||||
version: 1.2.5
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.dataflow.internal.SsaImpl::Consistency as Consistency
|
||||
import semmle.code.csharp.dataflow.internal.SsaImplCommon::Consistency
|
||||
import Ssa
|
||||
|
||||
class MyRelevantDefinition extends Consistency::RelevantDefinition, Ssa::Definition {
|
||||
class MyRelevantDefinition extends RelevantDefinition, Ssa::Definition {
|
||||
override predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
@@ -10,14 +10,6 @@ class MyRelevantDefinition extends Consistency::RelevantDefinition, Ssa::Definit
|
||||
}
|
||||
}
|
||||
|
||||
query predicate nonUniqueDef = Consistency::nonUniqueDef/4;
|
||||
|
||||
query predicate readWithoutDef = Consistency::readWithoutDef/3;
|
||||
|
||||
query predicate deadDef = Consistency::deadDef/2;
|
||||
|
||||
query predicate notDominatedByDef = Consistency::notDominatedByDef/4;
|
||||
|
||||
query predicate localDeclWithSsaDef(LocalVariableDeclExpr d) {
|
||||
// Local variables in C# must be initialized before every use, so uninitialized
|
||||
// local variables should not have an SSA definition, as that would imply that
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
## 0.3.5
|
||||
|
||||
## 0.3.4
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
|
||||
|
||||
## 0.3.3
|
||||
|
||||
## 0.3.2
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* All deprecated predicates/classes/modules that have been deprecated for over a year have been
|
||||
deleted.
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
10
csharp/ql/lib/change-notes/released/0.3.4.md
Normal file
10
csharp/ql/lib/change-notes/released/0.3.4.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## 0.3.4
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* All deprecated predicates/classes/modules that have been deprecated for over a year have been deleted.
|
||||
1
csharp/ql/lib/change-notes/released/0.3.5.md
Normal file
1
csharp/ql/lib/change-notes/released/0.3.5.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.3.5
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.3.3
|
||||
lastReleaseVersion: 0.3.5
|
||||
|
||||
@@ -13,7 +13,7 @@ private import semmle.code.csharp.dataflow.TaintTracking2
|
||||
predicate maybeANonCryptogrphicHash(Callable callable, Variable v, Expr xor, Expr mul, LoopStmt loop) {
|
||||
callable = loop.getEnclosingCallable() and
|
||||
(
|
||||
maybeUsedInFnvFunction(v, xor, mul, loop) or
|
||||
maybeUsedInFNVFunction(v, xor, mul, loop) or
|
||||
maybeUsedInElfHashFunction(v, xor, mul, loop)
|
||||
)
|
||||
}
|
||||
@@ -23,7 +23,7 @@ predicate maybeANonCryptogrphicHash(Callable callable, Variable v, Expr xor, Exp
|
||||
* where there is a loop statement `loop` where the variable `v` is used in an xor `xor` expression
|
||||
* followed by a multiplication `mul` expression.
|
||||
*/
|
||||
predicate maybeUsedInFnvFunction(Variable v, Operation xor, Operation mul, LoopStmt loop) {
|
||||
predicate maybeUsedInFNVFunction(Variable v, Operation xor, Operation mul, LoopStmt loop) {
|
||||
exists(Expr e1, Expr e2 |
|
||||
e1.getAChild*() = v.getAnAccess() and
|
||||
e2.getAChild*() = v.getAnAccess() and
|
||||
@@ -37,9 +37,6 @@ predicate maybeUsedInFnvFunction(Variable v, Operation xor, Operation mul, LoopS
|
||||
loop.getAChild*() = xor.getEnclosingStmt()
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for maybeUsedInFnvFunction */
|
||||
deprecated predicate maybeUsedInFNVFunction = maybeUsedInFnvFunction/4;
|
||||
|
||||
/**
|
||||
* Holds if the arguments are used in a way that resembles an Elf-Hash hash function
|
||||
* where there is a loop statement `loop` where the variable `v` is used in an xor `xor` expression
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 0.3.4-dev
|
||||
version: 0.3.5
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -8,12 +8,13 @@ private import CIL
|
||||
* Provides classes for working with static single assignment (SSA) form.
|
||||
*/
|
||||
module Ssa {
|
||||
private import internal.SsaImpl as SsaImpl
|
||||
private import internal.SsaImplCommon as SsaImpl
|
||||
private import internal.SsaImpl
|
||||
|
||||
/** An SSA definition. */
|
||||
class Definition extends SsaImpl::Definition {
|
||||
/** Gets a read of this SSA definition. */
|
||||
final ReadAccess getARead() { result = SsaImpl::getARead(this) }
|
||||
final ReadAccess getARead() { result = getARead(this) }
|
||||
|
||||
/** Gets the underlying variable update, if any. */
|
||||
final VariableUpdate getVariableUpdate() {
|
||||
@@ -24,11 +25,11 @@ module Ssa {
|
||||
}
|
||||
|
||||
/** Gets a first read of this SSA definition. */
|
||||
final ReadAccess getAFirstRead() { result = SsaImpl::getAFirstRead(this) }
|
||||
final ReadAccess getAFirstRead() { result = getAFirstRead(this) }
|
||||
|
||||
/** Holds if `first` and `second` are adjacent reads of this SSA definition. */
|
||||
final predicate hasAdjacentReads(ReadAccess first, ReadAccess second) {
|
||||
SsaImpl::hasAdjacentReads(this, first, second)
|
||||
hasAdjacentReads(this, first, second)
|
||||
}
|
||||
|
||||
private Definition getAPhiInput() { result = this.(PhiNode).getAnInput() }
|
||||
@@ -51,7 +52,7 @@ module Ssa {
|
||||
final override Location getLocation() { result = this.getBasicBlock().getLocation() }
|
||||
|
||||
/** Gets an input to this phi node. */
|
||||
final Definition getAnInput() { result = SsaImpl::getAPhiInput(this) }
|
||||
final Definition getAnInput() { result = getAPhiInput(this) }
|
||||
|
||||
/**
|
||||
* Holds if if `def` is an input to this phi node, and a reference to `def` at
|
||||
@@ -59,7 +60,7 @@ module Ssa {
|
||||
* other references.
|
||||
*/
|
||||
final predicate hasLastInputRef(Definition def, BasicBlock bb, int i) {
|
||||
SsaImpl::hasLastInputRef(this, def, bb, i)
|
||||
hasLastInputRef(this, def, bb, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,8 @@
|
||||
private import cil
|
||||
private import semmle.code.csharp.dataflow.internal.SsaImplCommon as SsaImplCommon
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
class BasicBlock = CIL::BasicBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock = CIL::ExitBasicBlock;
|
||||
|
||||
class SourceVariable = CIL::StackVariable;
|
||||
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
forceCachingInSameStage() and
|
||||
exists(CIL::VariableUpdate vu |
|
||||
vu.updatesAt(bb, i) and
|
||||
v = vu.getVariable() and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(CIL::ReadAccess ra | bb.getNode(i) = ra |
|
||||
ra.getTarget() = v and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import SsaImplCommon::Make<SsaInput>
|
||||
private import semmle.code.cil.CIL
|
||||
private import SsaImplCommon
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
private import CIL
|
||||
|
||||
cached
|
||||
predicate forceCachingInSameStage() { any() }
|
||||
|
||||
|
||||
795
csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll
Normal file
795
csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll
Normal file
@@ -0,0 +1,795 @@
|
||||
/**
|
||||
* Provides a language-independent implementation of static single assignment
|
||||
* (SSA) form.
|
||||
*/
|
||||
|
||||
private import SsaImplSpecific
|
||||
|
||||
private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { getABasicBlockSuccessor(result) = bb }
|
||||
|
||||
/**
|
||||
* Liveness analysis (based on source variables) to restrict the size of the
|
||||
* SSA representation.
|
||||
*/
|
||||
private module Liveness {
|
||||
/**
|
||||
* A classification of variable references into reads (of a given kind) and
|
||||
* (certain or uncertain) writes.
|
||||
*/
|
||||
private newtype TRefKind =
|
||||
Read(boolean certain) { certain in [false, true] } or
|
||||
Write(boolean certain) { certain in [false, true] }
|
||||
|
||||
private class RefKind extends TRefKind {
|
||||
string toString() {
|
||||
exists(boolean certain | this = Read(certain) and result = "read (" + certain + ")")
|
||||
or
|
||||
exists(boolean certain | this = Write(certain) and result = "write (" + certain + ")")
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
this = Read(_) and
|
||||
result = 0
|
||||
or
|
||||
this = Write(_) and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`.
|
||||
*/
|
||||
predicate ref(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
exists(boolean certain | variableRead(bb, i, v, certain) | k = Read(certain))
|
||||
or
|
||||
exists(boolean certain | variableWrite(bb, i, v, certain) | k = Write(certain))
|
||||
}
|
||||
|
||||
private newtype OrderedRefIndex =
|
||||
MkOrderedRefIndex(int i, int tag) {
|
||||
exists(RefKind rk | ref(_, i, _, rk) | tag = rk.getOrder())
|
||||
}
|
||||
|
||||
private OrderedRefIndex refOrd(BasicBlock bb, int i, SourceVariable v, RefKind k, int ord) {
|
||||
ref(bb, i, v, k) and
|
||||
result = MkOrderedRefIndex(i, ord) and
|
||||
ord = k.getOrder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the reference to `v` at the `i`th node of
|
||||
* basic block `bb`, which has the given reference kind `k`.
|
||||
*
|
||||
* Reads are considered before writes when they happen at the same index.
|
||||
*/
|
||||
private int refRank(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
refOrd(bb, i, v, k, _) =
|
||||
rank[result](int j, int ord, OrderedRefIndex res |
|
||||
res = refOrd(bb, j, v, _, ord)
|
||||
|
|
||||
res order by j, ord
|
||||
)
|
||||
}
|
||||
|
||||
private int maxRefRank(BasicBlock bb, SourceVariable v) {
|
||||
result = refRank(bb, _, v, _) and
|
||||
not result + 1 = refRank(bb, _, v, _)
|
||||
}
|
||||
|
||||
predicate lastRefIsRead(BasicBlock bb, SourceVariable v) {
|
||||
maxRefRank(bb, v) = refRank(bb, _, v, Read(_))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the first reference to `v` inside basic block `bb`
|
||||
* that is either a read or a certain write.
|
||||
*/
|
||||
private int firstReadOrCertainWrite(BasicBlock bb, SourceVariable v) {
|
||||
result =
|
||||
min(int r, RefKind k |
|
||||
r = refRank(bb, _, v, k) and
|
||||
k != Write(false)
|
||||
|
|
||||
r
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if source variable `v` is live at the beginning of basic block `bb`.
|
||||
*/
|
||||
predicate liveAtEntry(BasicBlock bb, SourceVariable v) {
|
||||
// The first read or certain write to `v` inside `bb` is a read
|
||||
refRank(bb, _, v, Read(_)) = firstReadOrCertainWrite(bb, v)
|
||||
or
|
||||
// There is no certain write to `v` inside `bb`, but `v` is live at entry
|
||||
// to a successor basic block of `bb`
|
||||
not exists(firstReadOrCertainWrite(bb, v)) and
|
||||
liveAtExit(bb, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if source variable `v` is live at the end of basic block `bb`.
|
||||
*/
|
||||
predicate liveAtExit(BasicBlock bb, SourceVariable v) {
|
||||
liveAtEntry(getABasicBlockSuccessor(bb), v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live in basic block `bb` at index `i`.
|
||||
* The rank of `i` is `rnk` as defined by `refRank()`.
|
||||
*/
|
||||
private predicate liveAtRank(BasicBlock bb, int i, SourceVariable v, int rnk) {
|
||||
exists(RefKind kind | rnk = refRank(bb, i, v, kind) |
|
||||
rnk = maxRefRank(bb, v) and
|
||||
liveAtExit(bb, v)
|
||||
or
|
||||
ref(bb, i, v, kind) and
|
||||
kind = Read(_)
|
||||
or
|
||||
exists(RefKind nextKind |
|
||||
liveAtRank(bb, _, v, rnk + 1) and
|
||||
rnk + 1 = refRank(bb, _, v, nextKind) and
|
||||
nextKind != Write(true)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live after the (certain or uncertain) write at
|
||||
* index `i` inside basic block `bb`.
|
||||
*/
|
||||
predicate liveAfterWrite(BasicBlock bb, int i, SourceVariable v) {
|
||||
exists(int rnk | rnk = refRank(bb, i, v, Write(_)) | liveAtRank(bb, i, v, rnk))
|
||||
}
|
||||
}
|
||||
|
||||
private import Liveness
|
||||
|
||||
/**
|
||||
* Holds if `df` is in the dominance frontier of `bb`.
|
||||
*
|
||||
* This is equivalent to:
|
||||
*
|
||||
* ```ql
|
||||
* bb = getImmediateBasicBlockDominator*(getABasicBlockPredecessor(df)) and
|
||||
* not bb = getImmediateBasicBlockDominator+(df)
|
||||
* ```
|
||||
*/
|
||||
private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) {
|
||||
bb = getABasicBlockPredecessor(df) and not bb = getImmediateBasicBlockDominator(df)
|
||||
or
|
||||
exists(BasicBlock prev | inDominanceFrontier(prev, df) |
|
||||
bb = getImmediateBasicBlockDominator(prev) and
|
||||
not bb = getImmediateBasicBlockDominator(df)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb` is in the dominance frontier of a block containing a
|
||||
* definition of `v`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) {
|
||||
exists(BasicBlock defbb, Definition def |
|
||||
def.definesAt(v, defbb, _) and
|
||||
inDominanceFrontier(defbb, bb)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TDefinition =
|
||||
TWriteDef(SourceVariable v, BasicBlock bb, int i) {
|
||||
variableWrite(bb, i, v, _) and
|
||||
liveAfterWrite(bb, i, v)
|
||||
} or
|
||||
TPhiNode(SourceVariable v, BasicBlock bb) {
|
||||
inDefDominanceFrontier(bb, v) and
|
||||
liveAtEntry(bb, v)
|
||||
}
|
||||
|
||||
private module SsaDefReaches {
|
||||
newtype TSsaRefKind =
|
||||
SsaActualRead() or
|
||||
SsaPhiRead() or
|
||||
SsaDef()
|
||||
|
||||
class SsaRead = SsaActualRead or SsaPhiRead;
|
||||
|
||||
/**
|
||||
* A classification of SSA variable references into reads and definitions.
|
||||
*/
|
||||
class SsaRefKind extends TSsaRefKind {
|
||||
string toString() {
|
||||
this = SsaActualRead() and
|
||||
result = "SsaActualRead"
|
||||
or
|
||||
this = SsaPhiRead() and
|
||||
result = "SsaPhiRead"
|
||||
or
|
||||
this = SsaDef() and
|
||||
result = "SsaDef"
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
this instanceof SsaRead and
|
||||
result = 0
|
||||
or
|
||||
this = SsaDef() and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb` is in the dominance frontier of a block containing a
|
||||
* read of `v`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate inReadDominanceFrontier(BasicBlock bb, SourceVariable v) {
|
||||
exists(BasicBlock readbb | inDominanceFrontier(readbb, bb) |
|
||||
lastRefIsRead(readbb, v)
|
||||
or
|
||||
phiRead(readbb, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a phi-read node should be inserted for variable `v` at the beginning
|
||||
* of basic block `bb`.
|
||||
*
|
||||
* Phi-read nodes are like normal phi nodes, but they are inserted based on reads
|
||||
* instead of writes, and only if the dominance-frontier block does not already
|
||||
* contain a reference (read or write) to `v`. Unlike normal phi nodes, this is
|
||||
* an internal implementation detail that is not exposed.
|
||||
*
|
||||
* The motivation for adding phi-reads is to improve performance of the use-use
|
||||
* calculation in cases where there is a large number of reads that can reach the
|
||||
* same join-point, and from there reach a large number of basic blocks. Example:
|
||||
*
|
||||
* ```cs
|
||||
* if (a)
|
||||
* use(x);
|
||||
* else if (b)
|
||||
* use(x);
|
||||
* else if (c)
|
||||
* use(x);
|
||||
* else if (d)
|
||||
* use(x);
|
||||
* // many more ifs ...
|
||||
*
|
||||
* // phi-read for `x` inserted here
|
||||
*
|
||||
* // program not mentioning `x`, with large basic block graph
|
||||
*
|
||||
* use(x);
|
||||
* ```
|
||||
*
|
||||
* Without phi-reads, the analysis has to replicate reachability for each of
|
||||
* the guarded uses of `x`. However, with phi-reads, the analysis will limit
|
||||
* each conditional use of `x` to reach the basic block containing the phi-read
|
||||
* node for `x`, and only that basic block will have to compute reachability
|
||||
* through the remainder of the large program.
|
||||
*
|
||||
* Like normal reads, each phi-read node `phi-read` can be reached from exactly
|
||||
* one SSA definition (without passing through another definition): Assume, for
|
||||
* the sake of contradiction, that there are two reaching definitions `def1` and
|
||||
* `def2`. Now, if both `def1` and `def2` dominate `phi-read`, then the nearest
|
||||
* dominating definition will prevent the other from reaching `phi-read`. So, at
|
||||
* least one of `def1` and `def2` cannot dominate `phi-read`; assume it is `def1`.
|
||||
* Then `def1` must go through one of its dominance-frontier blocks in order to
|
||||
* reach `phi-read`. However, such a block will always start with a (normal) phi
|
||||
* node, which contradicts reachability.
|
||||
*
|
||||
* Also, like normal reads, the unique SSA definition `def` that reaches `phi-read`,
|
||||
* will dominate `phi-read`. Assuming it doesn't means that the path from `def`
|
||||
* to `phi-read` goes through a dominance-frontier block, and hence a phi node,
|
||||
* which contradicts reachability.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate phiRead(BasicBlock bb, SourceVariable v) {
|
||||
inReadDominanceFrontier(bb, v) and
|
||||
liveAtEntry(bb, v) and
|
||||
// only if there are no other references to `v` inside `bb`
|
||||
not ref(bb, _, v, _) and
|
||||
not exists(Definition def | def.definesAt(v, bb, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v`,
|
||||
* either a read (when `k` is `SsaRead()`) or an SSA definition (when `k`
|
||||
* is `SsaDef()`).
|
||||
*
|
||||
* Unlike `Liveness::ref`, this includes `phi` nodes.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaRef(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
|
||||
variableRead(bb, i, v, _) and
|
||||
k = SsaActualRead()
|
||||
or
|
||||
phiRead(bb, v) and
|
||||
i = -1 and
|
||||
k = SsaPhiRead()
|
||||
or
|
||||
any(Definition def).definesAt(v, bb, i) and
|
||||
k = SsaDef()
|
||||
}
|
||||
|
||||
private newtype OrderedSsaRefIndex =
|
||||
MkOrderedSsaRefIndex(int i, SsaRefKind k) { ssaRef(_, i, _, k) }
|
||||
|
||||
private OrderedSsaRefIndex ssaRefOrd(BasicBlock bb, int i, SourceVariable v, SsaRefKind k, int ord) {
|
||||
ssaRef(bb, i, v, k) and
|
||||
result = MkOrderedSsaRefIndex(i, k) and
|
||||
ord = k.getOrder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the reference to `v` at the `i`th node of basic
|
||||
* block `bb`, which has the given reference kind `k`.
|
||||
*
|
||||
* For example, if `bb` is a basic block with a phi node for `v` (considered
|
||||
* to be at index -1), reads `v` at node 2, and defines it at node 5, we have:
|
||||
*
|
||||
* ```ql
|
||||
* ssaRefRank(bb, -1, v, SsaDef()) = 1 // phi node
|
||||
* ssaRefRank(bb, 2, v, Read()) = 2 // read at node 2
|
||||
* ssaRefRank(bb, 5, v, SsaDef()) = 3 // definition at node 5
|
||||
* ```
|
||||
*
|
||||
* Reads are considered before writes when they happen at the same index.
|
||||
*/
|
||||
int ssaRefRank(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
|
||||
ssaRefOrd(bb, i, v, k, _) =
|
||||
rank[result](int j, int ord, OrderedSsaRefIndex res |
|
||||
res = ssaRefOrd(bb, j, v, _, ord)
|
||||
|
|
||||
res order by j, ord
|
||||
)
|
||||
}
|
||||
|
||||
int maxSsaRefRank(BasicBlock bb, SourceVariable v) {
|
||||
result = ssaRefRank(bb, _, v, _) and
|
||||
not result + 1 = ssaRefRank(bb, _, v, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition `def` reaches rank index `rnk` in its own
|
||||
* basic block `bb`.
|
||||
*/
|
||||
predicate ssaDefReachesRank(BasicBlock bb, Definition def, int rnk, SourceVariable v) {
|
||||
exists(int i |
|
||||
rnk = ssaRefRank(bb, i, v, SsaDef()) and
|
||||
def.definesAt(v, bb, i)
|
||||
)
|
||||
or
|
||||
ssaDefReachesRank(bb, def, rnk - 1, v) and
|
||||
rnk = ssaRefRank(bb, _, v, any(SsaRead k))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches index `i` in the same
|
||||
* basic block `bb`, without crossing another SSA definition of `v`.
|
||||
*/
|
||||
predicate ssaDefReachesReadWithinBlock(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
exists(int rnk |
|
||||
ssaDefReachesRank(bb, def, rnk, v) and
|
||||
rnk = ssaRefRank(bb, i, v, any(SsaRead k))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `ssaRefRank()`, but restricted to a particular SSA definition `def`.
|
||||
*/
|
||||
int ssaDefRank(Definition def, SourceVariable v, BasicBlock bb, int i, SsaRefKind k) {
|
||||
v = def.getSourceVariable() and
|
||||
result = ssaRefRank(bb, i, v, k) and
|
||||
(
|
||||
ssaDefReachesRead(_, def, bb, i)
|
||||
or
|
||||
def.definesAt(_, bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the reference to `def` at index `i` in basic block `bb` is the
|
||||
* last reference to `v` inside `bb`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v)
|
||||
}
|
||||
|
||||
predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v, SsaRefKind k) {
|
||||
exists(ssaDefRank(def, v, bb, _, k))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
|
||||
* `bb2` is a transitive successor of `bb1`, `def` is live at the end of _some_
|
||||
* predecessor of `bb2`, and the underlying variable for `def` is neither read
|
||||
* nor written in any block on the path between `bb1` and `bb2`.
|
||||
*
|
||||
* Phi reads are considered as normal reads for this predicate.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate varBlockReachesInclPhiRead(Definition def, BasicBlock bb1, BasicBlock bb2) {
|
||||
defOccursInBlock(def, bb1, _, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReachesInclPhiRead(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate phiReadStep(Definition def, SourceVariable v, BasicBlock bb1, BasicBlock bb2) {
|
||||
varBlockReachesInclPhiRead(def, bb1, bb2) and
|
||||
defOccursInBlock(def, bb2, v, SsaPhiRead())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate varBlockReachesExclPhiRead(Definition def, BasicBlock bb1, BasicBlock bb2) {
|
||||
varBlockReachesInclPhiRead(pragma[only_bind_into](def), bb1, pragma[only_bind_into](bb2)) and
|
||||
ssaRef(bb2, _, def.getSourceVariable(), [SsaActualRead().(TSsaRefKind), SsaDef()])
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReachesExclPhiRead(def, mid, bb2) and
|
||||
phiReadStep(def, _, bb1, mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
|
||||
* the underlying variable `v` of `def` is accessed in basic block `bb2`
|
||||
* (either a read or a write), `bb2` is a transitive successor of `bb1`, and
|
||||
* `v` is neither read nor written in any block on the path between `bb1`
|
||||
* and `bb2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate varBlockReaches(Definition def, BasicBlock bb1, BasicBlock bb2) {
|
||||
varBlockReachesExclPhiRead(def, bb1, bb2) and
|
||||
not defOccursInBlock(def, bb1, _, SsaPhiRead())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
|
||||
varBlockReaches(def, bb1, bb2) and
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaActualRead()) = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb` (either a read or a write),
|
||||
* `bb1` can reach a transitive successor `bb2` where `def` is no longer live,
|
||||
* and `v` is neither read nor written in any block on the path between `bb`
|
||||
* and `bb2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate varBlockReachesExit(Definition def, BasicBlock bb) {
|
||||
exists(BasicBlock bb2 | varBlockReachesInclPhiRead(def, bb, bb2) |
|
||||
not defOccursInBlock(def, bb2, _, _) and
|
||||
not ssaDefReachesEndOfBlock(bb2, def, _)
|
||||
)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReachesExit(def, mid) and
|
||||
phiReadStep(def, _, bb, mid)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate phiReadExposedForTesting = phiRead/2;
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the SSA definition of `v` at `def` reaches the end of basic
|
||||
* block `bb`, at which point it is still live, without crossing another
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) {
|
||||
exists(int last |
|
||||
last = maxSsaRefRank(pragma[only_bind_into](bb), pragma[only_bind_into](v)) and
|
||||
ssaDefReachesRank(bb, def, last, v) and
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
liveThrough(bb, pragma[only_bind_into](v))
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `inp` is an input to the phi node `phi` along the edge originating in `bb`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
|
||||
exists(SourceVariable v, BasicBlock bbDef |
|
||||
phi.definesAt(v, bbDef, _) and
|
||||
getABasicBlockPredecessor(bbDef) = bb and
|
||||
ssaDefReachesEndOfBlock(bb, inp, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the SSA definition of `v` at `def` reaches a read at index `i` in
|
||||
* basic block `bb`, without crossing another SSA definition of `v`. The read
|
||||
* is of kind `rk`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i)
|
||||
or
|
||||
ssaRef(bb, i, v, any(SsaRead k)) and
|
||||
ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and
|
||||
not ssaDefReachesReadWithinBlock(v, _, bb, i)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read
|
||||
* or a write), `def` is read at index `i2` in basic block `bb2`, and there is a
|
||||
* path between them without any read of `def`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
|
||||
exists(int rnk |
|
||||
rnk = ssaDefRank(def, _, bb1, i1, _) and
|
||||
rnk + 1 = ssaDefRank(def, _, bb1, i2, SsaActualRead()) and
|
||||
variableRead(bb1, i2, _, _) and
|
||||
bb2 = bb1
|
||||
)
|
||||
or
|
||||
lastSsaRef(def, _, bb1, i1) and
|
||||
defAdjacentRead(def, bb1, bb2, i2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate adjacentDefRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
v = def.getSourceVariable()
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) |
|
||||
ssaRef(bb1, i1, v, SsaDef())
|
||||
or
|
||||
variableRead(bb1, i1, v, true)
|
||||
)
|
||||
or
|
||||
exists(BasicBlock bb3, int i3 |
|
||||
adjacentDefReachesRead(def, bb1, i1, bb3, i3) and
|
||||
variableRead(bb3, i3, _, false) and
|
||||
adjacentDefRead(def, bb3, i3, bb2, i2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `adjacentDefRead`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
|
||||
adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
|
||||
variableRead(bb2, i2, _, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the node at index `i` in `bb` is a last reference to SSA definition
|
||||
* `def`. The reference is last because it can reach another write `next`,
|
||||
* without passing through another read or write.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
exists(SourceVariable v |
|
||||
// Next reference to `v` inside `bb` is a write
|
||||
exists(int rnk, int j |
|
||||
rnk = ssaDefRank(def, v, bb, i, _) and
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
)
|
||||
or
|
||||
// Can reach a write using one or more steps
|
||||
lastSsaRef(def, v, bb, i) and
|
||||
exists(BasicBlock bb2 |
|
||||
varBlockReaches(def, bb, bb2) and
|
||||
1 = ssaDefRank(next, v, bb2, _, SsaDef())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `inp` is an immediately preceding definition of uncertain definition
|
||||
* `def`. Since `def` is uncertain, the value from the preceding definition might
|
||||
* still be valid.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate uncertainWriteDefinitionInput(UncertainWriteDefinition def, Definition inp) {
|
||||
lastRefRedef(inp, _, _, def)
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesUncertainRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
|
||||
variableRead(bb2, i2, _, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `lastRefRedef`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedefNoUncertainReads(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
lastRefRedef(def, bb, i, next) and
|
||||
not variableRead(bb, i, def.getSourceVariable(), false)
|
||||
or
|
||||
exists(BasicBlock bb0, int i0 |
|
||||
lastRefRedef(def, bb0, i0, next) and
|
||||
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the node at index `i` in `bb` is a last reference to SSA
|
||||
* definition `def`.
|
||||
*
|
||||
* That is, the node can reach the end of the enclosing callable, or another
|
||||
* SSA definition for the underlying source variable, without passing through
|
||||
* another read.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRef(Definition def, BasicBlock bb, int i) {
|
||||
// Can reach another definition
|
||||
lastRefRedef(def, bb, i, _)
|
||||
or
|
||||
exists(SourceVariable v | lastSsaRef(def, v, bb, i) |
|
||||
// Can reach exit directly
|
||||
bb instanceof ExitBasicBlock
|
||||
or
|
||||
// Can reach a block using one or more steps, where `def` is no longer live
|
||||
varBlockReachesExit(def, bb)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `lastRefRedef`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefNoUncertainReads(Definition def, BasicBlock bb, int i) {
|
||||
lastRef(def, bb, i) and
|
||||
not variableRead(bb, i, def.getSourceVariable(), false)
|
||||
or
|
||||
exists(BasicBlock bb0, int i0 |
|
||||
lastRef(def, bb0, i0) and
|
||||
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
|
||||
)
|
||||
}
|
||||
|
||||
/** A static single assignment (SSA) definition. */
|
||||
class Definition extends TDefinition {
|
||||
/** Gets the source variable underlying this SSA definition. */
|
||||
SourceVariable getSourceVariable() { this.definesAt(result, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if this SSA definition defines `v` at index `i` in basic block `bb`.
|
||||
* Phi nodes are considered to be at index `-1`, while normal variable writes
|
||||
* are at the index of the control flow node they wrap.
|
||||
*/
|
||||
final predicate definesAt(SourceVariable v, BasicBlock bb, int i) {
|
||||
this = TWriteDef(v, bb, i)
|
||||
or
|
||||
this = TPhiNode(v, bb) and i = -1
|
||||
}
|
||||
|
||||
/** Gets the basic block to which this SSA definition belongs. */
|
||||
final BasicBlock getBasicBlock() { this.definesAt(_, result, _) }
|
||||
|
||||
/** Gets a textual representation of this SSA definition. */
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
/** An SSA definition that corresponds to a write. */
|
||||
class WriteDefinition extends Definition, TWriteDef {
|
||||
private SourceVariable v;
|
||||
private BasicBlock bb;
|
||||
private int i;
|
||||
|
||||
WriteDefinition() { this = TWriteDef(v, bb, i) }
|
||||
|
||||
override string toString() { result = "WriteDef" }
|
||||
}
|
||||
|
||||
/** A phi node. */
|
||||
class PhiNode extends Definition, TPhiNode {
|
||||
override string toString() { result = "Phi" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An SSA definition that represents an uncertain update of the underlying
|
||||
* source variable.
|
||||
*/
|
||||
class UncertainWriteDefinition extends WriteDefinition {
|
||||
UncertainWriteDefinition() {
|
||||
exists(SourceVariable v, BasicBlock bb, int i |
|
||||
this.definesAt(v, bb, i) and
|
||||
variableWrite(bb, i, v, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides a set of consistency queries. */
|
||||
module Consistency {
|
||||
abstract class RelevantDefinition extends Definition {
|
||||
abstract predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
);
|
||||
}
|
||||
|
||||
query predicate nonUniqueDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
not exists(unique(Definition def0 | ssaDefReachesRead(v, def0, bb, i)))
|
||||
}
|
||||
|
||||
query predicate readWithoutDef(SourceVariable v, BasicBlock bb, int i) {
|
||||
variableRead(bb, i, v, _) and
|
||||
not ssaDefReachesRead(v, _, bb, i)
|
||||
}
|
||||
|
||||
query predicate deadDef(RelevantDefinition def, SourceVariable v) {
|
||||
v = def.getSourceVariable() and
|
||||
not ssaDefReachesRead(_, def, _, _) and
|
||||
not phiHasInputFromBlock(_, def, _) and
|
||||
not uncertainWriteDefinitionInput(_, def)
|
||||
}
|
||||
|
||||
query predicate notDominatedByDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef) |
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
(bb != bbDef or i < iDef)
|
||||
or
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
not ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
30
csharp/ql/lib/semmle/code/cil/internal/SsaImplSpecific.qll
Normal file
30
csharp/ql/lib/semmle/code/cil/internal/SsaImplSpecific.qll
Normal file
@@ -0,0 +1,30 @@
|
||||
/** Provides the CIL specific parameters for `SsaImplCommon.qll`. */
|
||||
|
||||
private import cil
|
||||
private import SsaImpl
|
||||
|
||||
class BasicBlock = CIL::BasicBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock = CIL::ExitBasicBlock;
|
||||
|
||||
class SourceVariable = CIL::StackVariable;
|
||||
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
forceCachingInSameStage() and
|
||||
exists(CIL::VariableUpdate vu |
|
||||
vu.updatesAt(bb, i) and
|
||||
v = vu.getVariable() and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(CIL::ReadAccess ra | bb.getNode(i) = ra |
|
||||
ra.getTarget() = v and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
@@ -192,7 +192,8 @@ class BasicBlock extends TBasicBlockStart {
|
||||
* Gets the basic block that immediately dominates this basic block, if any.
|
||||
*
|
||||
* That is, all paths reaching this basic block from some entry point
|
||||
* basic block must go through the result.
|
||||
* basic block must go through the result, which is an immediate basic block
|
||||
* predecessor of this basic block.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
@@ -206,7 +207,8 @@ class BasicBlock extends TBasicBlockStart {
|
||||
*
|
||||
* The basic block starting on line 2 is an immediate dominator of
|
||||
* the basic block online 4 (all paths from the entry point of `M`
|
||||
* to `return s.Length;` must go through the null check.
|
||||
* to `return s.Length;` must go through the null check, and the null check
|
||||
* is an immediate predecessor of `return s.Length;`).
|
||||
*/
|
||||
BasicBlock getImmediateDominator() { bbIDominates(result, this) }
|
||||
|
||||
|
||||
@@ -6,158 +6,26 @@ import csharp
|
||||
* scope variables.
|
||||
*/
|
||||
module PreSsa {
|
||||
private import AssignableDefinitions
|
||||
private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl
|
||||
private import semmle.code.csharp.controlflow.internal.PreBasicBlocks as PreBasicBlocks
|
||||
private import semmle.code.csharp.dataflow.internal.SsaImplCommon as SsaImplCommon
|
||||
|
||||
private predicate definitionAt(
|
||||
AssignableDefinition def, SsaInput::BasicBlock bb, int i, SsaInput::SourceVariable v
|
||||
) {
|
||||
bb.getElement(i) = def.getExpr() and
|
||||
v = def.getTarget() and
|
||||
// In cases like `(x, x) = (0, 1)`, we discard the first (dead) definition of `x`
|
||||
not exists(TupleAssignmentDefinition first, TupleAssignmentDefinition second | first = def |
|
||||
second.getAssignment() = first.getAssignment() and
|
||||
second.getEvaluationOrder() > first.getEvaluationOrder() and
|
||||
second.getTarget() = v
|
||||
)
|
||||
or
|
||||
def.(ImplicitParameterDefinition).getParameter() = v and
|
||||
exists(Callable c | v = c.getAParameter() |
|
||||
scopeFirst(c, bb) and
|
||||
i = -1
|
||||
)
|
||||
}
|
||||
|
||||
predicate implicitEntryDef(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v) {
|
||||
not v instanceof LocalScopeVariable and
|
||||
c = v.getACallable() and
|
||||
scopeFirst(c, bb)
|
||||
}
|
||||
|
||||
module SsaInput implements SsaImplCommon::InputSig {
|
||||
class BasicBlock = PreBasicBlocks::PreBasicBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock extends BasicBlock {
|
||||
ExitBasicBlock() { scopeLast(_, this.getLastElement(), _) }
|
||||
}
|
||||
|
||||
/** Holds if `a` is assigned in non-constructor callable `c`. */
|
||||
pragma[nomagic]
|
||||
private predicate assignableDefinition(Assignable a, Callable c) {
|
||||
exists(AssignableDefinition def | def.getTarget() = a |
|
||||
c = def.getEnclosingCallable() and
|
||||
not c instanceof Constructor
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `a` is accessed in callable `c`. */
|
||||
pragma[nomagic]
|
||||
private predicate assignableAccess(Assignable a, Callable c) {
|
||||
exists(AssignableAccess aa | aa.getTarget() = a | c = aa.getEnclosingCallable())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate assignableNoCapturing(Assignable a, Callable c) {
|
||||
assignableAccess(a, c) and
|
||||
/*
|
||||
* The code below is equivalent to
|
||||
* ```ql
|
||||
* not exists(Callable other | assignableDefinition(a, other) | other != c)
|
||||
* ```
|
||||
* but it avoids a Cartesian product in the compiler generated antijoin
|
||||
* predicate.
|
||||
*/
|
||||
|
||||
(
|
||||
not assignableDefinition(a, _)
|
||||
or
|
||||
c = unique(Callable c0 | assignableDefinition(a, c0) | c0)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate assignableNoComplexQualifiers(Assignable a) {
|
||||
forall(QualifiableExpr qe | qe.(AssignableAccess).getTarget() = a | qe.targetIsThisInstance())
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple assignable. Either a local scope variable or a field/property
|
||||
* that behaves like a local scope variable.
|
||||
*/
|
||||
class SourceVariable extends Assignable {
|
||||
private Callable c;
|
||||
|
||||
SourceVariable() {
|
||||
(
|
||||
this instanceof LocalScopeVariable
|
||||
or
|
||||
this = any(Field f | not f.isVolatile())
|
||||
or
|
||||
this = any(TrivialProperty tp | not tp.isOverridableOrImplementable())
|
||||
) and
|
||||
assignableNoCapturing(this, c) and
|
||||
assignableNoComplexQualifiers(this)
|
||||
}
|
||||
|
||||
/** Gets a callable in which this simple assignable can be analyzed. */
|
||||
Callable getACallable() { result = c }
|
||||
}
|
||||
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(AssignableDefinition def |
|
||||
definitionAt(def, bb, i, v) and
|
||||
if def.getTargetAccess().isRefArgument() then certain = false else certain = true
|
||||
)
|
||||
or
|
||||
exists(Callable c |
|
||||
implicitEntryDef(c, bb, v) and
|
||||
i = -1 and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(AssignableRead read |
|
||||
read = bb.getElement(i) and
|
||||
read.getTarget() = v and
|
||||
certain = true
|
||||
)
|
||||
or
|
||||
v =
|
||||
any(LocalScopeVariable lsv |
|
||||
lsv.getCallable() = bb.(ExitBasicBlock).getEnclosingCallable() and
|
||||
i = bb.length() and
|
||||
(lsv.isRef() or v.(Parameter).isOut()) and
|
||||
certain = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
import pressa.SsaImplSpecific
|
||||
private import pressa.SsaImplCommon as SsaImpl
|
||||
|
||||
class Definition extends SsaImpl::Definition {
|
||||
final AssignableRead getARead() {
|
||||
exists(SsaInput::BasicBlock bb, int i |
|
||||
exists(BasicBlock bb, int i |
|
||||
SsaImpl::ssaDefReachesRead(_, this, bb, i) and
|
||||
result = bb.getElement(i)
|
||||
)
|
||||
}
|
||||
|
||||
final AssignableDefinition getDefinition() {
|
||||
exists(SsaInput::BasicBlock bb, int i, SsaInput::SourceVariable v |
|
||||
exists(BasicBlock bb, int i, SourceVariable v |
|
||||
this.definesAt(v, bb, i) and
|
||||
definitionAt(result, bb, i, v)
|
||||
)
|
||||
}
|
||||
|
||||
final AssignableRead getAFirstRead() {
|
||||
exists(SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 |
|
||||
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
|
||||
this.definesAt(_, bb1, i1) and
|
||||
SsaImpl::adjacentDefRead(this, bb1, i1, bb2, i2) and
|
||||
result = bb2.getElement(i2)
|
||||
@@ -174,14 +42,14 @@ module PreSsa {
|
||||
not result instanceof PhiNode
|
||||
}
|
||||
|
||||
final predicate isLiveAtEndOfBlock(SsaInput::BasicBlock bb) {
|
||||
final predicate isLiveAtEndOfBlock(BasicBlock bb) {
|
||||
SsaImpl::ssaDefReachesEndOfBlock(bb, this, _)
|
||||
}
|
||||
|
||||
Location getLocation() {
|
||||
result = this.getDefinition().getLocation()
|
||||
or
|
||||
exists(Callable c, SsaInput::BasicBlock bb, SsaInput::SourceVariable v |
|
||||
exists(Callable c, BasicBlock bb, SourceVariable v |
|
||||
this.definesAt(v, bb, -1) and
|
||||
implicitEntryDef(c, bb, v) and
|
||||
result = c.getLocation()
|
||||
@@ -196,9 +64,9 @@ module PreSsa {
|
||||
}
|
||||
|
||||
predicate adjacentReadPairSameVar(AssignableRead read1, AssignableRead read2) {
|
||||
exists(SsaInput::BasicBlock bb1, int i1, SsaInput::BasicBlock bb2, int i2 |
|
||||
exists(BasicBlock bb1, int i1, BasicBlock bb2, int i2 |
|
||||
read1 = bb1.getElement(i1) and
|
||||
SsaInput::variableRead(bb1, i1, _, true) and
|
||||
variableRead(bb1, i1, _, true) and
|
||||
SsaImpl::adjacentDefRead(_, bb1, i1, bb2, i2) and
|
||||
read2 = bb2.getElement(i2)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,795 @@
|
||||
/**
|
||||
* Provides a language-independent implementation of static single assignment
|
||||
* (SSA) form.
|
||||
*/
|
||||
|
||||
private import SsaImplSpecific
|
||||
|
||||
private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { getABasicBlockSuccessor(result) = bb }
|
||||
|
||||
/**
|
||||
* Liveness analysis (based on source variables) to restrict the size of the
|
||||
* SSA representation.
|
||||
*/
|
||||
private module Liveness {
|
||||
/**
|
||||
* A classification of variable references into reads (of a given kind) and
|
||||
* (certain or uncertain) writes.
|
||||
*/
|
||||
private newtype TRefKind =
|
||||
Read(boolean certain) { certain in [false, true] } or
|
||||
Write(boolean certain) { certain in [false, true] }
|
||||
|
||||
private class RefKind extends TRefKind {
|
||||
string toString() {
|
||||
exists(boolean certain | this = Read(certain) and result = "read (" + certain + ")")
|
||||
or
|
||||
exists(boolean certain | this = Write(certain) and result = "write (" + certain + ")")
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
this = Read(_) and
|
||||
result = 0
|
||||
or
|
||||
this = Write(_) and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`.
|
||||
*/
|
||||
predicate ref(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
exists(boolean certain | variableRead(bb, i, v, certain) | k = Read(certain))
|
||||
or
|
||||
exists(boolean certain | variableWrite(bb, i, v, certain) | k = Write(certain))
|
||||
}
|
||||
|
||||
private newtype OrderedRefIndex =
|
||||
MkOrderedRefIndex(int i, int tag) {
|
||||
exists(RefKind rk | ref(_, i, _, rk) | tag = rk.getOrder())
|
||||
}
|
||||
|
||||
private OrderedRefIndex refOrd(BasicBlock bb, int i, SourceVariable v, RefKind k, int ord) {
|
||||
ref(bb, i, v, k) and
|
||||
result = MkOrderedRefIndex(i, ord) and
|
||||
ord = k.getOrder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the reference to `v` at the `i`th node of
|
||||
* basic block `bb`, which has the given reference kind `k`.
|
||||
*
|
||||
* Reads are considered before writes when they happen at the same index.
|
||||
*/
|
||||
private int refRank(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
refOrd(bb, i, v, k, _) =
|
||||
rank[result](int j, int ord, OrderedRefIndex res |
|
||||
res = refOrd(bb, j, v, _, ord)
|
||||
|
|
||||
res order by j, ord
|
||||
)
|
||||
}
|
||||
|
||||
private int maxRefRank(BasicBlock bb, SourceVariable v) {
|
||||
result = refRank(bb, _, v, _) and
|
||||
not result + 1 = refRank(bb, _, v, _)
|
||||
}
|
||||
|
||||
predicate lastRefIsRead(BasicBlock bb, SourceVariable v) {
|
||||
maxRefRank(bb, v) = refRank(bb, _, v, Read(_))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the first reference to `v` inside basic block `bb`
|
||||
* that is either a read or a certain write.
|
||||
*/
|
||||
private int firstReadOrCertainWrite(BasicBlock bb, SourceVariable v) {
|
||||
result =
|
||||
min(int r, RefKind k |
|
||||
r = refRank(bb, _, v, k) and
|
||||
k != Write(false)
|
||||
|
|
||||
r
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if source variable `v` is live at the beginning of basic block `bb`.
|
||||
*/
|
||||
predicate liveAtEntry(BasicBlock bb, SourceVariable v) {
|
||||
// The first read or certain write to `v` inside `bb` is a read
|
||||
refRank(bb, _, v, Read(_)) = firstReadOrCertainWrite(bb, v)
|
||||
or
|
||||
// There is no certain write to `v` inside `bb`, but `v` is live at entry
|
||||
// to a successor basic block of `bb`
|
||||
not exists(firstReadOrCertainWrite(bb, v)) and
|
||||
liveAtExit(bb, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if source variable `v` is live at the end of basic block `bb`.
|
||||
*/
|
||||
predicate liveAtExit(BasicBlock bb, SourceVariable v) {
|
||||
liveAtEntry(getABasicBlockSuccessor(bb), v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live in basic block `bb` at index `i`.
|
||||
* The rank of `i` is `rnk` as defined by `refRank()`.
|
||||
*/
|
||||
private predicate liveAtRank(BasicBlock bb, int i, SourceVariable v, int rnk) {
|
||||
exists(RefKind kind | rnk = refRank(bb, i, v, kind) |
|
||||
rnk = maxRefRank(bb, v) and
|
||||
liveAtExit(bb, v)
|
||||
or
|
||||
ref(bb, i, v, kind) and
|
||||
kind = Read(_)
|
||||
or
|
||||
exists(RefKind nextKind |
|
||||
liveAtRank(bb, _, v, rnk + 1) and
|
||||
rnk + 1 = refRank(bb, _, v, nextKind) and
|
||||
nextKind != Write(true)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live after the (certain or uncertain) write at
|
||||
* index `i` inside basic block `bb`.
|
||||
*/
|
||||
predicate liveAfterWrite(BasicBlock bb, int i, SourceVariable v) {
|
||||
exists(int rnk | rnk = refRank(bb, i, v, Write(_)) | liveAtRank(bb, i, v, rnk))
|
||||
}
|
||||
}
|
||||
|
||||
private import Liveness
|
||||
|
||||
/**
|
||||
* Holds if `df` is in the dominance frontier of `bb`.
|
||||
*
|
||||
* This is equivalent to:
|
||||
*
|
||||
* ```ql
|
||||
* bb = getImmediateBasicBlockDominator*(getABasicBlockPredecessor(df)) and
|
||||
* not bb = getImmediateBasicBlockDominator+(df)
|
||||
* ```
|
||||
*/
|
||||
private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) {
|
||||
bb = getABasicBlockPredecessor(df) and not bb = getImmediateBasicBlockDominator(df)
|
||||
or
|
||||
exists(BasicBlock prev | inDominanceFrontier(prev, df) |
|
||||
bb = getImmediateBasicBlockDominator(prev) and
|
||||
not bb = getImmediateBasicBlockDominator(df)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb` is in the dominance frontier of a block containing a
|
||||
* definition of `v`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) {
|
||||
exists(BasicBlock defbb, Definition def |
|
||||
def.definesAt(v, defbb, _) and
|
||||
inDominanceFrontier(defbb, bb)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TDefinition =
|
||||
TWriteDef(SourceVariable v, BasicBlock bb, int i) {
|
||||
variableWrite(bb, i, v, _) and
|
||||
liveAfterWrite(bb, i, v)
|
||||
} or
|
||||
TPhiNode(SourceVariable v, BasicBlock bb) {
|
||||
inDefDominanceFrontier(bb, v) and
|
||||
liveAtEntry(bb, v)
|
||||
}
|
||||
|
||||
private module SsaDefReaches {
|
||||
newtype TSsaRefKind =
|
||||
SsaActualRead() or
|
||||
SsaPhiRead() or
|
||||
SsaDef()
|
||||
|
||||
class SsaRead = SsaActualRead or SsaPhiRead;
|
||||
|
||||
/**
|
||||
* A classification of SSA variable references into reads and definitions.
|
||||
*/
|
||||
class SsaRefKind extends TSsaRefKind {
|
||||
string toString() {
|
||||
this = SsaActualRead() and
|
||||
result = "SsaActualRead"
|
||||
or
|
||||
this = SsaPhiRead() and
|
||||
result = "SsaPhiRead"
|
||||
or
|
||||
this = SsaDef() and
|
||||
result = "SsaDef"
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
this instanceof SsaRead and
|
||||
result = 0
|
||||
or
|
||||
this = SsaDef() and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb` is in the dominance frontier of a block containing a
|
||||
* read of `v`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate inReadDominanceFrontier(BasicBlock bb, SourceVariable v) {
|
||||
exists(BasicBlock readbb | inDominanceFrontier(readbb, bb) |
|
||||
lastRefIsRead(readbb, v)
|
||||
or
|
||||
phiRead(readbb, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a phi-read node should be inserted for variable `v` at the beginning
|
||||
* of basic block `bb`.
|
||||
*
|
||||
* Phi-read nodes are like normal phi nodes, but they are inserted based on reads
|
||||
* instead of writes, and only if the dominance-frontier block does not already
|
||||
* contain a reference (read or write) to `v`. Unlike normal phi nodes, this is
|
||||
* an internal implementation detail that is not exposed.
|
||||
*
|
||||
* The motivation for adding phi-reads is to improve performance of the use-use
|
||||
* calculation in cases where there is a large number of reads that can reach the
|
||||
* same join-point, and from there reach a large number of basic blocks. Example:
|
||||
*
|
||||
* ```cs
|
||||
* if (a)
|
||||
* use(x);
|
||||
* else if (b)
|
||||
* use(x);
|
||||
* else if (c)
|
||||
* use(x);
|
||||
* else if (d)
|
||||
* use(x);
|
||||
* // many more ifs ...
|
||||
*
|
||||
* // phi-read for `x` inserted here
|
||||
*
|
||||
* // program not mentioning `x`, with large basic block graph
|
||||
*
|
||||
* use(x);
|
||||
* ```
|
||||
*
|
||||
* Without phi-reads, the analysis has to replicate reachability for each of
|
||||
* the guarded uses of `x`. However, with phi-reads, the analysis will limit
|
||||
* each conditional use of `x` to reach the basic block containing the phi-read
|
||||
* node for `x`, and only that basic block will have to compute reachability
|
||||
* through the remainder of the large program.
|
||||
*
|
||||
* Like normal reads, each phi-read node `phi-read` can be reached from exactly
|
||||
* one SSA definition (without passing through another definition): Assume, for
|
||||
* the sake of contradiction, that there are two reaching definitions `def1` and
|
||||
* `def2`. Now, if both `def1` and `def2` dominate `phi-read`, then the nearest
|
||||
* dominating definition will prevent the other from reaching `phi-read`. So, at
|
||||
* least one of `def1` and `def2` cannot dominate `phi-read`; assume it is `def1`.
|
||||
* Then `def1` must go through one of its dominance-frontier blocks in order to
|
||||
* reach `phi-read`. However, such a block will always start with a (normal) phi
|
||||
* node, which contradicts reachability.
|
||||
*
|
||||
* Also, like normal reads, the unique SSA definition `def` that reaches `phi-read`,
|
||||
* will dominate `phi-read`. Assuming it doesn't means that the path from `def`
|
||||
* to `phi-read` goes through a dominance-frontier block, and hence a phi node,
|
||||
* which contradicts reachability.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate phiRead(BasicBlock bb, SourceVariable v) {
|
||||
inReadDominanceFrontier(bb, v) and
|
||||
liveAtEntry(bb, v) and
|
||||
// only if there are no other references to `v` inside `bb`
|
||||
not ref(bb, _, v, _) and
|
||||
not exists(Definition def | def.definesAt(v, bb, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v`,
|
||||
* either a read (when `k` is `SsaRead()`) or an SSA definition (when `k`
|
||||
* is `SsaDef()`).
|
||||
*
|
||||
* Unlike `Liveness::ref`, this includes `phi` nodes.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaRef(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
|
||||
variableRead(bb, i, v, _) and
|
||||
k = SsaActualRead()
|
||||
or
|
||||
phiRead(bb, v) and
|
||||
i = -1 and
|
||||
k = SsaPhiRead()
|
||||
or
|
||||
any(Definition def).definesAt(v, bb, i) and
|
||||
k = SsaDef()
|
||||
}
|
||||
|
||||
private newtype OrderedSsaRefIndex =
|
||||
MkOrderedSsaRefIndex(int i, SsaRefKind k) { ssaRef(_, i, _, k) }
|
||||
|
||||
private OrderedSsaRefIndex ssaRefOrd(BasicBlock bb, int i, SourceVariable v, SsaRefKind k, int ord) {
|
||||
ssaRef(bb, i, v, k) and
|
||||
result = MkOrderedSsaRefIndex(i, k) and
|
||||
ord = k.getOrder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the reference to `v` at the `i`th node of basic
|
||||
* block `bb`, which has the given reference kind `k`.
|
||||
*
|
||||
* For example, if `bb` is a basic block with a phi node for `v` (considered
|
||||
* to be at index -1), reads `v` at node 2, and defines it at node 5, we have:
|
||||
*
|
||||
* ```ql
|
||||
* ssaRefRank(bb, -1, v, SsaDef()) = 1 // phi node
|
||||
* ssaRefRank(bb, 2, v, Read()) = 2 // read at node 2
|
||||
* ssaRefRank(bb, 5, v, SsaDef()) = 3 // definition at node 5
|
||||
* ```
|
||||
*
|
||||
* Reads are considered before writes when they happen at the same index.
|
||||
*/
|
||||
int ssaRefRank(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
|
||||
ssaRefOrd(bb, i, v, k, _) =
|
||||
rank[result](int j, int ord, OrderedSsaRefIndex res |
|
||||
res = ssaRefOrd(bb, j, v, _, ord)
|
||||
|
|
||||
res order by j, ord
|
||||
)
|
||||
}
|
||||
|
||||
int maxSsaRefRank(BasicBlock bb, SourceVariable v) {
|
||||
result = ssaRefRank(bb, _, v, _) and
|
||||
not result + 1 = ssaRefRank(bb, _, v, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition `def` reaches rank index `rnk` in its own
|
||||
* basic block `bb`.
|
||||
*/
|
||||
predicate ssaDefReachesRank(BasicBlock bb, Definition def, int rnk, SourceVariable v) {
|
||||
exists(int i |
|
||||
rnk = ssaRefRank(bb, i, v, SsaDef()) and
|
||||
def.definesAt(v, bb, i)
|
||||
)
|
||||
or
|
||||
ssaDefReachesRank(bb, def, rnk - 1, v) and
|
||||
rnk = ssaRefRank(bb, _, v, any(SsaRead k))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches index `i` in the same
|
||||
* basic block `bb`, without crossing another SSA definition of `v`.
|
||||
*/
|
||||
predicate ssaDefReachesReadWithinBlock(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
exists(int rnk |
|
||||
ssaDefReachesRank(bb, def, rnk, v) and
|
||||
rnk = ssaRefRank(bb, i, v, any(SsaRead k))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `ssaRefRank()`, but restricted to a particular SSA definition `def`.
|
||||
*/
|
||||
int ssaDefRank(Definition def, SourceVariable v, BasicBlock bb, int i, SsaRefKind k) {
|
||||
v = def.getSourceVariable() and
|
||||
result = ssaRefRank(bb, i, v, k) and
|
||||
(
|
||||
ssaDefReachesRead(_, def, bb, i)
|
||||
or
|
||||
def.definesAt(_, bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the reference to `def` at index `i` in basic block `bb` is the
|
||||
* last reference to `v` inside `bb`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v)
|
||||
}
|
||||
|
||||
predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v, SsaRefKind k) {
|
||||
exists(ssaDefRank(def, v, bb, _, k))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
|
||||
* `bb2` is a transitive successor of `bb1`, `def` is live at the end of _some_
|
||||
* predecessor of `bb2`, and the underlying variable for `def` is neither read
|
||||
* nor written in any block on the path between `bb1` and `bb2`.
|
||||
*
|
||||
* Phi reads are considered as normal reads for this predicate.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate varBlockReachesInclPhiRead(Definition def, BasicBlock bb1, BasicBlock bb2) {
|
||||
defOccursInBlock(def, bb1, _, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReachesInclPhiRead(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate phiReadStep(Definition def, SourceVariable v, BasicBlock bb1, BasicBlock bb2) {
|
||||
varBlockReachesInclPhiRead(def, bb1, bb2) and
|
||||
defOccursInBlock(def, bb2, v, SsaPhiRead())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate varBlockReachesExclPhiRead(Definition def, BasicBlock bb1, BasicBlock bb2) {
|
||||
varBlockReachesInclPhiRead(pragma[only_bind_into](def), bb1, pragma[only_bind_into](bb2)) and
|
||||
ssaRef(bb2, _, def.getSourceVariable(), [SsaActualRead().(TSsaRefKind), SsaDef()])
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReachesExclPhiRead(def, mid, bb2) and
|
||||
phiReadStep(def, _, bb1, mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
|
||||
* the underlying variable `v` of `def` is accessed in basic block `bb2`
|
||||
* (either a read or a write), `bb2` is a transitive successor of `bb1`, and
|
||||
* `v` is neither read nor written in any block on the path between `bb1`
|
||||
* and `bb2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate varBlockReaches(Definition def, BasicBlock bb1, BasicBlock bb2) {
|
||||
varBlockReachesExclPhiRead(def, bb1, bb2) and
|
||||
not defOccursInBlock(def, bb1, _, SsaPhiRead())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
|
||||
varBlockReaches(def, bb1, bb2) and
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaActualRead()) = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb` (either a read or a write),
|
||||
* `bb1` can reach a transitive successor `bb2` where `def` is no longer live,
|
||||
* and `v` is neither read nor written in any block on the path between `bb`
|
||||
* and `bb2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate varBlockReachesExit(Definition def, BasicBlock bb) {
|
||||
exists(BasicBlock bb2 | varBlockReachesInclPhiRead(def, bb, bb2) |
|
||||
not defOccursInBlock(def, bb2, _, _) and
|
||||
not ssaDefReachesEndOfBlock(bb2, def, _)
|
||||
)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReachesExit(def, mid) and
|
||||
phiReadStep(def, _, bb, mid)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate phiReadExposedForTesting = phiRead/2;
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the SSA definition of `v` at `def` reaches the end of basic
|
||||
* block `bb`, at which point it is still live, without crossing another
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) {
|
||||
exists(int last |
|
||||
last = maxSsaRefRank(pragma[only_bind_into](bb), pragma[only_bind_into](v)) and
|
||||
ssaDefReachesRank(bb, def, last, v) and
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
liveThrough(bb, pragma[only_bind_into](v))
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `inp` is an input to the phi node `phi` along the edge originating in `bb`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
|
||||
exists(SourceVariable v, BasicBlock bbDef |
|
||||
phi.definesAt(v, bbDef, _) and
|
||||
getABasicBlockPredecessor(bbDef) = bb and
|
||||
ssaDefReachesEndOfBlock(bb, inp, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the SSA definition of `v` at `def` reaches a read at index `i` in
|
||||
* basic block `bb`, without crossing another SSA definition of `v`. The read
|
||||
* is of kind `rk`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i)
|
||||
or
|
||||
ssaRef(bb, i, v, any(SsaRead k)) and
|
||||
ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and
|
||||
not ssaDefReachesReadWithinBlock(v, _, bb, i)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read
|
||||
* or a write), `def` is read at index `i2` in basic block `bb2`, and there is a
|
||||
* path between them without any read of `def`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
|
||||
exists(int rnk |
|
||||
rnk = ssaDefRank(def, _, bb1, i1, _) and
|
||||
rnk + 1 = ssaDefRank(def, _, bb1, i2, SsaActualRead()) and
|
||||
variableRead(bb1, i2, _, _) and
|
||||
bb2 = bb1
|
||||
)
|
||||
or
|
||||
lastSsaRef(def, _, bb1, i1) and
|
||||
defAdjacentRead(def, bb1, bb2, i2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate adjacentDefRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
v = def.getSourceVariable()
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) |
|
||||
ssaRef(bb1, i1, v, SsaDef())
|
||||
or
|
||||
variableRead(bb1, i1, v, true)
|
||||
)
|
||||
or
|
||||
exists(BasicBlock bb3, int i3 |
|
||||
adjacentDefReachesRead(def, bb1, i1, bb3, i3) and
|
||||
variableRead(bb3, i3, _, false) and
|
||||
adjacentDefRead(def, bb3, i3, bb2, i2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `adjacentDefRead`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
|
||||
adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
|
||||
variableRead(bb2, i2, _, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the node at index `i` in `bb` is a last reference to SSA definition
|
||||
* `def`. The reference is last because it can reach another write `next`,
|
||||
* without passing through another read or write.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
exists(SourceVariable v |
|
||||
// Next reference to `v` inside `bb` is a write
|
||||
exists(int rnk, int j |
|
||||
rnk = ssaDefRank(def, v, bb, i, _) and
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
)
|
||||
or
|
||||
// Can reach a write using one or more steps
|
||||
lastSsaRef(def, v, bb, i) and
|
||||
exists(BasicBlock bb2 |
|
||||
varBlockReaches(def, bb, bb2) and
|
||||
1 = ssaDefRank(next, v, bb2, _, SsaDef())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `inp` is an immediately preceding definition of uncertain definition
|
||||
* `def`. Since `def` is uncertain, the value from the preceding definition might
|
||||
* still be valid.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate uncertainWriteDefinitionInput(UncertainWriteDefinition def, Definition inp) {
|
||||
lastRefRedef(inp, _, _, def)
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesUncertainRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
|
||||
variableRead(bb2, i2, _, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `lastRefRedef`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedefNoUncertainReads(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
lastRefRedef(def, bb, i, next) and
|
||||
not variableRead(bb, i, def.getSourceVariable(), false)
|
||||
or
|
||||
exists(BasicBlock bb0, int i0 |
|
||||
lastRefRedef(def, bb0, i0, next) and
|
||||
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the node at index `i` in `bb` is a last reference to SSA
|
||||
* definition `def`.
|
||||
*
|
||||
* That is, the node can reach the end of the enclosing callable, or another
|
||||
* SSA definition for the underlying source variable, without passing through
|
||||
* another read.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRef(Definition def, BasicBlock bb, int i) {
|
||||
// Can reach another definition
|
||||
lastRefRedef(def, bb, i, _)
|
||||
or
|
||||
exists(SourceVariable v | lastSsaRef(def, v, bb, i) |
|
||||
// Can reach exit directly
|
||||
bb instanceof ExitBasicBlock
|
||||
or
|
||||
// Can reach a block using one or more steps, where `def` is no longer live
|
||||
varBlockReachesExit(def, bb)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `lastRefRedef`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefNoUncertainReads(Definition def, BasicBlock bb, int i) {
|
||||
lastRef(def, bb, i) and
|
||||
not variableRead(bb, i, def.getSourceVariable(), false)
|
||||
or
|
||||
exists(BasicBlock bb0, int i0 |
|
||||
lastRef(def, bb0, i0) and
|
||||
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
|
||||
)
|
||||
}
|
||||
|
||||
/** A static single assignment (SSA) definition. */
|
||||
class Definition extends TDefinition {
|
||||
/** Gets the source variable underlying this SSA definition. */
|
||||
SourceVariable getSourceVariable() { this.definesAt(result, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if this SSA definition defines `v` at index `i` in basic block `bb`.
|
||||
* Phi nodes are considered to be at index `-1`, while normal variable writes
|
||||
* are at the index of the control flow node they wrap.
|
||||
*/
|
||||
final predicate definesAt(SourceVariable v, BasicBlock bb, int i) {
|
||||
this = TWriteDef(v, bb, i)
|
||||
or
|
||||
this = TPhiNode(v, bb) and i = -1
|
||||
}
|
||||
|
||||
/** Gets the basic block to which this SSA definition belongs. */
|
||||
final BasicBlock getBasicBlock() { this.definesAt(_, result, _) }
|
||||
|
||||
/** Gets a textual representation of this SSA definition. */
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
/** An SSA definition that corresponds to a write. */
|
||||
class WriteDefinition extends Definition, TWriteDef {
|
||||
private SourceVariable v;
|
||||
private BasicBlock bb;
|
||||
private int i;
|
||||
|
||||
WriteDefinition() { this = TWriteDef(v, bb, i) }
|
||||
|
||||
override string toString() { result = "WriteDef" }
|
||||
}
|
||||
|
||||
/** A phi node. */
|
||||
class PhiNode extends Definition, TPhiNode {
|
||||
override string toString() { result = "Phi" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An SSA definition that represents an uncertain update of the underlying
|
||||
* source variable.
|
||||
*/
|
||||
class UncertainWriteDefinition extends WriteDefinition {
|
||||
UncertainWriteDefinition() {
|
||||
exists(SourceVariable v, BasicBlock bb, int i |
|
||||
this.definesAt(v, bb, i) and
|
||||
variableWrite(bb, i, v, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides a set of consistency queries. */
|
||||
module Consistency {
|
||||
abstract class RelevantDefinition extends Definition {
|
||||
abstract predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
);
|
||||
}
|
||||
|
||||
query predicate nonUniqueDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
not exists(unique(Definition def0 | ssaDefReachesRead(v, def0, bb, i)))
|
||||
}
|
||||
|
||||
query predicate readWithoutDef(SourceVariable v, BasicBlock bb, int i) {
|
||||
variableRead(bb, i, v, _) and
|
||||
not ssaDefReachesRead(v, _, bb, i)
|
||||
}
|
||||
|
||||
query predicate deadDef(RelevantDefinition def, SourceVariable v) {
|
||||
v = def.getSourceVariable() and
|
||||
not ssaDefReachesRead(_, def, _, _) and
|
||||
not phiHasInputFromBlock(_, def, _) and
|
||||
not uncertainWriteDefinitionInput(_, def)
|
||||
}
|
||||
|
||||
query predicate notDominatedByDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef) |
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
(bb != bbDef or i < iDef)
|
||||
or
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
not ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/** Provides the C# specific parameters for `SsaImplCommon.qll`. */
|
||||
|
||||
private import csharp
|
||||
private import AssignableDefinitions
|
||||
private import semmle.code.csharp.controlflow.internal.PreBasicBlocks as PreBasicBlocks
|
||||
private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl
|
||||
|
||||
class BasicBlock = PreBasicBlocks::PreBasicBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock extends BasicBlock {
|
||||
ExitBasicBlock() { scopeLast(_, this.getLastElement(), _) }
|
||||
}
|
||||
|
||||
/** Holds if `a` is assigned in non-constructor callable `c`. */
|
||||
pragma[nomagic]
|
||||
private predicate assignableDefinition(Assignable a, Callable c) {
|
||||
exists(AssignableDefinition def | def.getTarget() = a |
|
||||
c = def.getEnclosingCallable() and
|
||||
not c instanceof Constructor
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `a` is accessed in callable `c`. */
|
||||
pragma[nomagic]
|
||||
private predicate assignableAccess(Assignable a, Callable c) {
|
||||
exists(AssignableAccess aa | aa.getTarget() = a | c = aa.getEnclosingCallable())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate assignableNoCapturing(Assignable a, Callable c) {
|
||||
assignableAccess(a, c) and
|
||||
/*
|
||||
* The code below is equivalent to
|
||||
* ```ql
|
||||
* not exists(Callable other | assignableDefinition(a, other) | other != c)
|
||||
* ```
|
||||
* but it avoids a Cartesian product in the compiler generated antijoin
|
||||
* predicate.
|
||||
*/
|
||||
|
||||
(
|
||||
not assignableDefinition(a, _)
|
||||
or
|
||||
c = unique(Callable c0 | assignableDefinition(a, c0) | c0)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate assignableNoComplexQualifiers(Assignable a) {
|
||||
forall(QualifiableExpr qe | qe.(AssignableAccess).getTarget() = a | qe.targetIsThisInstance())
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple assignable. Either a local scope variable or a field/property
|
||||
* that behaves like a local scope variable.
|
||||
*/
|
||||
class SourceVariable extends Assignable {
|
||||
private Callable c;
|
||||
|
||||
SourceVariable() {
|
||||
(
|
||||
this instanceof LocalScopeVariable
|
||||
or
|
||||
this = any(Field f | not f.isVolatile())
|
||||
or
|
||||
this = any(TrivialProperty tp | not tp.isOverridableOrImplementable())
|
||||
) and
|
||||
assignableNoCapturing(this, c) and
|
||||
assignableNoComplexQualifiers(this)
|
||||
}
|
||||
|
||||
/** Gets a callable in which this simple assignable can be analyzed. */
|
||||
Callable getACallable() { result = c }
|
||||
}
|
||||
|
||||
predicate definitionAt(AssignableDefinition def, BasicBlock bb, int i, SourceVariable v) {
|
||||
bb.getElement(i) = def.getExpr() and
|
||||
v = def.getTarget() and
|
||||
// In cases like `(x, x) = (0, 1)`, we discard the first (dead) definition of `x`
|
||||
not exists(TupleAssignmentDefinition first, TupleAssignmentDefinition second | first = def |
|
||||
second.getAssignment() = first.getAssignment() and
|
||||
second.getEvaluationOrder() > first.getEvaluationOrder() and
|
||||
second.getTarget() = v
|
||||
)
|
||||
or
|
||||
def.(ImplicitParameterDefinition).getParameter() = v and
|
||||
exists(Callable c | v = c.getAParameter() |
|
||||
scopeFirst(c, bb) and
|
||||
i = -1
|
||||
)
|
||||
}
|
||||
|
||||
predicate implicitEntryDef(Callable c, BasicBlock bb, SourceVariable v) {
|
||||
not v instanceof LocalScopeVariable and
|
||||
c = v.getACallable() and
|
||||
scopeFirst(c, bb)
|
||||
}
|
||||
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(AssignableDefinition def |
|
||||
definitionAt(def, bb, i, v) and
|
||||
if def.getTargetAccess().isRefArgument() then certain = false else certain = true
|
||||
)
|
||||
or
|
||||
exists(Callable c |
|
||||
implicitEntryDef(c, bb, v) and
|
||||
i = -1 and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(AssignableRead read |
|
||||
read = bb.getElement(i) and
|
||||
read.getTarget() = v and
|
||||
certain = true
|
||||
)
|
||||
or
|
||||
v =
|
||||
any(LocalScopeVariable lsv |
|
||||
lsv.getCallable() = bb.(ExitBasicBlock).getEnclosingCallable() and
|
||||
i = bb.length() and
|
||||
(lsv.isRef() or v.(Parameter).isOut()) and
|
||||
certain = false
|
||||
)
|
||||
}
|
||||
@@ -12,8 +12,6 @@
|
||||
* `namespace; type; subtypes; name; signature; ext; input; output; kind; provenance`
|
||||
* - Negative Summaries:
|
||||
* `namespace; type; name; signature; provenance`
|
||||
* A negative summary is used to indicate that there is no flow via a callable.
|
||||
*
|
||||
* The interpretation of a row is similar to API-graphs with a left-to-right
|
||||
* reading.
|
||||
* 1. The `namespace` column selects a namespace.
|
||||
|
||||
@@ -4,74 +4,19 @@ import csharp
|
||||
* Provides a simple SSA implementation for local scope variables.
|
||||
*/
|
||||
module BaseSsa {
|
||||
private import AssignableDefinitions
|
||||
private import semmle.code.csharp.dataflow.internal.SsaImplCommon as SsaImplCommon
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is assignable definition `def`,
|
||||
* targeting local scope variable `v`.
|
||||
*/
|
||||
private predicate definitionAt(
|
||||
AssignableDefinition def, ControlFlow::BasicBlock bb, int i, SsaInput::SourceVariable v
|
||||
) {
|
||||
bb.getNode(i) = def.getAControlFlowNode() and
|
||||
v = def.getTarget() and
|
||||
// In cases like `(x, x) = (0, 1)`, we discard the first (dead) definition of `x`
|
||||
not exists(TupleAssignmentDefinition first, TupleAssignmentDefinition second | first = def |
|
||||
second.getAssignment() = first.getAssignment() and
|
||||
second.getEvaluationOrder() > first.getEvaluationOrder() and
|
||||
second.getTarget() = v
|
||||
)
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
class BasicBlock = ControlFlow::BasicBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) {
|
||||
result = bb.getImmediateDominator()
|
||||
}
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock = ControlFlow::BasicBlocks::ExitBlock;
|
||||
|
||||
pragma[noinline]
|
||||
private Callable getAnAssigningCallable(LocalScopeVariable v) {
|
||||
result = any(AssignableDefinition def | def.getTarget() = v).getEnclosingCallable()
|
||||
}
|
||||
|
||||
class SourceVariable extends LocalScopeVariable {
|
||||
SourceVariable() { not getAnAssigningCallable(this) != getAnAssigningCallable(this) }
|
||||
}
|
||||
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(AssignableDefinition def |
|
||||
definitionAt(def, bb, i, v) and
|
||||
if def.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(AssignableRead read |
|
||||
read.getAControlFlowNode() = bb.getNode(i) and
|
||||
read.getTarget() = v and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
import basessa.SsaImplSpecific
|
||||
private import basessa.SsaImplCommon as SsaImpl
|
||||
|
||||
class Definition extends SsaImpl::Definition {
|
||||
final AssignableRead getARead() {
|
||||
exists(ControlFlow::BasicBlock bb, int i |
|
||||
exists(BasicBlock bb, int i |
|
||||
SsaImpl::ssaDefReachesRead(_, this, bb, i) and
|
||||
result.getAControlFlowNode() = bb.getNode(i)
|
||||
)
|
||||
}
|
||||
|
||||
final AssignableDefinition getDefinition() {
|
||||
exists(ControlFlow::BasicBlock bb, int i, SsaInput::SourceVariable v |
|
||||
exists(BasicBlock bb, int i, SourceVariable v |
|
||||
this.definesAt(v, bb, i) and
|
||||
definitionAt(result, bb, i, v)
|
||||
)
|
||||
|
||||
@@ -3,53 +3,7 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
private import SsaImplCommon as SsaImplCommon
|
||||
private import AssignableDefinitions
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
class BasicBlock = ControlFlow::BasicBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock = ControlFlow::BasicBlocks::ExitBlock;
|
||||
|
||||
class SourceVariable = Ssa::SourceVariable;
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a (potential) write to source
|
||||
* variable `v`. The Boolean `certain` indicates whether the write is certain.
|
||||
*
|
||||
* This includes implicit writes via calls.
|
||||
*/
|
||||
predicate variableWrite(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) {
|
||||
variableWriteDirect(bb, i, v, certain)
|
||||
or
|
||||
variableWriteQualifier(bb, i, v, certain)
|
||||
or
|
||||
updatesNamedFieldOrProp(bb, i, _, v, _) and
|
||||
certain = false
|
||||
or
|
||||
updatesCapturedVariable(bb, i, _, v, _, _) and
|
||||
certain = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th of basic block `bb` reads source variable `v`.
|
||||
*
|
||||
* This includes implicit reads via calls.
|
||||
*/
|
||||
predicate variableRead(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) {
|
||||
variableReadActual(bb, i, v) and
|
||||
certain = true
|
||||
or
|
||||
variableReadPseudo(bb, i, v) and
|
||||
certain = false
|
||||
}
|
||||
}
|
||||
|
||||
import SsaImplCommon::Make<SsaInput>
|
||||
import SsaImplCommon
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` reads source variable `v`.
|
||||
@@ -851,6 +805,24 @@ private module CapturedVariableImpl {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a (potential) write to source
|
||||
* variable `v`. The Boolean `certain` indicates whether the write is certain.
|
||||
*
|
||||
* This includes implicit writes via calls.
|
||||
*/
|
||||
predicate variableWrite(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) {
|
||||
variableWriteDirect(bb, i, v, certain)
|
||||
or
|
||||
variableWriteQualifier(bb, i, v, certain)
|
||||
or
|
||||
updatesNamedFieldOrProp(bb, i, _, v, _) and
|
||||
certain = false
|
||||
or
|
||||
updatesCapturedVariable(bb, i, _, v, _, _) and
|
||||
certain = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Liveness analysis to restrict the size of the SSA representation for
|
||||
* captured variables.
|
||||
@@ -1067,6 +1039,19 @@ private predicate variableReadPseudo(ControlFlow::BasicBlock bb, int i, Ssa::Sou
|
||||
capturedReadIn(bb, i, v, _, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th of basic block `bb` reads source variable `v`.
|
||||
*
|
||||
* This includes implicit reads via calls.
|
||||
*/
|
||||
predicate variableRead(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v, boolean certain) {
|
||||
variableReadActual(bb, i, v) and
|
||||
certain = true
|
||||
or
|
||||
variableReadPseudo(bb, i, v) and
|
||||
certain = false
|
||||
}
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
@@ -1166,7 +1151,7 @@ private module Cached {
|
||||
predicate variableWriteQualifier(
|
||||
ControlFlow::BasicBlock bb, int i, QualifiedFieldOrPropSourceVariable v, boolean certain
|
||||
) {
|
||||
SsaInput::variableWrite(bb, i, v.getQualifier(), certain) and
|
||||
variableWrite(bb, i, v.getQualifier(), certain) and
|
||||
// Eliminate corner case where a call definition can overlap with a
|
||||
// qualifier definition: if method `M` updates field `F`, then a call
|
||||
// to `M` is both an update of `x.M` and `x.M.M`, so the former call
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
||||
/** Provides the C# specific parameters for `SsaImplCommon.qll`. */
|
||||
|
||||
private import csharp
|
||||
private import AssignableDefinitions
|
||||
private import SsaImpl as SsaImpl
|
||||
|
||||
class BasicBlock = ControlFlow::BasicBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock = ControlFlow::BasicBlocks::ExitBlock;
|
||||
|
||||
class SourceVariable = Ssa::SourceVariable;
|
||||
|
||||
predicate variableWrite = SsaImpl::variableWrite/4;
|
||||
|
||||
predicate variableRead = SsaImpl::variableRead/4;
|
||||
@@ -0,0 +1,795 @@
|
||||
/**
|
||||
* Provides a language-independent implementation of static single assignment
|
||||
* (SSA) form.
|
||||
*/
|
||||
|
||||
private import SsaImplSpecific
|
||||
|
||||
private BasicBlock getABasicBlockPredecessor(BasicBlock bb) { getABasicBlockSuccessor(result) = bb }
|
||||
|
||||
/**
|
||||
* Liveness analysis (based on source variables) to restrict the size of the
|
||||
* SSA representation.
|
||||
*/
|
||||
private module Liveness {
|
||||
/**
|
||||
* A classification of variable references into reads (of a given kind) and
|
||||
* (certain or uncertain) writes.
|
||||
*/
|
||||
private newtype TRefKind =
|
||||
Read(boolean certain) { certain in [false, true] } or
|
||||
Write(boolean certain) { certain in [false, true] }
|
||||
|
||||
private class RefKind extends TRefKind {
|
||||
string toString() {
|
||||
exists(boolean certain | this = Read(certain) and result = "read (" + certain + ")")
|
||||
or
|
||||
exists(boolean certain | this = Write(certain) and result = "write (" + certain + ")")
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
this = Read(_) and
|
||||
result = 0
|
||||
or
|
||||
this = Write(_) and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v` of kind `k`.
|
||||
*/
|
||||
predicate ref(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
exists(boolean certain | variableRead(bb, i, v, certain) | k = Read(certain))
|
||||
or
|
||||
exists(boolean certain | variableWrite(bb, i, v, certain) | k = Write(certain))
|
||||
}
|
||||
|
||||
private newtype OrderedRefIndex =
|
||||
MkOrderedRefIndex(int i, int tag) {
|
||||
exists(RefKind rk | ref(_, i, _, rk) | tag = rk.getOrder())
|
||||
}
|
||||
|
||||
private OrderedRefIndex refOrd(BasicBlock bb, int i, SourceVariable v, RefKind k, int ord) {
|
||||
ref(bb, i, v, k) and
|
||||
result = MkOrderedRefIndex(i, ord) and
|
||||
ord = k.getOrder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the reference to `v` at the `i`th node of
|
||||
* basic block `bb`, which has the given reference kind `k`.
|
||||
*
|
||||
* Reads are considered before writes when they happen at the same index.
|
||||
*/
|
||||
private int refRank(BasicBlock bb, int i, SourceVariable v, RefKind k) {
|
||||
refOrd(bb, i, v, k, _) =
|
||||
rank[result](int j, int ord, OrderedRefIndex res |
|
||||
res = refOrd(bb, j, v, _, ord)
|
||||
|
|
||||
res order by j, ord
|
||||
)
|
||||
}
|
||||
|
||||
private int maxRefRank(BasicBlock bb, SourceVariable v) {
|
||||
result = refRank(bb, _, v, _) and
|
||||
not result + 1 = refRank(bb, _, v, _)
|
||||
}
|
||||
|
||||
predicate lastRefIsRead(BasicBlock bb, SourceVariable v) {
|
||||
maxRefRank(bb, v) = refRank(bb, _, v, Read(_))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the first reference to `v` inside basic block `bb`
|
||||
* that is either a read or a certain write.
|
||||
*/
|
||||
private int firstReadOrCertainWrite(BasicBlock bb, SourceVariable v) {
|
||||
result =
|
||||
min(int r, RefKind k |
|
||||
r = refRank(bb, _, v, k) and
|
||||
k != Write(false)
|
||||
|
|
||||
r
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if source variable `v` is live at the beginning of basic block `bb`.
|
||||
*/
|
||||
predicate liveAtEntry(BasicBlock bb, SourceVariable v) {
|
||||
// The first read or certain write to `v` inside `bb` is a read
|
||||
refRank(bb, _, v, Read(_)) = firstReadOrCertainWrite(bb, v)
|
||||
or
|
||||
// There is no certain write to `v` inside `bb`, but `v` is live at entry
|
||||
// to a successor basic block of `bb`
|
||||
not exists(firstReadOrCertainWrite(bb, v)) and
|
||||
liveAtExit(bb, v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if source variable `v` is live at the end of basic block `bb`.
|
||||
*/
|
||||
predicate liveAtExit(BasicBlock bb, SourceVariable v) {
|
||||
liveAtEntry(getABasicBlockSuccessor(bb), v)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live in basic block `bb` at index `i`.
|
||||
* The rank of `i` is `rnk` as defined by `refRank()`.
|
||||
*/
|
||||
private predicate liveAtRank(BasicBlock bb, int i, SourceVariable v, int rnk) {
|
||||
exists(RefKind kind | rnk = refRank(bb, i, v, kind) |
|
||||
rnk = maxRefRank(bb, v) and
|
||||
liveAtExit(bb, v)
|
||||
or
|
||||
ref(bb, i, v, kind) and
|
||||
kind = Read(_)
|
||||
or
|
||||
exists(RefKind nextKind |
|
||||
liveAtRank(bb, _, v, rnk + 1) and
|
||||
rnk + 1 = refRank(bb, _, v, nextKind) and
|
||||
nextKind != Write(true)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if variable `v` is live after the (certain or uncertain) write at
|
||||
* index `i` inside basic block `bb`.
|
||||
*/
|
||||
predicate liveAfterWrite(BasicBlock bb, int i, SourceVariable v) {
|
||||
exists(int rnk | rnk = refRank(bb, i, v, Write(_)) | liveAtRank(bb, i, v, rnk))
|
||||
}
|
||||
}
|
||||
|
||||
private import Liveness
|
||||
|
||||
/**
|
||||
* Holds if `df` is in the dominance frontier of `bb`.
|
||||
*
|
||||
* This is equivalent to:
|
||||
*
|
||||
* ```ql
|
||||
* bb = getImmediateBasicBlockDominator*(getABasicBlockPredecessor(df)) and
|
||||
* not bb = getImmediateBasicBlockDominator+(df)
|
||||
* ```
|
||||
*/
|
||||
private predicate inDominanceFrontier(BasicBlock bb, BasicBlock df) {
|
||||
bb = getABasicBlockPredecessor(df) and not bb = getImmediateBasicBlockDominator(df)
|
||||
or
|
||||
exists(BasicBlock prev | inDominanceFrontier(prev, df) |
|
||||
bb = getImmediateBasicBlockDominator(prev) and
|
||||
not bb = getImmediateBasicBlockDominator(df)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb` is in the dominance frontier of a block containing a
|
||||
* definition of `v`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private predicate inDefDominanceFrontier(BasicBlock bb, SourceVariable v) {
|
||||
exists(BasicBlock defbb, Definition def |
|
||||
def.definesAt(v, defbb, _) and
|
||||
inDominanceFrontier(defbb, bb)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TDefinition =
|
||||
TWriteDef(SourceVariable v, BasicBlock bb, int i) {
|
||||
variableWrite(bb, i, v, _) and
|
||||
liveAfterWrite(bb, i, v)
|
||||
} or
|
||||
TPhiNode(SourceVariable v, BasicBlock bb) {
|
||||
inDefDominanceFrontier(bb, v) and
|
||||
liveAtEntry(bb, v)
|
||||
}
|
||||
|
||||
private module SsaDefReaches {
|
||||
newtype TSsaRefKind =
|
||||
SsaActualRead() or
|
||||
SsaPhiRead() or
|
||||
SsaDef()
|
||||
|
||||
class SsaRead = SsaActualRead or SsaPhiRead;
|
||||
|
||||
/**
|
||||
* A classification of SSA variable references into reads and definitions.
|
||||
*/
|
||||
class SsaRefKind extends TSsaRefKind {
|
||||
string toString() {
|
||||
this = SsaActualRead() and
|
||||
result = "SsaActualRead"
|
||||
or
|
||||
this = SsaPhiRead() and
|
||||
result = "SsaPhiRead"
|
||||
or
|
||||
this = SsaDef() and
|
||||
result = "SsaDef"
|
||||
}
|
||||
|
||||
int getOrder() {
|
||||
this instanceof SsaRead and
|
||||
result = 0
|
||||
or
|
||||
this = SsaDef() and
|
||||
result = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `bb` is in the dominance frontier of a block containing a
|
||||
* read of `v`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate inReadDominanceFrontier(BasicBlock bb, SourceVariable v) {
|
||||
exists(BasicBlock readbb | inDominanceFrontier(readbb, bb) |
|
||||
lastRefIsRead(readbb, v)
|
||||
or
|
||||
phiRead(readbb, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a phi-read node should be inserted for variable `v` at the beginning
|
||||
* of basic block `bb`.
|
||||
*
|
||||
* Phi-read nodes are like normal phi nodes, but they are inserted based on reads
|
||||
* instead of writes, and only if the dominance-frontier block does not already
|
||||
* contain a reference (read or write) to `v`. Unlike normal phi nodes, this is
|
||||
* an internal implementation detail that is not exposed.
|
||||
*
|
||||
* The motivation for adding phi-reads is to improve performance of the use-use
|
||||
* calculation in cases where there is a large number of reads that can reach the
|
||||
* same join-point, and from there reach a large number of basic blocks. Example:
|
||||
*
|
||||
* ```cs
|
||||
* if (a)
|
||||
* use(x);
|
||||
* else if (b)
|
||||
* use(x);
|
||||
* else if (c)
|
||||
* use(x);
|
||||
* else if (d)
|
||||
* use(x);
|
||||
* // many more ifs ...
|
||||
*
|
||||
* // phi-read for `x` inserted here
|
||||
*
|
||||
* // program not mentioning `x`, with large basic block graph
|
||||
*
|
||||
* use(x);
|
||||
* ```
|
||||
*
|
||||
* Without phi-reads, the analysis has to replicate reachability for each of
|
||||
* the guarded uses of `x`. However, with phi-reads, the analysis will limit
|
||||
* each conditional use of `x` to reach the basic block containing the phi-read
|
||||
* node for `x`, and only that basic block will have to compute reachability
|
||||
* through the remainder of the large program.
|
||||
*
|
||||
* Like normal reads, each phi-read node `phi-read` can be reached from exactly
|
||||
* one SSA definition (without passing through another definition): Assume, for
|
||||
* the sake of contradiction, that there are two reaching definitions `def1` and
|
||||
* `def2`. Now, if both `def1` and `def2` dominate `phi-read`, then the nearest
|
||||
* dominating definition will prevent the other from reaching `phi-read`. So, at
|
||||
* least one of `def1` and `def2` cannot dominate `phi-read`; assume it is `def1`.
|
||||
* Then `def1` must go through one of its dominance-frontier blocks in order to
|
||||
* reach `phi-read`. However, such a block will always start with a (normal) phi
|
||||
* node, which contradicts reachability.
|
||||
*
|
||||
* Also, like normal reads, the unique SSA definition `def` that reaches `phi-read`,
|
||||
* will dominate `phi-read`. Assuming it doesn't means that the path from `def`
|
||||
* to `phi-read` goes through a dominance-frontier block, and hence a phi node,
|
||||
* which contradicts reachability.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate phiRead(BasicBlock bb, SourceVariable v) {
|
||||
inReadDominanceFrontier(bb, v) and
|
||||
liveAtEntry(bb, v) and
|
||||
// only if there are no other references to `v` inside `bb`
|
||||
not ref(bb, _, v, _) and
|
||||
not exists(Definition def | def.definesAt(v, bb, _))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is a reference to `v`,
|
||||
* either a read (when `k` is `SsaRead()`) or an SSA definition (when `k`
|
||||
* is `SsaDef()`).
|
||||
*
|
||||
* Unlike `Liveness::ref`, this includes `phi` nodes.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaRef(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
|
||||
variableRead(bb, i, v, _) and
|
||||
k = SsaActualRead()
|
||||
or
|
||||
phiRead(bb, v) and
|
||||
i = -1 and
|
||||
k = SsaPhiRead()
|
||||
or
|
||||
any(Definition def).definesAt(v, bb, i) and
|
||||
k = SsaDef()
|
||||
}
|
||||
|
||||
private newtype OrderedSsaRefIndex =
|
||||
MkOrderedSsaRefIndex(int i, SsaRefKind k) { ssaRef(_, i, _, k) }
|
||||
|
||||
private OrderedSsaRefIndex ssaRefOrd(BasicBlock bb, int i, SourceVariable v, SsaRefKind k, int ord) {
|
||||
ssaRef(bb, i, v, k) and
|
||||
result = MkOrderedSsaRefIndex(i, k) and
|
||||
ord = k.getOrder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the (1-based) rank of the reference to `v` at the `i`th node of basic
|
||||
* block `bb`, which has the given reference kind `k`.
|
||||
*
|
||||
* For example, if `bb` is a basic block with a phi node for `v` (considered
|
||||
* to be at index -1), reads `v` at node 2, and defines it at node 5, we have:
|
||||
*
|
||||
* ```ql
|
||||
* ssaRefRank(bb, -1, v, SsaDef()) = 1 // phi node
|
||||
* ssaRefRank(bb, 2, v, Read()) = 2 // read at node 2
|
||||
* ssaRefRank(bb, 5, v, SsaDef()) = 3 // definition at node 5
|
||||
* ```
|
||||
*
|
||||
* Reads are considered before writes when they happen at the same index.
|
||||
*/
|
||||
int ssaRefRank(BasicBlock bb, int i, SourceVariable v, SsaRefKind k) {
|
||||
ssaRefOrd(bb, i, v, k, _) =
|
||||
rank[result](int j, int ord, OrderedSsaRefIndex res |
|
||||
res = ssaRefOrd(bb, j, v, _, ord)
|
||||
|
|
||||
res order by j, ord
|
||||
)
|
||||
}
|
||||
|
||||
int maxSsaRefRank(BasicBlock bb, SourceVariable v) {
|
||||
result = ssaRefRank(bb, _, v, _) and
|
||||
not result + 1 = ssaRefRank(bb, _, v, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition `def` reaches rank index `rnk` in its own
|
||||
* basic block `bb`.
|
||||
*/
|
||||
predicate ssaDefReachesRank(BasicBlock bb, Definition def, int rnk, SourceVariable v) {
|
||||
exists(int i |
|
||||
rnk = ssaRefRank(bb, i, v, SsaDef()) and
|
||||
def.definesAt(v, bb, i)
|
||||
)
|
||||
or
|
||||
ssaDefReachesRank(bb, def, rnk - 1, v) and
|
||||
rnk = ssaRefRank(bb, _, v, any(SsaRead k))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the SSA definition of `v` at `def` reaches index `i` in the same
|
||||
* basic block `bb`, without crossing another SSA definition of `v`.
|
||||
*/
|
||||
predicate ssaDefReachesReadWithinBlock(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
exists(int rnk |
|
||||
ssaDefReachesRank(bb, def, rnk, v) and
|
||||
rnk = ssaRefRank(bb, i, v, any(SsaRead k))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as `ssaRefRank()`, but restricted to a particular SSA definition `def`.
|
||||
*/
|
||||
int ssaDefRank(Definition def, SourceVariable v, BasicBlock bb, int i, SsaRefKind k) {
|
||||
v = def.getSourceVariable() and
|
||||
result = ssaRefRank(bb, i, v, k) and
|
||||
(
|
||||
ssaDefReachesRead(_, def, bb, i)
|
||||
or
|
||||
def.definesAt(_, bb, i)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the reference to `def` at index `i` in basic block `bb` is the
|
||||
* last reference to `v` inside `bb`.
|
||||
*/
|
||||
pragma[noinline]
|
||||
predicate lastSsaRef(Definition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefRank(def, v, bb, i, _) = maxSsaRefRank(bb, v)
|
||||
}
|
||||
|
||||
predicate defOccursInBlock(Definition def, BasicBlock bb, SourceVariable v, SsaRefKind k) {
|
||||
exists(ssaDefRank(def, v, bb, _, k))
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate ssaDefReachesThroughBlock(Definition def, BasicBlock bb) {
|
||||
ssaDefReachesEndOfBlock(bb, def, _) and
|
||||
not defOccursInBlock(_, bb, def.getSourceVariable(), _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
|
||||
* `bb2` is a transitive successor of `bb1`, `def` is live at the end of _some_
|
||||
* predecessor of `bb2`, and the underlying variable for `def` is neither read
|
||||
* nor written in any block on the path between `bb1` and `bb2`.
|
||||
*
|
||||
* Phi reads are considered as normal reads for this predicate.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate varBlockReachesInclPhiRead(Definition def, BasicBlock bb1, BasicBlock bb2) {
|
||||
defOccursInBlock(def, bb1, _, _) and
|
||||
bb2 = getABasicBlockSuccessor(bb1)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReachesInclPhiRead(def, bb1, mid) and
|
||||
ssaDefReachesThroughBlock(def, mid) and
|
||||
bb2 = getABasicBlockSuccessor(mid)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate phiReadStep(Definition def, SourceVariable v, BasicBlock bb1, BasicBlock bb2) {
|
||||
varBlockReachesInclPhiRead(def, bb1, bb2) and
|
||||
defOccursInBlock(def, bb2, v, SsaPhiRead())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate varBlockReachesExclPhiRead(Definition def, BasicBlock bb1, BasicBlock bb2) {
|
||||
varBlockReachesInclPhiRead(pragma[only_bind_into](def), bb1, pragma[only_bind_into](bb2)) and
|
||||
ssaRef(bb2, _, def.getSourceVariable(), [SsaActualRead().(TSsaRefKind), SsaDef()])
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReachesExclPhiRead(def, mid, bb2) and
|
||||
phiReadStep(def, _, bb1, mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb1` (either a read or a write),
|
||||
* the underlying variable `v` of `def` is accessed in basic block `bb2`
|
||||
* (either a read or a write), `bb2` is a transitive successor of `bb1`, and
|
||||
* `v` is neither read nor written in any block on the path between `bb1`
|
||||
* and `bb2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate varBlockReaches(Definition def, BasicBlock bb1, BasicBlock bb2) {
|
||||
varBlockReachesExclPhiRead(def, bb1, bb2) and
|
||||
not defOccursInBlock(def, bb1, _, SsaPhiRead())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate defAdjacentRead(Definition def, BasicBlock bb1, BasicBlock bb2, int i2) {
|
||||
varBlockReaches(def, bb1, bb2) and
|
||||
ssaRefRank(bb2, i2, def.getSourceVariable(), SsaActualRead()) = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `def` is accessed in basic block `bb` (either a read or a write),
|
||||
* `bb1` can reach a transitive successor `bb2` where `def` is no longer live,
|
||||
* and `v` is neither read nor written in any block on the path between `bb`
|
||||
* and `bb2`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate varBlockReachesExit(Definition def, BasicBlock bb) {
|
||||
exists(BasicBlock bb2 | varBlockReachesInclPhiRead(def, bb, bb2) |
|
||||
not defOccursInBlock(def, bb2, _, _) and
|
||||
not ssaDefReachesEndOfBlock(bb2, def, _)
|
||||
)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
varBlockReachesExit(def, mid) and
|
||||
phiReadStep(def, _, bb, mid)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate phiReadExposedForTesting = phiRead/2;
|
||||
|
||||
private import SsaDefReaches
|
||||
|
||||
pragma[nomagic]
|
||||
predicate liveThrough(BasicBlock bb, SourceVariable v) {
|
||||
liveAtExit(bb, v) and
|
||||
not ssaRef(bb, _, v, SsaDef())
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the SSA definition of `v` at `def` reaches the end of basic
|
||||
* block `bb`, at which point it is still live, without crossing another
|
||||
* SSA definition of `v`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesEndOfBlock(BasicBlock bb, Definition def, SourceVariable v) {
|
||||
exists(int last |
|
||||
last = maxSsaRefRank(pragma[only_bind_into](bb), pragma[only_bind_into](v)) and
|
||||
ssaDefReachesRank(bb, def, last, v) and
|
||||
liveAtExit(bb, v)
|
||||
)
|
||||
or
|
||||
// The construction of SSA form ensures that each read of a variable is
|
||||
// dominated by its definition. An SSA definition therefore reaches a
|
||||
// control flow node if it is the _closest_ SSA definition that dominates
|
||||
// the node. If two definitions dominate a node then one must dominate the
|
||||
// other, so therefore the definition of _closest_ is given by the dominator
|
||||
// tree. Thus, reaching definitions can be calculated in terms of dominance.
|
||||
ssaDefReachesEndOfBlock(getImmediateBasicBlockDominator(bb), def, pragma[only_bind_into](v)) and
|
||||
liveThrough(bb, pragma[only_bind_into](v))
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `inp` is an input to the phi node `phi` along the edge originating in `bb`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate phiHasInputFromBlock(PhiNode phi, Definition inp, BasicBlock bb) {
|
||||
exists(SourceVariable v, BasicBlock bbDef |
|
||||
phi.definesAt(v, bbDef, _) and
|
||||
getABasicBlockPredecessor(bbDef) = bb and
|
||||
ssaDefReachesEndOfBlock(bb, inp, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the SSA definition of `v` at `def` reaches a read at index `i` in
|
||||
* basic block `bb`, without crossing another SSA definition of `v`. The read
|
||||
* is of kind `rk`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate ssaDefReachesRead(SourceVariable v, Definition def, BasicBlock bb, int i) {
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i)
|
||||
or
|
||||
ssaRef(bb, i, v, any(SsaRead k)) and
|
||||
ssaDefReachesEndOfBlock(getABasicBlockPredecessor(bb), def, v) and
|
||||
not ssaDefReachesReadWithinBlock(v, _, bb, i)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read
|
||||
* or a write), `def` is read at index `i2` in basic block `bb2`, and there is a
|
||||
* path between them without any read of `def`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate adjacentDefRead(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
|
||||
exists(int rnk |
|
||||
rnk = ssaDefRank(def, _, bb1, i1, _) and
|
||||
rnk + 1 = ssaDefRank(def, _, bb1, i2, SsaActualRead()) and
|
||||
variableRead(bb1, i2, _, _) and
|
||||
bb2 = bb1
|
||||
)
|
||||
or
|
||||
lastSsaRef(def, _, bb1, i1) and
|
||||
defAdjacentRead(def, bb1, bb2, i2)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate adjacentDefRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2, SourceVariable v
|
||||
) {
|
||||
adjacentDefRead(def, bb1, i1, bb2, i2) and
|
||||
v = def.getSourceVariable()
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
exists(SourceVariable v | adjacentDefRead(def, bb1, i1, bb2, i2, v) |
|
||||
ssaRef(bb1, i1, v, SsaDef())
|
||||
or
|
||||
variableRead(bb1, i1, v, true)
|
||||
)
|
||||
or
|
||||
exists(BasicBlock bb3, int i3 |
|
||||
adjacentDefReachesRead(def, bb1, i1, bb3, i3) and
|
||||
variableRead(bb3, i3, _, false) and
|
||||
adjacentDefRead(def, bb3, i3, bb2, i2)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `adjacentDefRead`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate adjacentDefNoUncertainReads(Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2) {
|
||||
adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
|
||||
variableRead(bb2, i2, _, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the node at index `i` in `bb` is a last reference to SSA definition
|
||||
* `def`. The reference is last because it can reach another write `next`,
|
||||
* without passing through another read or write.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedef(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
exists(SourceVariable v |
|
||||
// Next reference to `v` inside `bb` is a write
|
||||
exists(int rnk, int j |
|
||||
rnk = ssaDefRank(def, v, bb, i, _) and
|
||||
next.definesAt(v, bb, j) and
|
||||
rnk + 1 = ssaRefRank(bb, j, v, SsaDef())
|
||||
)
|
||||
or
|
||||
// Can reach a write using one or more steps
|
||||
lastSsaRef(def, v, bb, i) and
|
||||
exists(BasicBlock bb2 |
|
||||
varBlockReaches(def, bb, bb2) and
|
||||
1 = ssaDefRank(next, v, bb2, _, SsaDef())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if `inp` is an immediately preceding definition of uncertain definition
|
||||
* `def`. Since `def` is uncertain, the value from the preceding definition might
|
||||
* still be valid.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate uncertainWriteDefinitionInput(UncertainWriteDefinition def, Definition inp) {
|
||||
lastRefRedef(inp, _, _, def)
|
||||
}
|
||||
|
||||
private predicate adjacentDefReachesUncertainRead(
|
||||
Definition def, BasicBlock bb1, int i1, BasicBlock bb2, int i2
|
||||
) {
|
||||
adjacentDefReachesRead(def, bb1, i1, bb2, i2) and
|
||||
variableRead(bb2, i2, _, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `lastRefRedef`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefRedefNoUncertainReads(Definition def, BasicBlock bb, int i, Definition next) {
|
||||
lastRefRedef(def, bb, i, next) and
|
||||
not variableRead(bb, i, def.getSourceVariable(), false)
|
||||
or
|
||||
exists(BasicBlock bb0, int i0 |
|
||||
lastRefRedef(def, bb0, i0, next) and
|
||||
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Holds if the node at index `i` in `bb` is a last reference to SSA
|
||||
* definition `def`.
|
||||
*
|
||||
* That is, the node can reach the end of the enclosing callable, or another
|
||||
* SSA definition for the underlying source variable, without passing through
|
||||
* another read.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRef(Definition def, BasicBlock bb, int i) {
|
||||
// Can reach another definition
|
||||
lastRefRedef(def, bb, i, _)
|
||||
or
|
||||
exists(SourceVariable v | lastSsaRef(def, v, bb, i) |
|
||||
// Can reach exit directly
|
||||
bb instanceof ExitBasicBlock
|
||||
or
|
||||
// Can reach a block using one or more steps, where `def` is no longer live
|
||||
varBlockReachesExit(def, bb)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* NB: If this predicate is exposed, it should be cached.
|
||||
*
|
||||
* Same as `lastRefRedef`, but ignores uncertain reads.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate lastRefNoUncertainReads(Definition def, BasicBlock bb, int i) {
|
||||
lastRef(def, bb, i) and
|
||||
not variableRead(bb, i, def.getSourceVariable(), false)
|
||||
or
|
||||
exists(BasicBlock bb0, int i0 |
|
||||
lastRef(def, bb0, i0) and
|
||||
adjacentDefReachesUncertainRead(def, bb, i, bb0, i0)
|
||||
)
|
||||
}
|
||||
|
||||
/** A static single assignment (SSA) definition. */
|
||||
class Definition extends TDefinition {
|
||||
/** Gets the source variable underlying this SSA definition. */
|
||||
SourceVariable getSourceVariable() { this.definesAt(result, _, _) }
|
||||
|
||||
/**
|
||||
* Holds if this SSA definition defines `v` at index `i` in basic block `bb`.
|
||||
* Phi nodes are considered to be at index `-1`, while normal variable writes
|
||||
* are at the index of the control flow node they wrap.
|
||||
*/
|
||||
final predicate definesAt(SourceVariable v, BasicBlock bb, int i) {
|
||||
this = TWriteDef(v, bb, i)
|
||||
or
|
||||
this = TPhiNode(v, bb) and i = -1
|
||||
}
|
||||
|
||||
/** Gets the basic block to which this SSA definition belongs. */
|
||||
final BasicBlock getBasicBlock() { this.definesAt(_, result, _) }
|
||||
|
||||
/** Gets a textual representation of this SSA definition. */
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
/** An SSA definition that corresponds to a write. */
|
||||
class WriteDefinition extends Definition, TWriteDef {
|
||||
private SourceVariable v;
|
||||
private BasicBlock bb;
|
||||
private int i;
|
||||
|
||||
WriteDefinition() { this = TWriteDef(v, bb, i) }
|
||||
|
||||
override string toString() { result = "WriteDef" }
|
||||
}
|
||||
|
||||
/** A phi node. */
|
||||
class PhiNode extends Definition, TPhiNode {
|
||||
override string toString() { result = "Phi" }
|
||||
}
|
||||
|
||||
/**
|
||||
* An SSA definition that represents an uncertain update of the underlying
|
||||
* source variable.
|
||||
*/
|
||||
class UncertainWriteDefinition extends WriteDefinition {
|
||||
UncertainWriteDefinition() {
|
||||
exists(SourceVariable v, BasicBlock bb, int i |
|
||||
this.definesAt(v, bb, i) and
|
||||
variableWrite(bb, i, v, false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides a set of consistency queries. */
|
||||
module Consistency {
|
||||
abstract class RelevantDefinition extends Definition {
|
||||
abstract predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
);
|
||||
}
|
||||
|
||||
query predicate nonUniqueDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
not exists(unique(Definition def0 | ssaDefReachesRead(v, def0, bb, i)))
|
||||
}
|
||||
|
||||
query predicate readWithoutDef(SourceVariable v, BasicBlock bb, int i) {
|
||||
variableRead(bb, i, v, _) and
|
||||
not ssaDefReachesRead(v, _, bb, i)
|
||||
}
|
||||
|
||||
query predicate deadDef(RelevantDefinition def, SourceVariable v) {
|
||||
v = def.getSourceVariable() and
|
||||
not ssaDefReachesRead(_, def, _, _) and
|
||||
not phiHasInputFromBlock(_, def, _) and
|
||||
not uncertainWriteDefinitionInput(_, def)
|
||||
}
|
||||
|
||||
query predicate notDominatedByDef(RelevantDefinition def, SourceVariable v, BasicBlock bb, int i) {
|
||||
exists(BasicBlock bbDef, int iDef | def.definesAt(v, bbDef, iDef) |
|
||||
ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
(bb != bbDef or i < iDef)
|
||||
or
|
||||
ssaDefReachesRead(v, def, bb, i) and
|
||||
not ssaDefReachesReadWithinBlock(v, def, bb, i) and
|
||||
not def.definesAt(v, getImmediateBasicBlockDominator*(bb), _)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/** Provides the C# specific parameters for `SsaImplCommon.qll`. */
|
||||
|
||||
private import csharp
|
||||
private import AssignableDefinitions
|
||||
|
||||
class BasicBlock = ControlFlow::BasicBlock;
|
||||
|
||||
BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result = bb.getImmediateDominator() }
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
class ExitBasicBlock = ControlFlow::BasicBlocks::ExitBlock;
|
||||
|
||||
pragma[noinline]
|
||||
private Callable getAnAssigningCallable(LocalScopeVariable v) {
|
||||
result = any(AssignableDefinition def | def.getTarget() = v).getEnclosingCallable()
|
||||
}
|
||||
|
||||
class SourceVariable extends LocalScopeVariable {
|
||||
SourceVariable() { not getAnAssigningCallable(this) != getAnAssigningCallable(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`th node of basic block `bb` is assignable definition `def`,
|
||||
* targeting local scope variable `v`.
|
||||
*/
|
||||
predicate definitionAt(AssignableDefinition def, BasicBlock bb, int i, SourceVariable v) {
|
||||
bb.getNode(i) = def.getAControlFlowNode() and
|
||||
v = def.getTarget() and
|
||||
// In cases like `(x, x) = (0, 1)`, we discard the first (dead) definition of `x`
|
||||
not exists(TupleAssignmentDefinition first, TupleAssignmentDefinition second | first = def |
|
||||
second.getAssignment() = first.getAssignment() and
|
||||
second.getEvaluationOrder() > first.getEvaluationOrder() and
|
||||
second.getTarget() = v
|
||||
)
|
||||
}
|
||||
|
||||
predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(AssignableDefinition def |
|
||||
definitionAt(def, bb, i, v) and
|
||||
if def.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
|
||||
predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(AssignableRead read |
|
||||
read.getAControlFlowNode() = bb.getNode(i) and
|
||||
read.getTarget() = v and
|
||||
certain = true
|
||||
)
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
/** Provides a module for importing negative models. */
|
||||
|
||||
/**
|
||||
* A module importing all generated negative Models as Data models.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
|
||||
private module GeneratedFrameworks {
|
||||
private import generated.dotnet.NegativeRuntime
|
||||
}
|
||||
|
||||
@@ -3237,6 +3237,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;Close;();generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;DisposeAsync;();generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;Flush;();generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;FlushAsync;();generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;IndentedTextWriter;(System.IO.TextWriter);generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;OutputTabs;();generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;OutputTabsAsync;();generated",
|
||||
@@ -3280,6 +3281,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;WriteLineAsync;(System.String);generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;WriteLineNoTabs;(System.String);generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;WriteLineNoTabsAsync;(System.String);generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;get_Indent;();generated",
|
||||
"System.CodeDom.Compiler;IndentedTextWriter;set_Indent;(System.Int32);generated",
|
||||
"System.CodeDom.Compiler;TempFileCollection;AddFile;(System.String,System.Boolean);generated",
|
||||
@@ -3457,6 +3459,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.CodeDom;CodeNamespaceImport;set_LinePragma;(System.CodeDom.CodeLinePragma);generated",
|
||||
"System.CodeDom;CodeNamespaceImportCollection;Clear;();generated",
|
||||
"System.CodeDom;CodeNamespaceImportCollection;Contains;(System.Object);generated",
|
||||
"System.CodeDom;CodeNamespaceImportCollection;GetEnumerator;();generated",
|
||||
"System.CodeDom;CodeNamespaceImportCollection;IndexOf;(System.Object);generated",
|
||||
"System.CodeDom;CodeNamespaceImportCollection;Remove;(System.Object);generated",
|
||||
"System.CodeDom;CodeNamespaceImportCollection;RemoveAt;(System.Int32);generated",
|
||||
@@ -3609,6 +3612,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Concurrent;ConcurrentDictionary<,>;Contains;(System.Collections.Generic.KeyValuePair<TKey,TValue>);generated",
|
||||
"System.Collections.Concurrent;ConcurrentDictionary<,>;Contains;(System.Object);generated",
|
||||
"System.Collections.Concurrent;ConcurrentDictionary<,>;ContainsKey;(TKey);generated",
|
||||
"System.Collections.Concurrent;ConcurrentDictionary<,>;GetEnumerator;();generated",
|
||||
"System.Collections.Concurrent;ConcurrentDictionary<,>;Remove;(System.Collections.Generic.KeyValuePair<TKey,TValue>);generated",
|
||||
"System.Collections.Concurrent;ConcurrentDictionary<,>;Remove;(System.Object);generated",
|
||||
"System.Collections.Concurrent;ConcurrentDictionary<,>;Remove;(TKey);generated",
|
||||
@@ -3672,7 +3676,9 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Generic;ByteEqualityComparer;Equals;(System.Object);generated",
|
||||
"System.Collections.Generic;ByteEqualityComparer;GetHashCode;();generated",
|
||||
"System.Collections.Generic;ByteEqualityComparer;GetHashCode;(System.Byte);generated",
|
||||
"System.Collections.Generic;CollectionExtensions;AsReadOnly<,>;(System.Collections.Generic.IDictionary<TKey,TValue>);generated",
|
||||
"System.Collections.Generic;CollectionExtensions;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>,TKey);generated",
|
||||
"System.Collections.Generic;CollectionExtensions;TryAdd<,>;(System.Collections.Generic.IDictionary<TKey,TValue>,TKey,TValue);generated",
|
||||
"System.Collections.Generic;Comparer<>;Compare;(System.Object,System.Object);generated",
|
||||
"System.Collections.Generic;Comparer<>;Compare;(T,T);generated",
|
||||
"System.Collections.Generic;Comparer<>;get_Default;();generated",
|
||||
@@ -3813,6 +3819,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Generic;KeyNotFoundException;KeyNotFoundException;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);generated",
|
||||
"System.Collections.Generic;KeyNotFoundException;KeyNotFoundException;(System.String);generated",
|
||||
"System.Collections.Generic;KeyNotFoundException;KeyNotFoundException;(System.String,System.Exception);generated",
|
||||
"System.Collections.Generic;KeyValuePair;Create<,>;(TKey,TValue);generated",
|
||||
"System.Collections.Generic;KeyValuePair<,>;ToString;();generated",
|
||||
"System.Collections.Generic;LinkedList<>+Enumerator;Dispose;();generated",
|
||||
"System.Collections.Generic;LinkedList<>+Enumerator;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);generated",
|
||||
@@ -4165,6 +4172,9 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Immutable;ImmutableDictionary;CreateBuilder<,>;();generated",
|
||||
"System.Collections.Immutable;ImmutableDictionary;CreateBuilder<,>;(System.Collections.Generic.IEqualityComparer<TKey>);generated",
|
||||
"System.Collections.Immutable;ImmutableDictionary;CreateBuilder<,>;(System.Collections.Generic.IEqualityComparer<TKey>,System.Collections.Generic.IEqualityComparer<TValue>);generated",
|
||||
"System.Collections.Immutable;ImmutableDictionary;CreateRange<,>;(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>);generated",
|
||||
"System.Collections.Immutable;ImmutableDictionary;CreateRange<,>;(System.Collections.Generic.IEqualityComparer<TKey>,System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>);generated",
|
||||
"System.Collections.Immutable;ImmutableDictionary;CreateRange<,>;(System.Collections.Generic.IEqualityComparer<TKey>,System.Collections.Generic.IEqualityComparer<TValue>,System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>);generated",
|
||||
"System.Collections.Immutable;ImmutableDictionary;GetValueOrDefault<,>;(System.Collections.Immutable.IImmutableDictionary<TKey,TValue>,TKey);generated",
|
||||
"System.Collections.Immutable;ImmutableDictionary<,>+Builder;Clear;();generated",
|
||||
"System.Collections.Immutable;ImmutableDictionary<,>+Builder;Contains;(System.Collections.Generic.KeyValuePair<TKey,TValue>);generated",
|
||||
@@ -4254,8 +4264,10 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Immutable;ImmutableInterlocked;TryRemove<,>;(System.Collections.Immutable.ImmutableDictionary<TKey,TValue>,TKey,TValue);generated",
|
||||
"System.Collections.Immutable;ImmutableInterlocked;TryUpdate<,>;(System.Collections.Immutable.ImmutableDictionary<TKey,TValue>,TKey,TValue,TValue);generated",
|
||||
"System.Collections.Immutable;ImmutableList;Create<>;();generated",
|
||||
"System.Collections.Immutable;ImmutableList;Create<>;(T);generated",
|
||||
"System.Collections.Immutable;ImmutableList;Create<>;(T[]);generated",
|
||||
"System.Collections.Immutable;ImmutableList;CreateBuilder<>;();generated",
|
||||
"System.Collections.Immutable;ImmutableList;CreateRange<>;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableList;IndexOf<>;(System.Collections.Immutable.IImmutableList<T>,T);generated",
|
||||
"System.Collections.Immutable;ImmutableList;IndexOf<>;(System.Collections.Immutable.IImmutableList<T>,T,System.Collections.Generic.IEqualityComparer<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableList;IndexOf<>;(System.Collections.Immutable.IImmutableList<T>,T,System.Int32);generated",
|
||||
@@ -4319,6 +4331,9 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Immutable;ImmutableQueue<>;get_IsEmpty;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary;Create<,>;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary;CreateBuilder<,>;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary;CreateRange<,>;(System.Collections.Generic.IComparer<TKey>,System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary;CreateRange<,>;(System.Collections.Generic.IComparer<TKey>,System.Collections.Generic.IEqualityComparer<TValue>,System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary;CreateRange<,>;(System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;Clear;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;Contains;(System.Collections.Generic.KeyValuePair<TKey,TValue>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;Contains;(System.Object);generated",
|
||||
@@ -4338,6 +4353,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;Dispose;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;MoveNext;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;Reset;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;get_Current;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>;Clear;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>;Contains;(System.Collections.Generic.KeyValuePair<TKey,TValue>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>;Contains;(System.Object);generated",
|
||||
@@ -4354,6 +4370,8 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>;get_IsReadOnly;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedDictionary<,>;get_IsSynchronized;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet;Create<>;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet;Create<>;(System.Collections.Generic.IComparer<T>,T);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet;Create<>;(T);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet;Create<>;(T[]);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet;CreateBuilder<>;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;Clear;();generated",
|
||||
@@ -4367,6 +4385,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;Overlaps;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;Remove;(T);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;SetEquals;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;SymmetricExceptWith;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;get_Count;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;get_IsReadOnly;();generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;get_IsSynchronized;();generated",
|
||||
@@ -4380,6 +4399,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;ExceptWith;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;IndexOf;(System.Object);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;IndexOf;(T);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;Intersect;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;IntersectWith;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;IsProperSubsetOf;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;IsProperSupersetOf;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
@@ -4391,6 +4411,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;Remove;(T);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;RemoveAt;(System.Int32);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;SetEquals;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;SymmetricExcept;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;SymmetricExceptWith;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;UnionWith;(System.Collections.Generic.IEnumerable<T>);generated",
|
||||
"System.Collections.Immutable;ImmutableSortedSet<>;get_Count;();generated",
|
||||
@@ -4798,6 +4819,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.ComponentModel.Composition.Hosting;AggregateCatalog;AggregateCatalog;(System.Collections.Generic.IEnumerable<System.ComponentModel.Composition.Primitives.ComposablePartCatalog>);generated",
|
||||
"System.ComponentModel.Composition.Hosting;AggregateCatalog;AggregateCatalog;(System.ComponentModel.Composition.Primitives.ComposablePartCatalog[]);generated",
|
||||
"System.ComponentModel.Composition.Hosting;AggregateCatalog;Dispose;(System.Boolean);generated",
|
||||
"System.ComponentModel.Composition.Hosting;AggregateCatalog;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);generated",
|
||||
"System.ComponentModel.Composition.Hosting;AggregateCatalog;OnChanged;(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs);generated",
|
||||
"System.ComponentModel.Composition.Hosting;AggregateCatalog;OnChanging;(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs);generated",
|
||||
"System.ComponentModel.Composition.Hosting;AggregateExportProvider;AggregateExportProvider;(System.Collections.Generic.IEnumerable<System.ComponentModel.Composition.Hosting.ExportProvider>);generated",
|
||||
@@ -4805,11 +4827,13 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.ComponentModel.Composition.Hosting;AggregateExportProvider;Dispose;(System.Boolean);generated",
|
||||
"System.ComponentModel.Composition.Hosting;ApplicationCatalog;ApplicationCatalog;();generated",
|
||||
"System.ComponentModel.Composition.Hosting;ApplicationCatalog;Dispose;(System.Boolean);generated",
|
||||
"System.ComponentModel.Composition.Hosting;ApplicationCatalog;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);generated",
|
||||
"System.ComponentModel.Composition.Hosting;ApplicationCatalog;ToString;();generated",
|
||||
"System.ComponentModel.Composition.Hosting;ApplicationCatalog;get_DisplayName;();generated",
|
||||
"System.ComponentModel.Composition.Hosting;ApplicationCatalog;get_Origin;();generated",
|
||||
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;AssemblyCatalog;(System.String);generated",
|
||||
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;Dispose;(System.Boolean);generated",
|
||||
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);generated",
|
||||
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;get_Origin;();generated",
|
||||
"System.ComponentModel.Composition.Hosting;AtomicComposition;AtomicComposition;();generated",
|
||||
"System.ComponentModel.Composition.Hosting;AtomicComposition;Complete;();generated",
|
||||
@@ -4845,6 +4869,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.ComponentModel.Composition.Hosting;CompositionContainer;SatisfyImportsOnce;(System.ComponentModel.Composition.Primitives.ComposablePart);generated",
|
||||
"System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;CompositionScopeDefinition;();generated",
|
||||
"System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;Dispose;(System.Boolean);generated",
|
||||
"System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);generated",
|
||||
"System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;OnChanged;(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs);generated",
|
||||
"System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;OnChanging;(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs);generated",
|
||||
"System.ComponentModel.Composition.Hosting;CompositionService;Dispose;();generated",
|
||||
@@ -4854,6 +4879,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;DirectoryCatalog;(System.String,System.Reflection.ReflectionContext);generated",
|
||||
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;DirectoryCatalog;(System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);generated",
|
||||
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;Dispose;(System.Boolean);generated",
|
||||
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);generated",
|
||||
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;OnChanged;(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs);generated",
|
||||
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;OnChanging;(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs);generated",
|
||||
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;Refresh;();generated",
|
||||
@@ -4876,6 +4902,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;get_AtomicComposition;();generated",
|
||||
"System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;set_AtomicComposition;(System.ComponentModel.Composition.Hosting.AtomicComposition);generated",
|
||||
"System.ComponentModel.Composition.Hosting;FilteredCatalog;Dispose;(System.Boolean);generated",
|
||||
"System.ComponentModel.Composition.Hosting;FilteredCatalog;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);generated",
|
||||
"System.ComponentModel.Composition.Hosting;FilteredCatalog;IncludeDependencies;();generated",
|
||||
"System.ComponentModel.Composition.Hosting;FilteredCatalog;IncludeDependents;();generated",
|
||||
"System.ComponentModel.Composition.Hosting;FilteredCatalog;OnChanged;(System.ComponentModel.Composition.Hosting.ComposablePartCatalogChangeEventArgs);generated",
|
||||
@@ -4909,6 +4936,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.ComponentModel.Composition.Primitives;ComposablePartCatalog;ComposablePartCatalog;();generated",
|
||||
"System.ComponentModel.Composition.Primitives;ComposablePartCatalog;Dispose;();generated",
|
||||
"System.ComponentModel.Composition.Primitives;ComposablePartCatalog;Dispose;(System.Boolean);generated",
|
||||
"System.ComponentModel.Composition.Primitives;ComposablePartCatalog;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);generated",
|
||||
"System.ComponentModel.Composition.Primitives;ComposablePartDefinition;ComposablePartDefinition;();generated",
|
||||
"System.ComponentModel.Composition.Primitives;ComposablePartDefinition;CreatePart;();generated",
|
||||
"System.ComponentModel.Composition.Primitives;ComposablePartDefinition;get_ExportDefinitions;();generated",
|
||||
@@ -6336,6 +6364,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.ComponentModel;TypeConverter+SimplePropertyDescriptor;get_ComponentType;();generated",
|
||||
"System.ComponentModel;TypeConverter+SimplePropertyDescriptor;get_IsReadOnly;();generated",
|
||||
"System.ComponentModel;TypeConverter+SimplePropertyDescriptor;get_PropertyType;();generated",
|
||||
"System.ComponentModel;TypeConverter+StandardValuesCollection;GetEnumerator;();generated",
|
||||
"System.ComponentModel;TypeConverter+StandardValuesCollection;get_Count;();generated",
|
||||
"System.ComponentModel;TypeConverter+StandardValuesCollection;get_IsSynchronized;();generated",
|
||||
"System.ComponentModel;TypeConverter+StandardValuesCollection;get_SyncRoot;();generated",
|
||||
@@ -7482,9 +7511,14 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Data.Common;DbCommand;DbCommand;();generated",
|
||||
"System.Data.Common;DbCommand;DisposeAsync;();generated",
|
||||
"System.Data.Common;DbCommand;ExecuteDbDataReader;(System.Data.CommandBehavior);generated",
|
||||
"System.Data.Common;DbCommand;ExecuteDbDataReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);generated",
|
||||
"System.Data.Common;DbCommand;ExecuteNonQuery;();generated",
|
||||
"System.Data.Common;DbCommand;ExecuteNonQueryAsync;();generated",
|
||||
"System.Data.Common;DbCommand;ExecuteNonQueryAsync;(System.Threading.CancellationToken);generated",
|
||||
"System.Data.Common;DbCommand;ExecuteReaderAsync;();generated",
|
||||
"System.Data.Common;DbCommand;ExecuteReaderAsync;(System.Data.CommandBehavior);generated",
|
||||
"System.Data.Common;DbCommand;ExecuteReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);generated",
|
||||
"System.Data.Common;DbCommand;ExecuteReaderAsync;(System.Threading.CancellationToken);generated",
|
||||
"System.Data.Common;DbCommand;ExecuteScalar;();generated",
|
||||
"System.Data.Common;DbCommand;ExecuteScalarAsync;();generated",
|
||||
"System.Data.Common;DbCommand;ExecuteScalarAsync;(System.Threading.CancellationToken);generated",
|
||||
@@ -7568,6 +7602,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Data.Common;DbConnectionStringBuilder;GetDefaultEvent;();generated",
|
||||
"System.Data.Common;DbConnectionStringBuilder;GetDefaultProperty;();generated",
|
||||
"System.Data.Common;DbConnectionStringBuilder;GetEditor;(System.Type);generated",
|
||||
"System.Data.Common;DbConnectionStringBuilder;GetEnumerator;();generated",
|
||||
"System.Data.Common;DbConnectionStringBuilder;GetEvents;();generated",
|
||||
"System.Data.Common;DbConnectionStringBuilder;GetEvents;(System.Attribute[]);generated",
|
||||
"System.Data.Common;DbConnectionStringBuilder;GetProperties;(System.Collections.Hashtable);generated",
|
||||
@@ -7635,6 +7670,8 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Data.Common;DbDataReader;GetDecimal;(System.Int32);generated",
|
||||
"System.Data.Common;DbDataReader;GetDouble;(System.Int32);generated",
|
||||
"System.Data.Common;DbDataReader;GetFieldType;(System.Int32);generated",
|
||||
"System.Data.Common;DbDataReader;GetFieldValueAsync<>;(System.Int32);generated",
|
||||
"System.Data.Common;DbDataReader;GetFieldValueAsync<>;(System.Int32,System.Threading.CancellationToken);generated",
|
||||
"System.Data.Common;DbDataReader;GetFloat;(System.Int32);generated",
|
||||
"System.Data.Common;DbDataReader;GetGuid;(System.Int32);generated",
|
||||
"System.Data.Common;DbDataReader;GetInt16;(System.Int32);generated",
|
||||
@@ -7644,6 +7681,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Data.Common;DbDataReader;GetOrdinal;(System.String);generated",
|
||||
"System.Data.Common;DbDataReader;GetProviderSpecificFieldType;(System.Int32);generated",
|
||||
"System.Data.Common;DbDataReader;GetSchemaTable;();generated",
|
||||
"System.Data.Common;DbDataReader;GetSchemaTableAsync;(System.Threading.CancellationToken);generated",
|
||||
"System.Data.Common;DbDataReader;GetStream;(System.Int32);generated",
|
||||
"System.Data.Common;DbDataReader;GetString;(System.Int32);generated",
|
||||
"System.Data.Common;DbDataReader;GetValue;(System.Int32);generated",
|
||||
@@ -8955,6 +8993,9 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Data;DataColumnChangeEventArgs;get_ProposedValue;();generated",
|
||||
"System.Data;DataColumnChangeEventArgs;get_Row;();generated",
|
||||
"System.Data;DataColumnChangeEventArgs;set_ProposedValue;(System.Object);generated",
|
||||
"System.Data;DataColumnCollection;Add;();generated",
|
||||
"System.Data;DataColumnCollection;Add;(System.String,System.Type);generated",
|
||||
"System.Data;DataColumnCollection;Add;(System.String,System.Type,System.String);generated",
|
||||
"System.Data;DataColumnCollection;CanRemove;(System.Data.DataColumn);generated",
|
||||
"System.Data;DataColumnCollection;Clear;();generated",
|
||||
"System.Data;DataColumnCollection;Contains;(System.String);generated",
|
||||
@@ -8977,6 +9018,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Data;DataReaderExtensions;GetDecimal;(System.Data.Common.DbDataReader,System.String);generated",
|
||||
"System.Data;DataReaderExtensions;GetDouble;(System.Data.Common.DbDataReader,System.String);generated",
|
||||
"System.Data;DataReaderExtensions;GetFieldType;(System.Data.Common.DbDataReader,System.String);generated",
|
||||
"System.Data;DataReaderExtensions;GetFieldValueAsync<>;(System.Data.Common.DbDataReader,System.String,System.Threading.CancellationToken);generated",
|
||||
"System.Data;DataReaderExtensions;GetFloat;(System.Data.Common.DbDataReader,System.String);generated",
|
||||
"System.Data;DataReaderExtensions;GetInt16;(System.Data.Common.DbDataReader,System.String);generated",
|
||||
"System.Data;DataReaderExtensions;GetInt32;(System.Data.Common.DbDataReader,System.String);generated",
|
||||
@@ -8994,6 +9036,10 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Data;DataRelation;get_Nested;();generated",
|
||||
"System.Data;DataRelation;get_ParentTable;();generated",
|
||||
"System.Data;DataRelation;set_Nested;(System.Boolean);generated",
|
||||
"System.Data;DataRelationCollection;Add;(System.Data.DataColumn,System.Data.DataColumn);generated",
|
||||
"System.Data;DataRelationCollection;Add;(System.Data.DataColumn[],System.Data.DataColumn[]);generated",
|
||||
"System.Data;DataRelationCollection;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn);generated",
|
||||
"System.Data;DataRelationCollection;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[]);generated",
|
||||
"System.Data;DataRelationCollection;AddCore;(System.Data.DataRelation);generated",
|
||||
"System.Data;DataRelationCollection;CanRemove;(System.Data.DataRelation);generated",
|
||||
"System.Data;DataRelationCollection;Clear;();generated",
|
||||
@@ -9240,6 +9286,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Data;DataTableClearEventArgs;get_Table;();generated",
|
||||
"System.Data;DataTableClearEventArgs;get_TableName;();generated",
|
||||
"System.Data;DataTableClearEventArgs;get_TableNamespace;();generated",
|
||||
"System.Data;DataTableCollection;Add;();generated",
|
||||
"System.Data;DataTableCollection;CanRemove;(System.Data.DataTable);generated",
|
||||
"System.Data;DataTableCollection;Clear;();generated",
|
||||
"System.Data;DataTableCollection;Contains;(System.String);generated",
|
||||
@@ -13395,15 +13442,19 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Drawing.Printing;PrinterResolution;set_X;(System.Int32);generated",
|
||||
"System.Drawing.Printing;PrinterResolution;set_Y;(System.Int32);generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PaperSizeCollection;CopyTo;(System.Drawing.Printing.PaperSize[],System.Int32);generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PaperSizeCollection;GetEnumerator;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PaperSizeCollection;get_Count;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PaperSizeCollection;get_IsSynchronized;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PaperSourceCollection;CopyTo;(System.Drawing.Printing.PaperSource[],System.Int32);generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PaperSourceCollection;GetEnumerator;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PaperSourceCollection;get_Count;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PaperSourceCollection;get_IsSynchronized;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;CopyTo;(System.Drawing.Printing.PrinterResolution[],System.Int32);generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;GetEnumerator;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;get_Count;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;get_IsSynchronized;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+StringCollection;CopyTo;(System.String[],System.Int32);generated",
|
||||
"System.Drawing.Printing;PrinterSettings+StringCollection;GetEnumerator;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+StringCollection;get_Count;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings+StringCollection;get_IsSynchronized;();generated",
|
||||
"System.Drawing.Printing;PrinterSettings;Clone;();generated",
|
||||
@@ -15734,10 +15785,12 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.IO.Compression;DeflateStream;EndWrite;(System.IAsyncResult);generated",
|
||||
"System.IO.Compression;DeflateStream;Flush;();generated",
|
||||
"System.IO.Compression;DeflateStream;Read;(System.Span<System.Byte>);generated",
|
||||
"System.IO.Compression;DeflateStream;ReadAsync;(System.Memory<System.Byte>,System.Threading.CancellationToken);generated",
|
||||
"System.IO.Compression;DeflateStream;ReadByte;();generated",
|
||||
"System.IO.Compression;DeflateStream;Seek;(System.Int64,System.IO.SeekOrigin);generated",
|
||||
"System.IO.Compression;DeflateStream;SetLength;(System.Int64);generated",
|
||||
"System.IO.Compression;DeflateStream;Write;(System.ReadOnlySpan<System.Byte>);generated",
|
||||
"System.IO.Compression;DeflateStream;WriteAsync;(System.ReadOnlyMemory<System.Byte>,System.Threading.CancellationToken);generated",
|
||||
"System.IO.Compression;DeflateStream;WriteByte;(System.Byte);generated",
|
||||
"System.IO.Compression;DeflateStream;get_CanRead;();generated",
|
||||
"System.IO.Compression;DeflateStream;get_CanSeek;();generated",
|
||||
@@ -15753,10 +15806,12 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.IO.Compression;GZipStream;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionLevel);generated",
|
||||
"System.IO.Compression;GZipStream;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionMode);generated",
|
||||
"System.IO.Compression;GZipStream;Read;(System.Span<System.Byte>);generated",
|
||||
"System.IO.Compression;GZipStream;ReadAsync;(System.Memory<System.Byte>,System.Threading.CancellationToken);generated",
|
||||
"System.IO.Compression;GZipStream;ReadByte;();generated",
|
||||
"System.IO.Compression;GZipStream;Seek;(System.Int64,System.IO.SeekOrigin);generated",
|
||||
"System.IO.Compression;GZipStream;SetLength;(System.Int64);generated",
|
||||
"System.IO.Compression;GZipStream;Write;(System.ReadOnlySpan<System.Byte>);generated",
|
||||
"System.IO.Compression;GZipStream;WriteAsync;(System.ReadOnlyMemory<System.Byte>,System.Threading.CancellationToken);generated",
|
||||
"System.IO.Compression;GZipStream;WriteByte;(System.Byte);generated",
|
||||
"System.IO.Compression;GZipStream;get_CanRead;();generated",
|
||||
"System.IO.Compression;GZipStream;get_CanSeek;();generated",
|
||||
@@ -16328,6 +16383,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.IO;BinaryWriter;BinaryWriter;(System.IO.Stream,System.Text.Encoding);generated",
|
||||
"System.IO;BinaryWriter;Close;();generated", "System.IO;BinaryWriter;Dispose;();generated",
|
||||
"System.IO;BinaryWriter;Dispose;(System.Boolean);generated",
|
||||
"System.IO;BinaryWriter;DisposeAsync;();generated",
|
||||
"System.IO;BinaryWriter;Flush;();generated",
|
||||
"System.IO;BinaryWriter;Seek;(System.Int32,System.IO.SeekOrigin);generated",
|
||||
"System.IO;BinaryWriter;Write7BitEncodedInt64;(System.Int64);generated",
|
||||
@@ -16718,14 +16774,18 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.IO;Stream;DisposeAsync;();generated",
|
||||
"System.IO;Stream;EndRead;(System.IAsyncResult);generated",
|
||||
"System.IO;Stream;EndWrite;(System.IAsyncResult);generated",
|
||||
"System.IO;Stream;Flush;();generated", "System.IO;Stream;ObjectInvariant;();generated",
|
||||
"System.IO;Stream;Flush;();generated", "System.IO;Stream;FlushAsync;();generated",
|
||||
"System.IO;Stream;FlushAsync;(System.Threading.CancellationToken);generated",
|
||||
"System.IO;Stream;ObjectInvariant;();generated",
|
||||
"System.IO;Stream;Read;(System.Span<System.Byte>);generated",
|
||||
"System.IO;Stream;ReadAsync;(System.Memory<System.Byte>,System.Threading.CancellationToken);generated",
|
||||
"System.IO;Stream;ReadByte;();generated",
|
||||
"System.IO;Stream;Seek;(System.Int64,System.IO.SeekOrigin);generated",
|
||||
"System.IO;Stream;SetLength;(System.Int64);generated",
|
||||
"System.IO;Stream;ValidateBufferArguments;(System.Byte[],System.Int32,System.Int32);generated",
|
||||
"System.IO;Stream;ValidateCopyToArguments;(System.IO.Stream,System.Int32);generated",
|
||||
"System.IO;Stream;Write;(System.ReadOnlySpan<System.Byte>);generated",
|
||||
"System.IO;Stream;WriteAsync;(System.ReadOnlyMemory<System.Byte>,System.Threading.CancellationToken);generated",
|
||||
"System.IO;Stream;WriteByte;(System.Byte);generated",
|
||||
"System.IO;Stream;get_CanRead;();generated", "System.IO;Stream;get_CanSeek;();generated",
|
||||
"System.IO;Stream;get_CanTimeout;();generated",
|
||||
@@ -16745,6 +16805,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.IO;StreamWriter;Dispose;(System.Boolean);generated",
|
||||
"System.IO;StreamWriter;DisposeAsync;();generated",
|
||||
"System.IO;StreamWriter;Flush;();generated",
|
||||
"System.IO;StreamWriter;FlushAsync;();generated",
|
||||
"System.IO;StreamWriter;StreamWriter;(System.IO.Stream);generated",
|
||||
"System.IO;StreamWriter;StreamWriter;(System.IO.Stream,System.Text.Encoding);generated",
|
||||
"System.IO;StreamWriter;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32);generated",
|
||||
@@ -16763,8 +16824,15 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.IO;StreamWriter;Write;(System.String,System.Object,System.Object);generated",
|
||||
"System.IO;StreamWriter;Write;(System.String,System.Object,System.Object,System.Object);generated",
|
||||
"System.IO;StreamWriter;Write;(System.String,System.Object[]);generated",
|
||||
"System.IO;StreamWriter;WriteAsync;(System.Char);generated",
|
||||
"System.IO;StreamWriter;WriteAsync;(System.Char[],System.Int32,System.Int32);generated",
|
||||
"System.IO;StreamWriter;WriteAsync;(System.String);generated",
|
||||
"System.IO;StreamWriter;WriteLine;(System.ReadOnlySpan<System.Char>);generated",
|
||||
"System.IO;StreamWriter;WriteLine;(System.String);generated",
|
||||
"System.IO;StreamWriter;WriteLineAsync;();generated",
|
||||
"System.IO;StreamWriter;WriteLineAsync;(System.Char);generated",
|
||||
"System.IO;StreamWriter;WriteLineAsync;(System.Char[],System.Int32,System.Int32);generated",
|
||||
"System.IO;StreamWriter;WriteLineAsync;(System.String);generated",
|
||||
"System.IO;StreamWriter;get_AutoFlush;();generated",
|
||||
"System.IO;StreamWriter;set_AutoFlush;(System.Boolean);generated",
|
||||
"System.IO;StringReader;Close;();generated",
|
||||
@@ -16777,8 +16845,10 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.IO;StringWriter;StringWriter;(System.Text.StringBuilder);generated",
|
||||
"System.IO;StringWriter;Write;(System.Char);generated",
|
||||
"System.IO;StringWriter;Write;(System.ReadOnlySpan<System.Char>);generated",
|
||||
"System.IO;StringWriter;Write;(System.Text.StringBuilder);generated",
|
||||
"System.IO;StringWriter;WriteAsync;(System.Char);generated",
|
||||
"System.IO;StringWriter;WriteLine;(System.ReadOnlySpan<System.Char>);generated",
|
||||
"System.IO;StringWriter;WriteLine;(System.Text.StringBuilder);generated",
|
||||
"System.IO;StringWriter;WriteLineAsync;(System.Char);generated",
|
||||
"System.IO;StringWriter;get_Encoding;();generated",
|
||||
"System.IO;TextReader;Close;();generated", "System.IO;TextReader;Dispose;();generated",
|
||||
@@ -16787,6 +16857,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.IO;TextWriter;Close;();generated", "System.IO;TextWriter;Dispose;();generated",
|
||||
"System.IO;TextWriter;Dispose;(System.Boolean);generated",
|
||||
"System.IO;TextWriter;DisposeAsync;();generated", "System.IO;TextWriter;Flush;();generated",
|
||||
"System.IO;TextWriter;FlushAsync;();generated",
|
||||
"System.IO;TextWriter;TextWriter;();generated",
|
||||
"System.IO;TextWriter;Write;(System.Boolean);generated",
|
||||
"System.IO;TextWriter;Write;(System.Char);generated",
|
||||
@@ -16801,6 +16872,9 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.IO;TextWriter;Write;(System.Text.StringBuilder);generated",
|
||||
"System.IO;TextWriter;Write;(System.UInt32);generated",
|
||||
"System.IO;TextWriter;Write;(System.UInt64);generated",
|
||||
"System.IO;TextWriter;WriteAsync;(System.Char);generated",
|
||||
"System.IO;TextWriter;WriteAsync;(System.Char[],System.Int32,System.Int32);generated",
|
||||
"System.IO;TextWriter;WriteAsync;(System.String);generated",
|
||||
"System.IO;TextWriter;WriteLine;();generated",
|
||||
"System.IO;TextWriter;WriteLine;(System.Boolean);generated",
|
||||
"System.IO;TextWriter;WriteLine;(System.Char);generated",
|
||||
@@ -16810,8 +16884,13 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.IO;TextWriter;WriteLine;(System.Int64);generated",
|
||||
"System.IO;TextWriter;WriteLine;(System.ReadOnlySpan<System.Char>);generated",
|
||||
"System.IO;TextWriter;WriteLine;(System.Single);generated",
|
||||
"System.IO;TextWriter;WriteLine;(System.Text.StringBuilder);generated",
|
||||
"System.IO;TextWriter;WriteLine;(System.UInt32);generated",
|
||||
"System.IO;TextWriter;WriteLine;(System.UInt64);generated",
|
||||
"System.IO;TextWriter;WriteLineAsync;();generated",
|
||||
"System.IO;TextWriter;WriteLineAsync;(System.Char);generated",
|
||||
"System.IO;TextWriter;WriteLineAsync;(System.Char[],System.Int32,System.Int32);generated",
|
||||
"System.IO;TextWriter;WriteLineAsync;(System.String);generated",
|
||||
"System.IO;TextWriter;get_Encoding;();generated",
|
||||
"System.IO;UnmanagedMemoryAccessor;Dispose;();generated",
|
||||
"System.IO;UnmanagedMemoryAccessor;Dispose;(System.Boolean);generated",
|
||||
@@ -18015,6 +18094,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net.Http.Headers;HttpHeadersNonValidated+Enumerator;Reset;();generated",
|
||||
"System.Net.Http.Headers;HttpHeadersNonValidated;Contains;(System.String);generated",
|
||||
"System.Net.Http.Headers;HttpHeadersNonValidated;ContainsKey;(System.String);generated",
|
||||
"System.Net.Http.Headers;HttpHeadersNonValidated;GetEnumerator;();generated",
|
||||
"System.Net.Http.Headers;HttpHeadersNonValidated;get_Count;();generated",
|
||||
"System.Net.Http.Headers;HttpRequestHeaders;get_Accept;();generated",
|
||||
"System.Net.Http.Headers;HttpRequestHeaders;get_AcceptCharset;();generated",
|
||||
@@ -18203,6 +18283,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net.Http.Json;JsonContent;TryComputeLength;(System.Int64);generated",
|
||||
"System.Net.Http.Json;JsonContent;get_ObjectType;();generated",
|
||||
"System.Net.Http.Json;JsonContent;get_Value;();generated",
|
||||
"System.Net.Http;ByteArrayContent;CreateContentReadStreamAsync;();generated",
|
||||
"System.Net.Http;ByteArrayContent;TryComputeLength;(System.Int64);generated",
|
||||
"System.Net.Http;DelegatingHandler;DelegatingHandler;();generated",
|
||||
"System.Net.Http;DelegatingHandler;Dispose;(System.Boolean);generated",
|
||||
@@ -18302,6 +18383,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net.Http;HttpClientHandler;set_UseDefaultCredentials;(System.Boolean);generated",
|
||||
"System.Net.Http;HttpClientHandler;set_UseProxy;(System.Boolean);generated",
|
||||
"System.Net.Http;HttpContent;CreateContentReadStreamAsync;();generated",
|
||||
"System.Net.Http;HttpContent;CreateContentReadStreamAsync;(System.Threading.CancellationToken);generated",
|
||||
"System.Net.Http;HttpContent;Dispose;();generated",
|
||||
"System.Net.Http;HttpContent;Dispose;(System.Boolean);generated",
|
||||
"System.Net.Http;HttpContent;HttpContent;();generated",
|
||||
@@ -18390,6 +18472,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net.Http;MultipartContent;get_HeaderEncodingSelector;();generated",
|
||||
"System.Net.Http;MultipartFormDataContent;MultipartFormDataContent;();generated",
|
||||
"System.Net.Http;MultipartFormDataContent;MultipartFormDataContent;(System.String);generated",
|
||||
"System.Net.Http;ReadOnlyMemoryContent;CreateContentReadStreamAsync;();generated",
|
||||
"System.Net.Http;ReadOnlyMemoryContent;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);generated",
|
||||
"System.Net.Http;ReadOnlyMemoryContent;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);generated",
|
||||
"System.Net.Http;ReadOnlyMemoryContent;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);generated",
|
||||
@@ -19020,6 +19103,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net.Quic;QuicStreamAbortedException;QuicStreamAbortedException;(System.String,System.Int64);generated",
|
||||
"System.Net.Quic;QuicStreamAbortedException;get_ErrorCode;();generated",
|
||||
"System.Net.Security;AuthenticatedStream;Dispose;(System.Boolean);generated",
|
||||
"System.Net.Security;AuthenticatedStream;DisposeAsync;();generated",
|
||||
"System.Net.Security;AuthenticatedStream;get_IsAuthenticated;();generated",
|
||||
"System.Net.Security;AuthenticatedStream;get_IsEncrypted;();generated",
|
||||
"System.Net.Security;AuthenticatedStream;get_IsMutuallyAuthenticated;();generated",
|
||||
@@ -19244,6 +19328,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net.Sockets;SendPacketsElement;set_MemoryBuffer;(System.Nullable<System.ReadOnlyMemory<System.Byte>>);generated",
|
||||
"System.Net.Sockets;SendPacketsElement;set_OffsetLong;(System.Int64);generated",
|
||||
"System.Net.Sockets;Socket;AcceptAsync;();generated",
|
||||
"System.Net.Sockets;Socket;AcceptAsync;(System.Net.Sockets.Socket);generated",
|
||||
"System.Net.Sockets;Socket;CancelConnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);generated",
|
||||
"System.Net.Sockets;Socket;Close;();generated",
|
||||
"System.Net.Sockets;Socket;Close;(System.Int32);generated",
|
||||
@@ -19258,6 +19343,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net.Sockets;Socket;DuplicateAndClose;(System.Int32);generated",
|
||||
"System.Net.Sockets;Socket;EndAccept;(System.Byte[],System.IAsyncResult);generated",
|
||||
"System.Net.Sockets;Socket;EndAccept;(System.Byte[],System.Int32,System.IAsyncResult);generated",
|
||||
"System.Net.Sockets;Socket;EndAccept;(System.IAsyncResult);generated",
|
||||
"System.Net.Sockets;Socket;EndConnect;(System.IAsyncResult);generated",
|
||||
"System.Net.Sockets;Socket;EndDisconnect;(System.IAsyncResult);generated",
|
||||
"System.Net.Sockets;Socket;EndReceive;(System.IAsyncResult);generated",
|
||||
@@ -19292,6 +19378,10 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net.Sockets;Socket;ReceiveAsync;(System.ArraySegment<System.Byte>,System.Net.Sockets.SocketFlags);generated",
|
||||
"System.Net.Sockets;Socket;ReceiveAsync;(System.Collections.Generic.IList<System.ArraySegment<System.Byte>>);generated",
|
||||
"System.Net.Sockets;Socket;ReceiveAsync;(System.Collections.Generic.IList<System.ArraySegment<System.Byte>>,System.Net.Sockets.SocketFlags);generated",
|
||||
"System.Net.Sockets;Socket;ReceiveFromAsync;(System.ArraySegment<System.Byte>,System.Net.EndPoint);generated",
|
||||
"System.Net.Sockets;Socket;ReceiveFromAsync;(System.ArraySegment<System.Byte>,System.Net.Sockets.SocketFlags,System.Net.EndPoint);generated",
|
||||
"System.Net.Sockets;Socket;ReceiveMessageFromAsync;(System.ArraySegment<System.Byte>,System.Net.EndPoint);generated",
|
||||
"System.Net.Sockets;Socket;ReceiveMessageFromAsync;(System.ArraySegment<System.Byte>,System.Net.Sockets.SocketFlags,System.Net.EndPoint);generated",
|
||||
"System.Net.Sockets;Socket;Select;(System.Collections.IList,System.Collections.IList,System.Collections.IList,System.Int32);generated",
|
||||
"System.Net.Sockets;Socket;Send;(System.Byte[]);generated",
|
||||
"System.Net.Sockets;Socket;Send;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags);generated",
|
||||
@@ -19391,11 +19481,14 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net.Sockets;SocketInformation;set_Options;(System.Net.Sockets.SocketInformationOptions);generated",
|
||||
"System.Net.Sockets;SocketInformation;set_ProtocolInformation;(System.Byte[]);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;AcceptAsync;(System.Net.Sockets.Socket);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;AcceptAsync;(System.Net.Sockets.Socket,System.Net.Sockets.Socket);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;ConnectAsync;(System.Net.Sockets.Socket,System.Net.IPAddress[],System.Int32);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;ConnectAsync;(System.Net.Sockets.Socket,System.Net.IPAddress[],System.Int32,System.Threading.CancellationToken);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;ConnectAsync;(System.Net.Sockets.Socket,System.String,System.Int32);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;ReceiveAsync;(System.Net.Sockets.Socket,System.ArraySegment<System.Byte>,System.Net.Sockets.SocketFlags);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;ReceiveAsync;(System.Net.Sockets.Socket,System.Collections.Generic.IList<System.ArraySegment<System.Byte>>,System.Net.Sockets.SocketFlags);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;ReceiveFromAsync;(System.Net.Sockets.Socket,System.ArraySegment<System.Byte>,System.Net.Sockets.SocketFlags,System.Net.EndPoint);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;ReceiveMessageFromAsync;(System.Net.Sockets.Socket,System.ArraySegment<System.Byte>,System.Net.Sockets.SocketFlags,System.Net.EndPoint);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;SendAsync;(System.Net.Sockets.Socket,System.ArraySegment<System.Byte>,System.Net.Sockets.SocketFlags);generated",
|
||||
"System.Net.Sockets;SocketTaskExtensions;SendAsync;(System.Net.Sockets.Socket,System.Collections.Generic.IList<System.ArraySegment<System.Byte>>,System.Net.Sockets.SocketFlags);generated",
|
||||
"System.Net.Sockets;TcpClient;Close;();generated",
|
||||
@@ -19437,6 +19530,8 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net.Sockets;TcpListener;AcceptTcpClientAsync;(System.Threading.CancellationToken);generated",
|
||||
"System.Net.Sockets;TcpListener;AllowNatTraversal;(System.Boolean);generated",
|
||||
"System.Net.Sockets;TcpListener;Create;(System.Int32);generated",
|
||||
"System.Net.Sockets;TcpListener;EndAcceptSocket;(System.IAsyncResult);generated",
|
||||
"System.Net.Sockets;TcpListener;EndAcceptTcpClient;(System.IAsyncResult);generated",
|
||||
"System.Net.Sockets;TcpListener;Pending;();generated",
|
||||
"System.Net.Sockets;TcpListener;Start;();generated",
|
||||
"System.Net.Sockets;TcpListener;Start;(System.Int32);generated",
|
||||
@@ -19653,6 +19748,10 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net;CredentialCache;Remove;(System.Uri,System.String);generated",
|
||||
"System.Net;CredentialCache;get_DefaultCredentials;();generated",
|
||||
"System.Net;CredentialCache;get_DefaultNetworkCredentials;();generated",
|
||||
"System.Net;Dns;EndGetHostAddresses;(System.IAsyncResult);generated",
|
||||
"System.Net;Dns;EndGetHostByName;(System.IAsyncResult);generated",
|
||||
"System.Net;Dns;EndGetHostEntry;(System.IAsyncResult);generated",
|
||||
"System.Net;Dns;EndResolve;(System.IAsyncResult);generated",
|
||||
"System.Net;Dns;GetHostAddresses;(System.String);generated",
|
||||
"System.Net;Dns;GetHostAddresses;(System.String,System.Net.Sockets.AddressFamily);generated",
|
||||
"System.Net;Dns;GetHostAddressesAsync;(System.String);generated",
|
||||
@@ -19696,6 +19795,8 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net;EndpointPermission;get_Port;();generated",
|
||||
"System.Net;EndpointPermission;get_Transport;();generated",
|
||||
"System.Net;FileWebRequest;Abort;();generated",
|
||||
"System.Net;FileWebRequest;EndGetRequestStream;(System.IAsyncResult);generated",
|
||||
"System.Net;FileWebRequest;EndGetResponse;(System.IAsyncResult);generated",
|
||||
"System.Net;FileWebRequest;FileWebRequest;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);generated",
|
||||
"System.Net;FileWebRequest;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);generated",
|
||||
"System.Net;FileWebRequest;GetRequestStreamAsync;();generated",
|
||||
@@ -19850,6 +19951,9 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net;HttpWebRequest;AddRange;(System.String,System.Int32,System.Int32);generated",
|
||||
"System.Net;HttpWebRequest;AddRange;(System.String,System.Int64);generated",
|
||||
"System.Net;HttpWebRequest;AddRange;(System.String,System.Int64,System.Int64);generated",
|
||||
"System.Net;HttpWebRequest;EndGetRequestStream;(System.IAsyncResult);generated",
|
||||
"System.Net;HttpWebRequest;EndGetRequestStream;(System.IAsyncResult,System.Net.TransportContext);generated",
|
||||
"System.Net;HttpWebRequest;EndGetResponse;(System.IAsyncResult);generated",
|
||||
"System.Net;HttpWebRequest;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);generated",
|
||||
"System.Net;HttpWebRequest;HttpWebRequest;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);generated",
|
||||
"System.Net;HttpWebRequest;get_AllowAutoRedirect;();generated",
|
||||
@@ -19998,7 +20102,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Net;NetworkCredential;get_SecurePassword;();generated",
|
||||
"System.Net;NetworkCredential;set_SecurePassword;(System.Security.SecureString);generated",
|
||||
"System.Net;PathList;GetCookiesCount;();generated",
|
||||
"System.Net;PathList;get_Count;();generated",
|
||||
"System.Net;PathList;get_Count;();generated", "System.Net;PathList;get_Values;();generated",
|
||||
"System.Net;PathList;set_Item;(System.String,System.Object);generated",
|
||||
"System.Net;ProtocolViolationException;ProtocolViolationException;();generated",
|
||||
"System.Net;ProtocolViolationException;ProtocolViolationException;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);generated",
|
||||
@@ -20384,7 +20488,6 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Numerics;BigInteger;Log;(System.Numerics.BigInteger,System.Double);generated",
|
||||
"System.Numerics;BigInteger;ModPow;(System.Numerics.BigInteger,System.Numerics.BigInteger,System.Numerics.BigInteger);generated",
|
||||
"System.Numerics;BigInteger;Multiply;(System.Numerics.BigInteger,System.Numerics.BigInteger);generated",
|
||||
"System.Numerics;BigInteger;Negate;(System.Numerics.BigInteger);generated",
|
||||
"System.Numerics;BigInteger;Parse;(System.ReadOnlySpan<System.Char>,System.Globalization.NumberStyles,System.IFormatProvider);generated",
|
||||
"System.Numerics;BigInteger;Parse;(System.String);generated",
|
||||
"System.Numerics;BigInteger;Parse;(System.String,System.Globalization.NumberStyles);generated",
|
||||
@@ -20450,7 +20553,6 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Numerics;BigInteger;op_Multiply;(System.Numerics.BigInteger,System.Numerics.BigInteger);generated",
|
||||
"System.Numerics;BigInteger;op_OnesComplement;(System.Numerics.BigInteger);generated",
|
||||
"System.Numerics;BigInteger;op_Subtraction;(System.Numerics.BigInteger,System.Numerics.BigInteger);generated",
|
||||
"System.Numerics;BigInteger;op_UnaryNegation;(System.Numerics.BigInteger);generated",
|
||||
"System.Numerics;BitOperations;IsPow2;(System.Int32);generated",
|
||||
"System.Numerics;BitOperations;IsPow2;(System.Int64);generated",
|
||||
"System.Numerics;BitOperations;IsPow2;(System.IntPtr);generated",
|
||||
@@ -23374,6 +23476,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Resources;ResourceSet;Dispose;(System.Boolean);generated",
|
||||
"System.Resources;ResourceSet;GetDefaultReader;();generated",
|
||||
"System.Resources;ResourceSet;GetDefaultWriter;();generated",
|
||||
"System.Resources;ResourceSet;GetEnumerator;();generated",
|
||||
"System.Resources;ResourceSet;GetObject;(System.String);generated",
|
||||
"System.Resources;ResourceSet;GetObject;(System.String,System.Boolean);generated",
|
||||
"System.Resources;ResourceSet;GetString;(System.String);generated",
|
||||
@@ -23588,8 +23691,6 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Runtime.CompilerServices;DisablePrivateReflectionAttribute;DisablePrivateReflectionAttribute;();generated",
|
||||
"System.Runtime.CompilerServices;DiscardableAttribute;DiscardableAttribute;();generated",
|
||||
"System.Runtime.CompilerServices;DynamicAttribute;DynamicAttribute;();generated",
|
||||
"System.Runtime.CompilerServices;DynamicAttribute;DynamicAttribute;(System.Boolean[]);generated",
|
||||
"System.Runtime.CompilerServices;DynamicAttribute;get_TransformFlags;();generated",
|
||||
"System.Runtime.CompilerServices;EnumeratorCancellationAttribute;EnumeratorCancellationAttribute;();generated",
|
||||
"System.Runtime.CompilerServices;FixedAddressValueTypeAttribute;FixedAddressValueTypeAttribute;();generated",
|
||||
"System.Runtime.CompilerServices;FixedBufferAttribute;FixedBufferAttribute;(System.Type,System.Int32);generated",
|
||||
@@ -29575,6 +29676,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Security.Cryptography.Pkcs;Pkcs12SafeContents;Decrypt;(System.ReadOnlySpan<System.Byte>);generated",
|
||||
"System.Security.Cryptography.Pkcs;Pkcs12SafeContents;Decrypt;(System.ReadOnlySpan<System.Char>);generated",
|
||||
"System.Security.Cryptography.Pkcs;Pkcs12SafeContents;Decrypt;(System.String);generated",
|
||||
"System.Security.Cryptography.Pkcs;Pkcs12SafeContents;GetBags;();generated",
|
||||
"System.Security.Cryptography.Pkcs;Pkcs12SafeContents;Pkcs12SafeContents;();generated",
|
||||
"System.Security.Cryptography.Pkcs;Pkcs12SafeContents;get_ConfidentialityMode;();generated",
|
||||
"System.Security.Cryptography.Pkcs;Pkcs12SafeContents;get_IsReadOnly;();generated",
|
||||
@@ -29882,6 +29984,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Security.Cryptography.X509Certificates;X509Certificate;get_Handle;();generated",
|
||||
"System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;MoveNext;();generated",
|
||||
"System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;Reset;();generated",
|
||||
"System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;X509CertificateEnumerator;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);generated",
|
||||
"System.Security.Cryptography.X509Certificates;X509CertificateCollection;Contains;(System.Security.Cryptography.X509Certificates.X509Certificate);generated",
|
||||
"System.Security.Cryptography.X509Certificates;X509CertificateCollection;GetHashCode;();generated",
|
||||
"System.Security.Cryptography.X509Certificates;X509CertificateCollection;IndexOf;(System.Security.Cryptography.X509Certificates.X509Certificate);generated",
|
||||
@@ -30051,6 +30154,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Security.Cryptography.Xml;EncryptionPropertyCollection;get_IsReadOnly;();generated",
|
||||
"System.Security.Cryptography.Xml;EncryptionPropertyCollection;get_IsSynchronized;();generated",
|
||||
"System.Security.Cryptography.Xml;IRelDecryptor;Decrypt;(System.Security.Cryptography.Xml.EncryptionMethod,System.Security.Cryptography.Xml.KeyInfo,System.IO.Stream);generated",
|
||||
"System.Security.Cryptography.Xml;KeyInfo;GetEnumerator;(System.Type);generated",
|
||||
"System.Security.Cryptography.Xml;KeyInfo;GetXml;();generated",
|
||||
"System.Security.Cryptography.Xml;KeyInfo;KeyInfo;();generated",
|
||||
"System.Security.Cryptography.Xml;KeyInfo;get_Count;();generated",
|
||||
@@ -30117,6 +30221,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Security.Cryptography.Xml;Transform;Transform;();generated",
|
||||
"System.Security.Cryptography.Xml;Transform;get_InputTypes;();generated",
|
||||
"System.Security.Cryptography.Xml;Transform;get_OutputTypes;();generated",
|
||||
"System.Security.Cryptography.Xml;TransformChain;GetEnumerator;();generated",
|
||||
"System.Security.Cryptography.Xml;TransformChain;TransformChain;();generated",
|
||||
"System.Security.Cryptography.Xml;TransformChain;get_Count;();generated",
|
||||
"System.Security.Cryptography.Xml;X509IssuerSerial;get_IssuerName;();generated",
|
||||
@@ -34289,6 +34394,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Text.Json.Serialization;ReferenceResolver;GetReference;(System.Object,System.Boolean);generated",
|
||||
"System.Text.Json.Serialization;ReferenceResolver;ResolveReference;(System.String);generated",
|
||||
"System.Text.Json.SourceGeneration;JsonSourceGenerator;Execute;(Microsoft.CodeAnalysis.GeneratorExecutionContext);generated",
|
||||
"System.Text.Json.SourceGeneration;JsonSourceGenerator;GetSerializableTypes;();generated",
|
||||
"System.Text.Json.SourceGeneration;JsonSourceGenerator;Initialize;(Microsoft.CodeAnalysis.GeneratorInitializationContext);generated",
|
||||
"System.Text.Json.SourceGeneration;JsonSourceGenerator;Initialize;(Microsoft.CodeAnalysis.IncrementalGeneratorInitializationContext);generated",
|
||||
"System.Text.Json;JsonDocument;Dispose;();generated",
|
||||
@@ -35318,6 +35424,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Threading.RateLimiting;MetadataName<>;op_Inequality;(System.Threading.RateLimiting.MetadataName<>,System.Threading.RateLimiting.MetadataName<>);generated",
|
||||
"System.Threading.RateLimiting;RateLimitLease;Dispose;();generated",
|
||||
"System.Threading.RateLimiting;RateLimitLease;Dispose;(System.Boolean);generated",
|
||||
"System.Threading.RateLimiting;RateLimitLease;GetAllMetadata;();generated",
|
||||
"System.Threading.RateLimiting;RateLimitLease;TryGetMetadata;(System.String,System.Object);generated",
|
||||
"System.Threading.RateLimiting;RateLimitLease;get_IsAcquired;();generated",
|
||||
"System.Threading.RateLimiting;RateLimitLease;get_MetadataNames;();generated",
|
||||
@@ -35400,6 +35507,10 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Threading.Tasks.Dataflow;DataflowBlock;OutputAvailableAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>);generated",
|
||||
"System.Threading.Tasks.Dataflow;DataflowBlock;OutputAvailableAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>,System.Threading.CancellationToken);generated",
|
||||
"System.Threading.Tasks.Dataflow;DataflowBlock;ReceiveAllAsync<>;(System.Threading.Tasks.Dataflow.IReceivableSourceBlock<TOutput>,System.Threading.CancellationToken);generated",
|
||||
"System.Threading.Tasks.Dataflow;DataflowBlock;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>);generated",
|
||||
"System.Threading.Tasks.Dataflow;DataflowBlock;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>,System.Threading.CancellationToken);generated",
|
||||
"System.Threading.Tasks.Dataflow;DataflowBlock;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>,System.TimeSpan);generated",
|
||||
"System.Threading.Tasks.Dataflow;DataflowBlock;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock<TOutput>,System.TimeSpan,System.Threading.CancellationToken);generated",
|
||||
"System.Threading.Tasks.Dataflow;DataflowBlock;SendAsync<>;(System.Threading.Tasks.Dataflow.ITargetBlock<TInput>,TInput);generated",
|
||||
"System.Threading.Tasks.Dataflow;DataflowBlockOptions;DataflowBlockOptions;();generated",
|
||||
"System.Threading.Tasks.Dataflow;DataflowBlockOptions;get_BoundedCapacity;();generated",
|
||||
@@ -36612,10 +36723,13 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Xml.Schema;XmlSchemaParticle;set_MinOccursString;(System.String);generated",
|
||||
"System.Xml.Schema;XmlSchemaPatternFacet;XmlSchemaPatternFacet;();generated",
|
||||
"System.Xml.Schema;XmlSchemaRedefine;XmlSchemaRedefine;();generated",
|
||||
"System.Xml.Schema;XmlSchemaSet;Add;(System.Xml.Schema.XmlSchemaSet);generated",
|
||||
"System.Xml.Schema;XmlSchemaSet;Compile;();generated",
|
||||
"System.Xml.Schema;XmlSchemaSet;Contains;(System.String);generated",
|
||||
"System.Xml.Schema;XmlSchemaSet;Contains;(System.Xml.Schema.XmlSchema);generated",
|
||||
"System.Xml.Schema;XmlSchemaSet;CopyTo;(System.Xml.Schema.XmlSchema[],System.Int32);generated",
|
||||
"System.Xml.Schema;XmlSchemaSet;RemoveRecursive;(System.Xml.Schema.XmlSchema);generated",
|
||||
"System.Xml.Schema;XmlSchemaSet;Schemas;();generated",
|
||||
"System.Xml.Schema;XmlSchemaSet;Schemas;(System.String);generated",
|
||||
"System.Xml.Schema;XmlSchemaSet;XmlSchemaSet;();generated",
|
||||
"System.Xml.Schema;XmlSchemaSet;get_Count;();generated",
|
||||
@@ -36868,6 +36982,7 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteEmptyTag;(System.String,System.String);generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteEndElement;();generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteEndElement;(System.Object);generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteId;(System.Object);generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteNamespaceDeclarations;(System.Xml.Serialization.XmlSerializerNamespaces);generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteNullTagEncoded;(System.String);generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteNullTagEncoded;(System.String,System.String);generated",
|
||||
@@ -36876,6 +36991,8 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteNullableQualifiedNameEncoded;(System.String,System.String,System.Xml.XmlQualifiedName,System.Xml.XmlQualifiedName);generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteNullableQualifiedNameLiteral;(System.String,System.String,System.Xml.XmlQualifiedName);generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteReferencedElements;();generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteReferencingElement;(System.String,System.String,System.Object);generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteReferencingElement;(System.String,System.String,System.Object,System.Boolean);generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteStartDocument;();generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteStartElement;(System.String);generated",
|
||||
"System.Xml.Serialization;XmlSerializationWriter;WriteStartElement;(System.String,System.String);generated",
|
||||
@@ -41046,7 +41163,29 @@ private class RuntimeNegativesummaryCsv extends NegativeSummaryModelCsv {
|
||||
"System;Tuple<>;Equals;(System.Object,System.Collections.IEqualityComparer);generated",
|
||||
"System;Tuple<>;GetHashCode;();generated",
|
||||
"System;Tuple<>;GetHashCode;(System.Collections.IEqualityComparer);generated",
|
||||
"System;Tuple<>;get_Length;();generated", "System;Type;Equals;(System.Object);generated",
|
||||
"System;Tuple<>;get_Length;();generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11,T12,T13,T14,System.ValueTuple<T15,T16,T17,T18,T19,T20,T21>>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11,T12,T13,T14,System.ValueTuple<T15,T16,T17,T18,T19,T20>>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11,T12,T13,T14,System.ValueTuple<T15,T16,T17,T18,T19>>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11,T12,T13,T14,System.ValueTuple<T15,T16,T17,T18>>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11,T12,T13,T14,System.ValueTuple<T15,T16,T17>>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11,T12,T13,T14,System.ValueTuple<T15,T16>>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11,T12,T13,T14,System.ValueTuple<T15>>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11,T12,T13,T14>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11,T12,T13>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11,T12>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10,T11>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9,T10>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8,T9>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7,System.ValueTuple<T8>>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6,T7>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5,T6>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,,>;(System.ValueTuple<T1,T2,T3,T4,T5>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,,>;(System.ValueTuple<T1,T2,T3,T4>);generated",
|
||||
"System;TupleExtensions;ToTuple<,,>;(System.ValueTuple<T1,T2,T3>);generated",
|
||||
"System;TupleExtensions;ToTuple<,>;(System.ValueTuple<T1,T2>);generated",
|
||||
"System;TupleExtensions;ToTuple<>;(System.ValueTuple<T1>);generated",
|
||||
"System;Type;Equals;(System.Object);generated",
|
||||
"System;Type;Equals;(System.Type);generated", "System;Type;GetArrayRank;();generated",
|
||||
"System;Type;GetAttributeFlagsImpl;();generated",
|
||||
"System;Type;GetConstructorImpl;(System.Reflection.BindingFlags,System.Reflection.Binder,System.Reflection.CallingConventions,System.Type[],System.Reflection.ParameterModifier[]);generated",
|
||||
|
||||
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