зеркало из https://github.com/github/codeql.git
Merge branch 'main' into python/test-MaD-keyword-argument
This commit is contained in:
Коммит
c520cb6d58
|
@ -1 +1 @@
|
||||||
7.0.2
|
7.1.0
|
||||||
|
|
|
@ -6,6 +6,7 @@ provide:
|
||||||
- "*/ql/consistency-queries/qlpack.yml"
|
- "*/ql/consistency-queries/qlpack.yml"
|
||||||
- "*/ql/automodel/src/qlpack.yml"
|
- "*/ql/automodel/src/qlpack.yml"
|
||||||
- "*/ql/automodel/test/qlpack.yml"
|
- "*/ql/automodel/test/qlpack.yml"
|
||||||
|
- "python/extractor/qlpack.yml"
|
||||||
- "shared/**/qlpack.yml"
|
- "shared/**/qlpack.yml"
|
||||||
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
|
- "cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml"
|
||||||
- "go/ql/config/legacy-support/qlpack.yml"
|
- "go/ql/config/legacy-support/qlpack.yml"
|
||||||
|
|
|
@ -255,7 +255,6 @@
|
||||||
"cpp/ql/lib/semmle/code/cpp/XML.qll",
|
"cpp/ql/lib/semmle/code/cpp/XML.qll",
|
||||||
"csharp/ql/lib/semmle/code/csharp/XML.qll",
|
"csharp/ql/lib/semmle/code/csharp/XML.qll",
|
||||||
"java/ql/lib/semmle/code/xml/XML.qll",
|
"java/ql/lib/semmle/code/xml/XML.qll",
|
||||||
"javascript/ql/lib/semmle/javascript/XML.qll",
|
|
||||||
"python/ql/lib/semmle/python/xml/XML.qll"
|
"python/ql/lib/semmle/python/xml/XML.qll"
|
||||||
],
|
],
|
||||||
"DuplicationProblems.inc.qhelp": [
|
"DuplicationProblems.inc.qhelp": [
|
||||||
|
@ -372,4 +371,4 @@
|
||||||
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
|
"python/ql/test/experimental/dataflow/model-summaries/InlineTaintTest.ext.yml",
|
||||||
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
|
"python/ql/test/experimental/dataflow/model-summaries/NormalDataflowTest.ext.yml"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
class Expr extends @expr {
|
||||||
|
string toString() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
class Location extends @location_expr {
|
||||||
|
string toString() { none() }
|
||||||
|
}
|
||||||
|
|
||||||
|
from Expr expr, int kind, int kind_new, Location loc
|
||||||
|
where
|
||||||
|
exprs(expr, kind, loc) and
|
||||||
|
if kind = 363 then kind_new = 1 else kind_new = kind
|
||||||
|
select expr, kind_new, loc
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,4 @@
|
||||||
|
description: Introduce re-use expressions
|
||||||
|
compatibility: partial
|
||||||
|
expr_reuse.rel: delete
|
||||||
|
exprs.rel: run exprs.qlo
|
|
@ -1,3 +1,13 @@
|
||||||
|
## 0.12.8
|
||||||
|
|
||||||
|
No user-facing changes.
|
||||||
|
|
||||||
|
## 0.12.7
|
||||||
|
|
||||||
|
### Minor Analysis Improvements
|
||||||
|
|
||||||
|
* Added destructors for named objects to the intermediate representation.
|
||||||
|
|
||||||
## 0.12.6
|
## 0.12.6
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
category: feature
|
||||||
|
---
|
||||||
|
* Added a predicate `GuardCondition.valueControls` to query whether a basic block is guarded by a particular `case` of a `switch` statement.
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
category: feature
|
||||||
|
---
|
||||||
|
* Added a predicate `GuardCondition.comparesLt/4` to query whether an expression is compared to a constant.
|
||||||
|
* Added a predicate `GuardCondition.ensuresLt/4` to query whether a basic block is guarded by an expression being less than a constant.
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
category: feature
|
||||||
|
---
|
||||||
|
* Added a predicate `GuardCondition.comparesEq/4` to query whether an expression is compared to a constant.
|
||||||
|
* Added a predicate `GuardCondition.ensuresEq/4` to query whether a basic block is guarded by an expression being equal to a constant.
|
|
@ -1,4 +1,5 @@
|
||||||
---
|
## 0.12.7
|
||||||
category: minorAnalysis
|
|
||||||
---
|
### Minor Analysis Improvements
|
||||||
* Added destructors for named objects to the intermediate representation.
|
|
||||||
|
* Added destructors for named objects to the intermediate representation.
|
|
@ -0,0 +1,3 @@
|
||||||
|
## 0.12.8
|
||||||
|
|
||||||
|
No user-facing changes.
|
|
@ -1,2 +1,2 @@
|
||||||
---
|
---
|
||||||
lastReleaseVersion: 0.12.6
|
lastReleaseVersion: 0.12.8
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: codeql/cpp-all
|
name: codeql/cpp-all
|
||||||
version: 0.12.7-dev
|
version: 0.12.9-dev
|
||||||
groups: cpp
|
groups: cpp
|
||||||
dbscheme: semmlecode.cpp.dbscheme
|
dbscheme: semmlecode.cpp.dbscheme
|
||||||
extractor: cpp
|
extractor: cpp
|
||||||
|
|
|
@ -309,9 +309,12 @@ class ExprNode extends AstNode {
|
||||||
override AstNode getChildInternal(int childIndex) {
|
override AstNode getChildInternal(int childIndex) {
|
||||||
result.getAst() = expr.getChild(childIndex)
|
result.getAst() = expr.getChild(childIndex)
|
||||||
or
|
or
|
||||||
|
childIndex = max(int index | exists(expr.getChild(index)) or index = 0) + 1 and
|
||||||
|
result.getAst() = expr.(ConditionDeclExpr).getInitializingExpr()
|
||||||
|
or
|
||||||
exists(int destructorIndex |
|
exists(int destructorIndex |
|
||||||
result.getAst() = expr.getImplicitDestructorCall(destructorIndex) and
|
result.getAst() = expr.getImplicitDestructorCall(destructorIndex) and
|
||||||
childIndex = destructorIndex + max(int index | exists(expr.getChild(index)) or index = 0) + 1
|
childIndex = destructorIndex + max(int index | exists(expr.getChild(index)) or index = 0) + 2
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -686,6 +689,8 @@ private string getChildAccessorWithoutConversions(Locatable parent, Element chil
|
||||||
not namedExprChildPredicates(expr, child, _) and
|
not namedExprChildPredicates(expr, child, _) and
|
||||||
exists(int n | expr.getChild(n) = child and result = "getChild(" + n + ")")
|
exists(int n | expr.getChild(n) = child and result = "getChild(" + n + ")")
|
||||||
or
|
or
|
||||||
|
expr.(ConditionDeclExpr).getInitializingExpr() = child and result = "getInitializingExpr()"
|
||||||
|
or
|
||||||
exists(int n |
|
exists(int n |
|
||||||
expr.getImplicitDestructorCall(n) = child and
|
expr.getImplicitDestructorCall(n) = child and
|
||||||
result = "getImplicitDestructorCall(" + n + ")"
|
result = "getImplicitDestructorCall(" + n + ")"
|
||||||
|
|
|
@ -20,6 +20,44 @@ private predicate isUnreachedBlock(IRBlock block) {
|
||||||
block.getFirstInstruction() instanceof UnreachedInstruction
|
block.getFirstInstruction() instanceof UnreachedInstruction
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private newtype TAbstractValue =
|
||||||
|
TBooleanValue(boolean b) { b = true or b = false } or
|
||||||
|
TMatchValue(CaseEdge c)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract value. This is either a boolean value, or a `switch` case.
|
||||||
|
*/
|
||||||
|
abstract class AbstractValue extends TAbstractValue {
|
||||||
|
/** Gets an abstract value that represents the dual of this value, if any. */
|
||||||
|
abstract AbstractValue getDualValue();
|
||||||
|
|
||||||
|
/** Gets a textual representation of this abstract value. */
|
||||||
|
abstract string toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A Boolean value. */
|
||||||
|
class BooleanValue extends AbstractValue, TBooleanValue {
|
||||||
|
/** Gets the underlying Boolean value. */
|
||||||
|
boolean getValue() { this = TBooleanValue(result) }
|
||||||
|
|
||||||
|
override BooleanValue getDualValue() { result.getValue() = this.getValue().booleanNot() }
|
||||||
|
|
||||||
|
override string toString() { result = this.getValue().toString() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A value that represents a match against a specific `switch` case. */
|
||||||
|
class MatchValue extends AbstractValue, TMatchValue {
|
||||||
|
/** Gets the case. */
|
||||||
|
CaseEdge getCase() { this = TMatchValue(result) }
|
||||||
|
|
||||||
|
override MatchValue getDualValue() {
|
||||||
|
// A `MatchValue` has no dual.
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
override string toString() { result = this.getCase().toString() }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Boolean condition in the AST that guards one or more basic blocks. This includes
|
* A Boolean condition in the AST that guards one or more basic blocks. This includes
|
||||||
* operands of logical operators but not switch statements.
|
* operands of logical operators but not switch statements.
|
||||||
|
@ -34,6 +72,15 @@ class GuardCondition extends Expr {
|
||||||
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
||||||
|
* entered if the value of this condition is `v`.
|
||||||
|
*
|
||||||
|
* For details on what "controls" mean, see the QLDoc for `controls`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate valueControls(BasicBlock controlled, AbstractValue v) { none() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
||||||
* entered if the value of this condition is `testIsTrue`.
|
* entered if the value of this condition is `testIsTrue`.
|
||||||
|
@ -61,7 +108,9 @@ class GuardCondition extends Expr {
|
||||||
* true (for `&&`) or false (for `||`) branch.
|
* true (for `&&`) or false (for `||`) branch.
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate controls(BasicBlock controlled, boolean testIsTrue) { none() }
|
final predicate controls(BasicBlock controlled, boolean testIsTrue) {
|
||||||
|
this.valueControls(controlled, any(BooleanValue bv | bv.getValue() = testIsTrue))
|
||||||
|
}
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
|
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
|
||||||
cached
|
cached
|
||||||
|
@ -76,6 +125,20 @@ class GuardCondition extends Expr {
|
||||||
cached
|
cached
|
||||||
predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { none() }
|
predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `e < k` evaluates to `isLessThan` if
|
||||||
|
* this expression evaluates to `value`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate comparesLt(Expr e, int k, boolean isLessThan, AbstractValue value) { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `e < k` must be `isLessThan` in `block`.
|
||||||
|
* If `isLessThan = false` then this implies `e >= k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresLt(Expr e, int k, BasicBlock block, boolean isLessThan) { none() }
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
||||||
cached
|
cached
|
||||||
predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
||||||
|
@ -88,6 +151,17 @@ class GuardCondition extends Expr {
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { none() }
|
predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) { none() }
|
||||||
|
|
||||||
|
/** Holds if (determined by this guard) `e == k` evaluates to `areEqual` if this expression evaluates to `value`. */
|
||||||
|
cached
|
||||||
|
predicate comparesEq(Expr e, int k, boolean areEqual, AbstractValue value) { none() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `e == k` must be `areEqual` in `block`.
|
||||||
|
* If `areEqual = false` then this implies `e != k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,13 +172,13 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||||
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
|
override predicate valueControls(BasicBlock controlled, AbstractValue v) {
|
||||||
exists(BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs |
|
exists(BinaryLogicalOperation binop, GuardCondition lhs, GuardCondition rhs |
|
||||||
this = binop and
|
this = binop and
|
||||||
lhs = binop.getLeftOperand() and
|
lhs = binop.getLeftOperand() and
|
||||||
rhs = binop.getRightOperand() and
|
rhs = binop.getRightOperand() and
|
||||||
lhs.controls(controlled, testIsTrue) and
|
lhs.valueControls(controlled, v) and
|
||||||
rhs.controls(controlled, testIsTrue)
|
rhs.valueControls(controlled, v)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,12 +190,27 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate comparesLt(Expr e, int k, boolean isLessThan, AbstractValue value) {
|
||||||
|
exists(BooleanValue partValue, GuardCondition part |
|
||||||
|
this.(BinaryLogicalOperation)
|
||||||
|
.impliesValue(part, partValue.getValue(), value.(BooleanValue).getValue())
|
||||||
|
|
|
||||||
|
part.comparesLt(e, k, isLessThan, partValue)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
||||||
exists(boolean testIsTrue |
|
exists(boolean testIsTrue |
|
||||||
this.comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
|
this.comparesLt(left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate ensuresLt(Expr e, int k, BasicBlock block, boolean isLessThan) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
this.comparesLt(e, k, isLessThan, value) and this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
||||||
exists(boolean partIsTrue, GuardCondition part |
|
exists(boolean partIsTrue, GuardCondition part |
|
||||||
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue)
|
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue)
|
||||||
|
@ -135,6 +224,21 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||||
this.comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
this.comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate comparesEq(Expr e, int k, boolean areEqual, AbstractValue value) {
|
||||||
|
exists(BooleanValue partValue, GuardCondition part |
|
||||||
|
this.(BinaryLogicalOperation)
|
||||||
|
.impliesValue(part, partValue.getValue(), value.(BooleanValue).getValue())
|
||||||
|
|
|
||||||
|
part.comparesEq(e, k, areEqual, partValue)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
this.comparesEq(e, k, areEqual, value) and this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -146,13 +250,12 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||||
|
|
||||||
GuardConditionFromIR() { this = ir.getUnconvertedResultExpression() }
|
GuardConditionFromIR() { this = ir.getUnconvertedResultExpression() }
|
||||||
|
|
||||||
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
|
override predicate valueControls(BasicBlock controlled, AbstractValue v) {
|
||||||
// This condition must determine the flow of control; that is, this
|
// This condition must determine the flow of control; that is, this
|
||||||
// node must be a top-level condition.
|
// node must be a top-level condition.
|
||||||
this.controlsBlock(controlled, testIsTrue)
|
this.controlsBlock(controlled, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
|
|
||||||
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
|
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
|
||||||
exists(Instruction li, Instruction ri |
|
exists(Instruction li, Instruction ri |
|
||||||
li.getUnconvertedResultExpression() = left and
|
li.getUnconvertedResultExpression() = left and
|
||||||
|
@ -161,10 +264,13 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override predicate comparesLt(Expr e, int k, boolean isLessThan, AbstractValue value) {
|
||||||
* Holds if (determined by this guard) `left < right + k` must be `isLessThan` in `block`.
|
exists(Instruction i |
|
||||||
* If `isLessThan = false` then this implies `left >= right + k`.
|
i.getUnconvertedResultExpression() = e and
|
||||||
*/
|
ir.comparesLt(i.getAUse(), k, isLessThan, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
||||||
exists(Instruction li, Instruction ri, boolean testIsTrue |
|
exists(Instruction li, Instruction ri, boolean testIsTrue |
|
||||||
li.getUnconvertedResultExpression() = left and
|
li.getUnconvertedResultExpression() = left and
|
||||||
|
@ -174,7 +280,14 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
override predicate ensuresLt(Expr e, int k, BasicBlock block, boolean isLessThan) {
|
||||||
|
exists(Instruction i, AbstractValue value |
|
||||||
|
i.getUnconvertedResultExpression() = e and
|
||||||
|
ir.comparesLt(i.getAUse(), k, isLessThan, value) and
|
||||||
|
this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
||||||
exists(Instruction li, Instruction ri |
|
exists(Instruction li, Instruction ri |
|
||||||
li.getUnconvertedResultExpression() = left and
|
li.getUnconvertedResultExpression() = left and
|
||||||
|
@ -183,10 +296,6 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if (determined by this guard) `left == right + k` must be `areEqual` in `block`.
|
|
||||||
* If `areEqual = false` then this implies `left != right + k`.
|
|
||||||
*/
|
|
||||||
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
|
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
|
||||||
exists(Instruction li, Instruction ri, boolean testIsTrue |
|
exists(Instruction li, Instruction ri, boolean testIsTrue |
|
||||||
li.getUnconvertedResultExpression() = left and
|
li.getUnconvertedResultExpression() = left and
|
||||||
|
@ -196,15 +305,30 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate comparesEq(Expr e, int k, boolean areEqual, AbstractValue value) {
|
||||||
|
exists(Instruction i |
|
||||||
|
i.getUnconvertedResultExpression() = e and
|
||||||
|
ir.comparesEq(i.getAUse(), k, areEqual, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) {
|
||||||
|
exists(Instruction i, AbstractValue value |
|
||||||
|
i.getUnconvertedResultExpression() = e and
|
||||||
|
ir.comparesEq(i.getAUse(), k, areEqual, value) and
|
||||||
|
this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this condition controls `block`, meaning that `block` is only
|
* Holds if this condition controls `block`, meaning that `block` is only
|
||||||
* entered if the value of this condition is `testIsTrue`. This helper
|
* entered if the value of this condition is `v`. This helper
|
||||||
* predicate does not necessarily hold for binary logical operations like
|
* predicate does not necessarily hold for binary logical operations like
|
||||||
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
||||||
*/
|
*/
|
||||||
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
|
private predicate controlsBlock(BasicBlock controlled, AbstractValue v) {
|
||||||
exists(IRBlock irb |
|
exists(IRBlock irb |
|
||||||
ir.controls(irb, testIsTrue) and
|
ir.valueControls(irb, v) and
|
||||||
nonExcludedIRAndBasicBlock(irb, controlled) and
|
nonExcludedIRAndBasicBlock(irb, controlled) and
|
||||||
not isUnreachedBlock(irb)
|
not isUnreachedBlock(irb)
|
||||||
)
|
)
|
||||||
|
@ -249,10 +373,28 @@ private predicate nonExcludedIRAndBasicBlock(IRBlock irb, BasicBlock controlled)
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
class IRGuardCondition extends Instruction {
|
class IRGuardCondition extends Instruction {
|
||||||
ConditionalBranchInstruction branch;
|
Instruction branch;
|
||||||
|
|
||||||
cached
|
cached
|
||||||
IRGuardCondition() { branch = get_branch_for_condition(this) }
|
IRGuardCondition() { branch = getBranchForCondition(this) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
||||||
|
* entered if the value of this condition is `v`.
|
||||||
|
*
|
||||||
|
* For details on what "controls" mean, see the QLDoc for `controls`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate valueControls(IRBlock controlled, AbstractValue v) {
|
||||||
|
// This condition must determine the flow of control; that is, this
|
||||||
|
// node must be a top-level condition.
|
||||||
|
this.controlsBlock(controlled, v)
|
||||||
|
or
|
||||||
|
exists(IRGuardCondition ne |
|
||||||
|
this = ne.(LogicalNotInstruction).getUnary() and
|
||||||
|
ne.valueControls(controlled, v.getDualValue())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
* Holds if this condition controls `controlled`, meaning that `controlled` is only
|
||||||
|
@ -282,13 +424,25 @@ class IRGuardCondition extends Instruction {
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate controls(IRBlock controlled, boolean testIsTrue) {
|
predicate controls(IRBlock controlled, boolean testIsTrue) {
|
||||||
// This condition must determine the flow of control; that is, this
|
this.valueControls(controlled, any(BooleanValue bv | bv.getValue() = testIsTrue))
|
||||||
// node must be a top-level condition.
|
}
|
||||||
this.controlsBlock(controlled, testIsTrue)
|
|
||||||
|
/**
|
||||||
|
* Holds if the control-flow edge `(pred, succ)` may be taken only if
|
||||||
|
* the value of this condition is `v`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate valueControlsEdge(IRBlock pred, IRBlock succ, AbstractValue v) {
|
||||||
|
pred.getASuccessor() = succ and
|
||||||
|
this.valueControls(pred, v)
|
||||||
or
|
or
|
||||||
exists(IRGuardCondition ne |
|
succ = this.getBranchSuccessor(v) and
|
||||||
this = ne.(LogicalNotInstruction).getUnary() and
|
(
|
||||||
ne.controls(controlled, testIsTrue.booleanNot())
|
branch.(ConditionalBranchInstruction).getCondition() = this and
|
||||||
|
branch.getBlock() = pred
|
||||||
|
or
|
||||||
|
branch.(SwitchInstruction).getExpression() = this and
|
||||||
|
branch.getBlock() = pred
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,17 +451,12 @@ class IRGuardCondition extends Instruction {
|
||||||
* the value of this condition is `testIsTrue`.
|
* the value of this condition is `testIsTrue`.
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) {
|
final predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) {
|
||||||
pred.getASuccessor() = succ and
|
this.valueControlsEdge(pred, succ, any(BooleanValue bv | bv.getValue() = testIsTrue))
|
||||||
this.controls(pred, testIsTrue)
|
|
||||||
or
|
|
||||||
succ = this.getBranchSuccessor(testIsTrue) and
|
|
||||||
branch.getCondition() = this and
|
|
||||||
branch.getBlock() = pred
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the block to which `branch` jumps directly when this condition is `testIsTrue`.
|
* Gets the block to which `branch` jumps directly when the value of this condition is `v`.
|
||||||
*
|
*
|
||||||
* This predicate is intended to help with situations in which an inference can only be made
|
* This predicate is intended to help with situations in which an inference can only be made
|
||||||
* based on an edge between a block with multiple successors and a block with multiple
|
* based on an edge between a block with multiple successors and a block with multiple
|
||||||
|
@ -321,21 +470,39 @@ class IRGuardCondition extends Instruction {
|
||||||
* return x;
|
* return x;
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
private IRBlock getBranchSuccessor(boolean testIsTrue) {
|
private IRBlock getBranchSuccessor(AbstractValue v) {
|
||||||
branch.getCondition() = this and
|
branch.(ConditionalBranchInstruction).getCondition() = this and
|
||||||
(
|
exists(BooleanValue bv | bv = v |
|
||||||
testIsTrue = true and
|
bv.getValue() = true and
|
||||||
result.getFirstInstruction() = branch.getTrueSuccessor()
|
result.getFirstInstruction() = branch.(ConditionalBranchInstruction).getTrueSuccessor()
|
||||||
or
|
or
|
||||||
testIsTrue = false and
|
bv.getValue() = false and
|
||||||
result.getFirstInstruction() = branch.getFalseSuccessor()
|
result.getFirstInstruction() = branch.(ConditionalBranchInstruction).getFalseSuccessor()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(SwitchInstruction switch, CaseEdge kind | switch = branch |
|
||||||
|
switch.getExpression() = this and
|
||||||
|
result.getFirstInstruction() = switch.getSuccessor(kind) and
|
||||||
|
kind = v.(MatchValue).getCase()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
|
/** Holds if (determined by this guard) `left < right + k` evaluates to `isLessThan` if this expression evaluates to `testIsTrue`. */
|
||||||
cached
|
cached
|
||||||
predicate comparesLt(Operand left, Operand right, int k, boolean isLessThan, boolean testIsTrue) {
|
predicate comparesLt(Operand left, Operand right, int k, boolean isLessThan, boolean testIsTrue) {
|
||||||
compares_lt(this, left, right, k, isLessThan, testIsTrue)
|
exists(BooleanValue value |
|
||||||
|
compares_lt(this, left, right, k, isLessThan, value) and
|
||||||
|
value.getValue() = testIsTrue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `op < k` evaluates to `isLessThan` if
|
||||||
|
* this expression evaluates to `value`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate comparesLt(Operand op, int k, boolean isLessThan, AbstractValue value) {
|
||||||
|
compares_lt(this, op, k, isLessThan, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -344,8 +511,19 @@ class IRGuardCondition extends Instruction {
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate ensuresLt(Operand left, Operand right, int k, IRBlock block, boolean isLessThan) {
|
predicate ensuresLt(Operand left, Operand right, int k, IRBlock block, boolean isLessThan) {
|
||||||
exists(boolean testIsTrue |
|
exists(AbstractValue value |
|
||||||
compares_lt(this, left, right, k, isLessThan, testIsTrue) and this.controls(block, testIsTrue)
|
compares_lt(this, left, right, k, isLessThan, value) and this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `op < k` must be `isLessThan` in `block`.
|
||||||
|
* If `isLessThan = false` then this implies `op >= k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresLt(Operand op, int k, IRBlock block, boolean isLessThan) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
compares_lt(this, op, k, isLessThan, value) and this.valueControls(block, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,16 +535,37 @@ class IRGuardCondition extends Instruction {
|
||||||
predicate ensuresLtEdge(
|
predicate ensuresLtEdge(
|
||||||
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean isLessThan
|
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean isLessThan
|
||||||
) {
|
) {
|
||||||
exists(boolean testIsTrue |
|
exists(AbstractValue value |
|
||||||
compares_lt(this, left, right, k, isLessThan, testIsTrue) and
|
compares_lt(this, left, right, k, isLessThan, value) and
|
||||||
this.controlsEdge(pred, succ, testIsTrue)
|
this.valueControlsEdge(pred, succ, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `op < k` must be `isLessThan` on the edge from
|
||||||
|
* `pred` to `succ`. If `isLessThan = false` then this implies `op >= k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresLtEdge(Operand left, int k, IRBlock pred, IRBlock succ, boolean isLessThan) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
compares_lt(this, left, k, isLessThan, value) and
|
||||||
|
this.valueControlsEdge(pred, succ, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
||||||
cached
|
cached
|
||||||
predicate comparesEq(Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue) {
|
predicate comparesEq(Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue) {
|
||||||
compares_eq(this, left, right, k, areEqual, testIsTrue)
|
exists(BooleanValue value |
|
||||||
|
compares_eq(this, left, right, k, areEqual, value) and
|
||||||
|
value.getValue() = testIsTrue
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if (determined by this guard) `op == k` evaluates to `areEqual` if this expression evaluates to `value`. */
|
||||||
|
cached
|
||||||
|
predicate comparesEq(Operand op, int k, boolean areEqual, AbstractValue value) {
|
||||||
|
compares_eq(this, op, k, areEqual, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -375,8 +574,19 @@ class IRGuardCondition extends Instruction {
|
||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) {
|
predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) {
|
||||||
exists(boolean testIsTrue |
|
exists(AbstractValue value |
|
||||||
compares_eq(this, left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
compares_eq(this, left, right, k, areEqual, value) and this.valueControls(block, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `op == k` must be `areEqual` in `block`.
|
||||||
|
* If `areEqual = false` then this implies `op != k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresEq(Operand op, int k, IRBlock block, boolean areEqual) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
compares_eq(this, op, k, areEqual, value) and this.valueControls(block, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,19 +598,31 @@ class IRGuardCondition extends Instruction {
|
||||||
predicate ensuresEqEdge(
|
predicate ensuresEqEdge(
|
||||||
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean areEqual
|
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean areEqual
|
||||||
) {
|
) {
|
||||||
exists(boolean testIsTrue |
|
exists(AbstractValue value |
|
||||||
compares_eq(this, left, right, k, areEqual, testIsTrue) and
|
compares_eq(this, left, right, k, areEqual, value) and
|
||||||
this.controlsEdge(pred, succ, testIsTrue)
|
this.valueControlsEdge(pred, succ, value)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if (determined by this guard) `op == k` must be `areEqual` on the edge from
|
||||||
|
* `pred` to `succ`. If `areEqual = false` then this implies `op != k`.
|
||||||
|
*/
|
||||||
|
cached
|
||||||
|
predicate ensuresEqEdge(Operand op, int k, IRBlock pred, IRBlock succ, boolean areEqual) {
|
||||||
|
exists(AbstractValue value |
|
||||||
|
compares_eq(this, op, k, areEqual, value) and
|
||||||
|
this.valueControlsEdge(pred, succ, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this condition controls `block`, meaning that `block` is only
|
* Holds if this condition controls `block`, meaning that `block` is only
|
||||||
* entered if the value of this condition is `testIsTrue`. This helper
|
* entered if the value of this condition is `v`. This helper
|
||||||
* predicate does not necessarily hold for binary logical operations like
|
* predicate does not necessarily hold for binary logical operations like
|
||||||
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
||||||
*/
|
*/
|
||||||
private predicate controlsBlock(IRBlock controlled, boolean testIsTrue) {
|
private predicate controlsBlock(IRBlock controlled, AbstractValue v) {
|
||||||
not isUnreachedBlock(controlled) and
|
not isUnreachedBlock(controlled) and
|
||||||
//
|
//
|
||||||
// For this block to control the block `controlled` with `testIsTrue` the
|
// For this block to control the block `controlled` with `testIsTrue` the
|
||||||
|
@ -441,7 +663,7 @@ class IRGuardCondition extends Instruction {
|
||||||
// that `this` strictly dominates `controlled` so that isn't necessary to check
|
// that `this` strictly dominates `controlled` so that isn't necessary to check
|
||||||
// directly.
|
// directly.
|
||||||
exists(IRBlock succ |
|
exists(IRBlock succ |
|
||||||
succ = this.getBranchSuccessor(testIsTrue) and
|
succ = this.getBranchSuccessor(v) and
|
||||||
this.hasDominatingEdgeTo(succ) and
|
this.hasDominatingEdgeTo(succ) and
|
||||||
succ.dominates(controlled)
|
succ.dominates(controlled)
|
||||||
)
|
)
|
||||||
|
@ -476,12 +698,14 @@ class IRGuardCondition extends Instruction {
|
||||||
private IRBlock getBranchBlock() { result = branch.getBlock() }
|
private IRBlock getBranchBlock() { result = branch.getBlock() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConditionalBranchInstruction get_branch_for_condition(Instruction guard) {
|
private Instruction getBranchForCondition(Instruction guard) {
|
||||||
result.getCondition() = guard
|
result.(ConditionalBranchInstruction).getCondition() = guard
|
||||||
or
|
or
|
||||||
exists(LogicalNotInstruction cond |
|
exists(LogicalNotInstruction cond |
|
||||||
result = get_branch_for_condition(cond) and cond.getUnary() = guard
|
result = getBranchForCondition(cond) and cond.getUnary() = guard
|
||||||
)
|
)
|
||||||
|
or
|
||||||
|
result.(SwitchInstruction).getExpression() = guard
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -490,52 +714,98 @@ private ConditionalBranchInstruction get_branch_for_condition(Instruction guard)
|
||||||
* Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`.
|
* Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`.
|
||||||
*/
|
*/
|
||||||
private predicate compares_eq(
|
private predicate compares_eq(
|
||||||
Instruction test, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
|
Instruction test, Operand left, Operand right, int k, boolean areEqual, AbstractValue value
|
||||||
) {
|
) {
|
||||||
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
|
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
|
||||||
exists(boolean eq | simple_comparison_eq(test, left, right, k, eq) |
|
exists(AbstractValue v | simple_comparison_eq(test, left, right, k, v) |
|
||||||
areEqual = true and testIsTrue = eq
|
areEqual = true and value = v
|
||||||
or
|
or
|
||||||
areEqual = false and testIsTrue = eq.booleanNot()
|
areEqual = false and value = v.getDualValue()
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// I think this is handled by forwarding in controlsBlock.
|
// I think this is handled by forwarding in controlsBlock.
|
||||||
//or
|
//or
|
||||||
//logical_comparison_eq(test, left, right, k, areEqual, testIsTrue)
|
//logical_comparison_eq(test, left, right, k, areEqual, testIsTrue)
|
||||||
/* a == b + k => b == a - k */
|
/* a == b + k => b == a - k */
|
||||||
exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, testIsTrue))
|
exists(int mk | k = -mk | compares_eq(test, right, left, mk, areEqual, value))
|
||||||
or
|
or
|
||||||
complex_eq(test, left, right, k, areEqual, testIsTrue)
|
complex_eq(test, left, right, k, areEqual, value)
|
||||||
or
|
or
|
||||||
/* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */
|
/* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */
|
||||||
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
|
exists(AbstractValue dual | value = dual.getDualValue() |
|
||||||
compares_eq(test.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, isFalse)
|
compares_eq(test.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, dual)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if `op == k` is `areEqual` given that `test` is equal to `value`. */
|
||||||
|
private predicate compares_eq(
|
||||||
|
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
|
||||||
|
) {
|
||||||
|
/* The simple case where the test *is* the comparison so areEqual = testIsTrue xor eq. */
|
||||||
|
exists(AbstractValue v | simple_comparison_eq(test, op, k, v) |
|
||||||
|
areEqual = true and value = v
|
||||||
|
or
|
||||||
|
areEqual = false and value = v.getDualValue()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
complex_eq(test, op, k, areEqual, value)
|
||||||
|
or
|
||||||
|
/* (x is true => (op == k)) => (!x is false => (op == k)) */
|
||||||
|
exists(AbstractValue dual | value = dual.getDualValue() |
|
||||||
|
compares_eq(test.(LogicalNotInstruction).getUnary(), op, k, areEqual, dual)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
// ((test is `areEqual` => op == const + k2) and const == `k1`) =>
|
||||||
|
// test is `areEqual` => op == k1 + k2
|
||||||
|
exists(int k1, int k2, ConstantInstruction const |
|
||||||
|
compares_eq(test, op, const.getAUse(), k2, areEqual, value) and
|
||||||
|
int_value(const) = k1 and
|
||||||
|
k = k1 + k2
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Rearrange various simple comparisons into `left == right + k` form. */
|
/** Rearrange various simple comparisons into `left == right + k` form. */
|
||||||
private predicate simple_comparison_eq(
|
private predicate simple_comparison_eq(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual
|
CompareInstruction cmp, Operand left, Operand right, int k, AbstractValue value
|
||||||
) {
|
) {
|
||||||
left = cmp.getLeftOperand() and
|
left = cmp.getLeftOperand() and
|
||||||
cmp instanceof CompareEQInstruction and
|
cmp instanceof CompareEQInstruction and
|
||||||
right = cmp.getRightOperand() and
|
right = cmp.getRightOperand() and
|
||||||
k = 0 and
|
k = 0 and
|
||||||
areEqual = true
|
value.(BooleanValue).getValue() = true
|
||||||
or
|
or
|
||||||
left = cmp.getLeftOperand() and
|
left = cmp.getLeftOperand() and
|
||||||
cmp instanceof CompareNEInstruction and
|
cmp instanceof CompareNEInstruction and
|
||||||
right = cmp.getRightOperand() and
|
right = cmp.getRightOperand() and
|
||||||
k = 0 and
|
k = 0 and
|
||||||
areEqual = false
|
value.(BooleanValue).getValue() = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Rearrange various simple comparisons into `op == k` form. */
|
||||||
|
private predicate simple_comparison_eq(Instruction test, Operand op, int k, AbstractValue value) {
|
||||||
|
exists(SwitchInstruction switch, CaseEdge case |
|
||||||
|
test = switch.getExpression() and
|
||||||
|
op.getDef() = test and
|
||||||
|
case = value.(MatchValue).getCase() and
|
||||||
|
exists(switch.getSuccessor(case)) and
|
||||||
|
case.getValue().toInt() = k
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate complex_eq(
|
private predicate complex_eq(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value
|
||||||
) {
|
) {
|
||||||
sub_eq(cmp, left, right, k, areEqual, testIsTrue)
|
sub_eq(cmp, left, right, k, areEqual, value)
|
||||||
or
|
or
|
||||||
add_eq(cmp, left, right, k, areEqual, testIsTrue)
|
add_eq(cmp, left, right, k, areEqual, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate complex_eq(
|
||||||
|
Instruction test, Operand op, int k, boolean areEqual, AbstractValue value
|
||||||
|
) {
|
||||||
|
sub_eq(test, op, k, areEqual, value)
|
||||||
|
or
|
||||||
|
add_eq(test, op, k, areEqual, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -545,31 +815,46 @@ private predicate complex_eq(
|
||||||
|
|
||||||
/** Holds if `left < right + k` evaluates to `isLt` given that test is `testIsTrue`. */
|
/** Holds if `left < right + k` evaluates to `isLt` given that test is `testIsTrue`. */
|
||||||
private predicate compares_lt(
|
private predicate compares_lt(
|
||||||
Instruction test, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
|
Instruction test, Operand left, Operand right, int k, boolean isLt, AbstractValue value
|
||||||
) {
|
) {
|
||||||
/* In the simple case, the test is the comparison, so isLt = testIsTrue */
|
/* In the simple case, the test is the comparison, so isLt = testIsTrue */
|
||||||
simple_comparison_lt(test, left, right, k) and isLt = true and testIsTrue = true
|
simple_comparison_lt(test, left, right, k) and
|
||||||
|
value.(BooleanValue).getValue() = isLt
|
||||||
or
|
or
|
||||||
simple_comparison_lt(test, left, right, k) and isLt = false and testIsTrue = false
|
complex_lt(test, left, right, k, isLt, value)
|
||||||
or
|
|
||||||
complex_lt(test, left, right, k, isLt, testIsTrue)
|
|
||||||
or
|
or
|
||||||
/* (not (left < right + k)) => (left >= right + k) */
|
/* (not (left < right + k)) => (left >= right + k) */
|
||||||
exists(boolean isGe | isLt = isGe.booleanNot() |
|
exists(boolean isGe | isLt = isGe.booleanNot() | compares_ge(test, left, right, k, isGe, value))
|
||||||
compares_ge(test, left, right, k, isGe, testIsTrue)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
/* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */
|
/* (x is true => (left < right + k)) => (!x is false => (left < right + k)) */
|
||||||
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
|
exists(AbstractValue dual | value = dual.getDualValue() |
|
||||||
compares_lt(test.(LogicalNotInstruction).getUnary(), left, right, k, isLt, isFalse)
|
compares_lt(test.(LogicalNotInstruction).getUnary(), left, right, k, isLt, dual)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if `op < k` evaluates to `isLt` given that `test` evaluates to `value`. */
|
||||||
|
private predicate compares_lt(Instruction test, Operand op, int k, boolean isLt, AbstractValue value) {
|
||||||
|
simple_comparison_lt(test, op, k, isLt, value)
|
||||||
|
or
|
||||||
|
complex_lt(test, op, k, isLt, value)
|
||||||
|
or
|
||||||
|
/* (x is true => (op < k)) => (!x is false => (op < k)) */
|
||||||
|
exists(AbstractValue dual | value = dual.getDualValue() |
|
||||||
|
compares_lt(test.(LogicalNotInstruction).getUnary(), op, k, isLt, dual)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(int k1, int k2, ConstantInstruction const |
|
||||||
|
compares_lt(test, op, const.getAUse(), k2, isLt, value) and
|
||||||
|
int_value(const) = k1 and
|
||||||
|
k = k1 + k2
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** `(a < b + k) => (b > a - k) => (b >= a + (1-k))` */
|
/** `(a < b + k) => (b > a - k) => (b >= a + (1-k))` */
|
||||||
private predicate compares_ge(
|
private predicate compares_ge(
|
||||||
Instruction test, Operand left, Operand right, int k, boolean isGe, boolean testIsTrue
|
Instruction test, Operand left, Operand right, int k, boolean isGe, AbstractValue value
|
||||||
) {
|
) {
|
||||||
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, testIsTrue))
|
exists(int onemk | k = 1 - onemk | compares_lt(test, right, left, onemk, isGe, value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Rearrange various simple comparisons into `left < right + k` form. */
|
/** Rearrange various simple comparisons into `left < right + k` form. */
|
||||||
|
@ -595,55 +880,99 @@ private predicate simple_comparison_lt(CompareInstruction cmp, Operand left, Ope
|
||||||
k = 1
|
k = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate complex_lt(
|
/** Rearrange various simple comparisons into `op < k` form. */
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
|
private predicate simple_comparison_lt(
|
||||||
|
Instruction test, Operand op, int k, boolean isLt, AbstractValue value
|
||||||
) {
|
) {
|
||||||
sub_lt(cmp, left, right, k, isLt, testIsTrue)
|
exists(SwitchInstruction switch, CaseEdge case |
|
||||||
|
test = switch.getExpression() and
|
||||||
|
op.getDef() = test and
|
||||||
|
case = value.(MatchValue).getCase() and
|
||||||
|
exists(switch.getSuccessor(case)) and
|
||||||
|
case.getMaxValue() > case.getMinValue()
|
||||||
|
|
|
||||||
|
// op <= k => op < k - 1
|
||||||
|
isLt = true and
|
||||||
|
case.getMaxValue().toInt() = k - 1
|
||||||
|
or
|
||||||
|
isLt = false and
|
||||||
|
case.getMinValue().toInt() = k
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate complex_lt(
|
||||||
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, AbstractValue value
|
||||||
|
) {
|
||||||
|
sub_lt(cmp, left, right, k, isLt, value)
|
||||||
or
|
or
|
||||||
add_lt(cmp, left, right, k, isLt, testIsTrue)
|
add_lt(cmp, left, right, k, isLt, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate complex_lt(
|
||||||
|
Instruction test, Operand left, int k, boolean isLt, AbstractValue value
|
||||||
|
) {
|
||||||
|
sub_lt(test, left, k, isLt, value)
|
||||||
|
or
|
||||||
|
add_lt(test, left, k, isLt, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// left - x < right + c => left < right + (c+x)
|
// left - x < right + c => left < right + (c+x)
|
||||||
// left < (right - x) + c => left < right + (c-x)
|
// left < (right - x) + c => left < right + (c-x)
|
||||||
private predicate sub_lt(
|
private predicate sub_lt(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, AbstractValue value
|
||||||
) {
|
) {
|
||||||
exists(SubInstruction lhs, int c, int x |
|
exists(SubInstruction lhs, int c, int x |
|
||||||
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
|
compares_lt(cmp, lhs.getAUse(), right, c, isLt, value) and
|
||||||
left = lhs.getLeftOperand() and
|
left = lhs.getLeftOperand() and
|
||||||
x = int_value(lhs.getRight()) and
|
x = int_value(lhs.getRight()) and
|
||||||
k = c + x
|
k = c + x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(SubInstruction rhs, int c, int x |
|
exists(SubInstruction rhs, int c, int x |
|
||||||
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
|
compares_lt(cmp, left, rhs.getAUse(), c, isLt, value) and
|
||||||
right = rhs.getLeftOperand() and
|
right = rhs.getLeftOperand() and
|
||||||
x = int_value(rhs.getRight()) and
|
x = int_value(rhs.getRight()) and
|
||||||
k = c - x
|
k = c - x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerSubInstruction lhs, int c, int x |
|
exists(PointerSubInstruction lhs, int c, int x |
|
||||||
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
|
compares_lt(cmp, lhs.getAUse(), right, c, isLt, value) and
|
||||||
left = lhs.getLeftOperand() and
|
left = lhs.getLeftOperand() and
|
||||||
x = int_value(lhs.getRight()) and
|
x = int_value(lhs.getRight()) and
|
||||||
k = c + x
|
k = c + x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerSubInstruction rhs, int c, int x |
|
exists(PointerSubInstruction rhs, int c, int x |
|
||||||
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
|
compares_lt(cmp, left, rhs.getAUse(), c, isLt, value) and
|
||||||
right = rhs.getLeftOperand() and
|
right = rhs.getLeftOperand() and
|
||||||
x = int_value(rhs.getRight()) and
|
x = int_value(rhs.getRight()) and
|
||||||
k = c - x
|
k = c - x
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private predicate sub_lt(Instruction test, Operand left, int k, boolean isLt, AbstractValue value) {
|
||||||
|
exists(SubInstruction lhs, int c, int x |
|
||||||
|
compares_lt(test, lhs.getAUse(), c, isLt, value) and
|
||||||
|
left = lhs.getLeftOperand() and
|
||||||
|
x = int_value(lhs.getRight()) and
|
||||||
|
k = c + x
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(PointerSubInstruction lhs, int c, int x |
|
||||||
|
compares_lt(test, lhs.getAUse(), c, isLt, value) and
|
||||||
|
left = lhs.getLeftOperand() and
|
||||||
|
x = int_value(lhs.getRight()) and
|
||||||
|
k = c + x
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// left + x < right + c => left < right + (c-x)
|
// left + x < right + c => left < right + (c-x)
|
||||||
// left < (right + x) + c => left < right + (c+x)
|
// left < (right + x) + c => left < right + (c+x)
|
||||||
private predicate add_lt(
|
private predicate add_lt(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, boolean testIsTrue
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean isLt, AbstractValue value
|
||||||
) {
|
) {
|
||||||
exists(AddInstruction lhs, int c, int x |
|
exists(AddInstruction lhs, int c, int x |
|
||||||
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
|
compares_lt(cmp, lhs.getAUse(), right, c, isLt, value) and
|
||||||
(
|
(
|
||||||
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
or
|
or
|
||||||
|
@ -653,7 +982,7 @@ private predicate add_lt(
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(AddInstruction rhs, int c, int x |
|
exists(AddInstruction rhs, int c, int x |
|
||||||
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
|
compares_lt(cmp, left, rhs.getAUse(), c, isLt, value) and
|
||||||
(
|
(
|
||||||
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
||||||
or
|
or
|
||||||
|
@ -663,7 +992,7 @@ private predicate add_lt(
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerAddInstruction lhs, int c, int x |
|
exists(PointerAddInstruction lhs, int c, int x |
|
||||||
compares_lt(cmp, lhs.getAUse(), right, c, isLt, testIsTrue) and
|
compares_lt(cmp, lhs.getAUse(), right, c, isLt, value) and
|
||||||
(
|
(
|
||||||
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
or
|
or
|
||||||
|
@ -673,7 +1002,7 @@ private predicate add_lt(
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerAddInstruction rhs, int c, int x |
|
exists(PointerAddInstruction rhs, int c, int x |
|
||||||
compares_lt(cmp, left, rhs.getAUse(), c, isLt, testIsTrue) and
|
compares_lt(cmp, left, rhs.getAUse(), c, isLt, value) and
|
||||||
(
|
(
|
||||||
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
||||||
or
|
or
|
||||||
|
@ -683,47 +1012,86 @@ private predicate add_lt(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private predicate add_lt(Instruction test, Operand left, int k, boolean isLt, AbstractValue value) {
|
||||||
|
exists(AddInstruction lhs, int c, int x |
|
||||||
|
compares_lt(test, lhs.getAUse(), c, isLt, value) and
|
||||||
|
(
|
||||||
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
|
or
|
||||||
|
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
|
||||||
|
) and
|
||||||
|
k = c - x
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(PointerAddInstruction lhs, int c, int x |
|
||||||
|
compares_lt(test, lhs.getAUse(), c, isLt, value) and
|
||||||
|
(
|
||||||
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
|
or
|
||||||
|
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
|
||||||
|
) and
|
||||||
|
k = c - x
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// left - x == right + c => left == right + (c+x)
|
// left - x == right + c => left == right + (c+x)
|
||||||
// left == (right - x) + c => left == right + (c-x)
|
// left == (right - x) + c => left == right + (c-x)
|
||||||
private predicate sub_eq(
|
private predicate sub_eq(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value
|
||||||
) {
|
) {
|
||||||
exists(SubInstruction lhs, int c, int x |
|
exists(SubInstruction lhs, int c, int x |
|
||||||
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
|
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and
|
||||||
left = lhs.getLeftOperand() and
|
left = lhs.getLeftOperand() and
|
||||||
x = int_value(lhs.getRight()) and
|
x = int_value(lhs.getRight()) and
|
||||||
k = c + x
|
k = c + x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(SubInstruction rhs, int c, int x |
|
exists(SubInstruction rhs, int c, int x |
|
||||||
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
|
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and
|
||||||
right = rhs.getLeftOperand() and
|
right = rhs.getLeftOperand() and
|
||||||
x = int_value(rhs.getRight()) and
|
x = int_value(rhs.getRight()) and
|
||||||
k = c - x
|
k = c - x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerSubInstruction lhs, int c, int x |
|
exists(PointerSubInstruction lhs, int c, int x |
|
||||||
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
|
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and
|
||||||
left = lhs.getLeftOperand() and
|
left = lhs.getLeftOperand() and
|
||||||
x = int_value(lhs.getRight()) and
|
x = int_value(lhs.getRight()) and
|
||||||
k = c + x
|
k = c + x
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerSubInstruction rhs, int c, int x |
|
exists(PointerSubInstruction rhs, int c, int x |
|
||||||
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
|
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and
|
||||||
right = rhs.getLeftOperand() and
|
right = rhs.getLeftOperand() and
|
||||||
x = int_value(rhs.getRight()) and
|
x = int_value(rhs.getRight()) and
|
||||||
k = c - x
|
k = c - x
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// op - x == c => op == (c+x)
|
||||||
|
private predicate sub_eq(Instruction test, Operand op, int k, boolean areEqual, AbstractValue value) {
|
||||||
|
exists(SubInstruction sub, int c, int x |
|
||||||
|
compares_eq(test, sub.getAUse(), c, areEqual, value) and
|
||||||
|
op = sub.getLeftOperand() and
|
||||||
|
x = int_value(sub.getRight()) and
|
||||||
|
k = c + x
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(PointerSubInstruction sub, int c, int x |
|
||||||
|
compares_eq(test, sub.getAUse(), c, areEqual, value) and
|
||||||
|
op = sub.getLeftOperand() and
|
||||||
|
x = int_value(sub.getRight()) and
|
||||||
|
k = c + x
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// left + x == right + c => left == right + (c-x)
|
// left + x == right + c => left == right + (c-x)
|
||||||
// left == (right + x) + c => left == right + (c+x)
|
// left == (right + x) + c => left == right + (c+x)
|
||||||
private predicate add_eq(
|
private predicate add_eq(
|
||||||
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, boolean testIsTrue
|
CompareInstruction cmp, Operand left, Operand right, int k, boolean areEqual, AbstractValue value
|
||||||
) {
|
) {
|
||||||
exists(AddInstruction lhs, int c, int x |
|
exists(AddInstruction lhs, int c, int x |
|
||||||
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
|
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and
|
||||||
(
|
(
|
||||||
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
or
|
or
|
||||||
|
@ -733,7 +1101,7 @@ private predicate add_eq(
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(AddInstruction rhs, int c, int x |
|
exists(AddInstruction rhs, int c, int x |
|
||||||
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
|
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and
|
||||||
(
|
(
|
||||||
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
||||||
or
|
or
|
||||||
|
@ -743,7 +1111,7 @@ private predicate add_eq(
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerAddInstruction lhs, int c, int x |
|
exists(PointerAddInstruction lhs, int c, int x |
|
||||||
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, testIsTrue) and
|
compares_eq(cmp, lhs.getAUse(), right, c, areEqual, value) and
|
||||||
(
|
(
|
||||||
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
or
|
or
|
||||||
|
@ -753,7 +1121,7 @@ private predicate add_eq(
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(PointerAddInstruction rhs, int c, int x |
|
exists(PointerAddInstruction rhs, int c, int x |
|
||||||
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, testIsTrue) and
|
compares_eq(cmp, left, rhs.getAUse(), c, areEqual, value) and
|
||||||
(
|
(
|
||||||
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
right = rhs.getLeftOperand() and x = int_value(rhs.getRight())
|
||||||
or
|
or
|
||||||
|
@ -763,5 +1131,30 @@ private predicate add_eq(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// left + x == right + c => left == right + (c-x)
|
||||||
|
private predicate add_eq(
|
||||||
|
Instruction test, Operand left, int k, boolean areEqual, AbstractValue value
|
||||||
|
) {
|
||||||
|
exists(AddInstruction lhs, int c, int x |
|
||||||
|
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
|
||||||
|
(
|
||||||
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
|
or
|
||||||
|
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
|
||||||
|
) and
|
||||||
|
k = c - x
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(PointerAddInstruction lhs, int c, int x |
|
||||||
|
compares_eq(test, lhs.getAUse(), c, areEqual, value) and
|
||||||
|
(
|
||||||
|
left = lhs.getLeftOperand() and x = int_value(lhs.getRight())
|
||||||
|
or
|
||||||
|
left = lhs.getRightOperand() and x = int_value(lhs.getLeft())
|
||||||
|
) and
|
||||||
|
k = c - x
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/** The int value of integer constant expression. */
|
/** The int value of integer constant expression. */
|
||||||
private int int_value(Instruction i) { result = i.(IntegerConstantInstruction).getValue().toInt() }
|
private int int_value(Instruction i) { result = i.(IntegerConstantInstruction).getValue().toInt() }
|
||||||
|
|
|
@ -28,6 +28,6 @@ import cpp
|
||||||
deprecated module DataFlow {
|
deprecated module DataFlow {
|
||||||
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
import DataFlowMake<CppOldDataFlow>
|
import DataFlowMake<Location, CppOldDataFlow>
|
||||||
import semmle.code.cpp.dataflow.internal.DataFlowImpl1
|
import semmle.code.cpp.dataflow.internal.DataFlowImpl1
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,6 @@ deprecated module TaintTracking {
|
||||||
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
|
||||||
private import semmle.code.cpp.dataflow.internal.TaintTrackingImplSpecific
|
private import semmle.code.cpp.dataflow.internal.TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.TaintTracking
|
private import codeql.dataflow.TaintTracking
|
||||||
import TaintFlowMake<CppOldDataFlow, CppOldTaintTracking>
|
import TaintFlowMake<Location, CppOldDataFlow, CppOldTaintTracking>
|
||||||
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
|
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImpl
|
private import codeql.dataflow.internal.DataFlowImpl
|
||||||
import MakeImpl<CppOldDataFlow>
|
import MakeImpl<Location, CppOldDataFlow>
|
||||||
|
|
|
@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|
|
@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|
|
@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|
|
@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImplCommon
|
private import codeql.dataflow.internal.DataFlowImplCommon
|
||||||
import MakeImplCommon<CppOldDataFlow>
|
import MakeImplCommon<Location, CppOldDataFlow>
|
||||||
|
|
|
@ -10,7 +10,7 @@ private import DataFlowImplSpecific
|
||||||
private import TaintTrackingImplSpecific
|
private import TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImplConsistency
|
private import codeql.dataflow.internal.DataFlowImplConsistency
|
||||||
|
|
||||||
private module Input implements InputSig<CppOldDataFlow> {
|
private module Input implements InputSig<Location, CppOldDataFlow> {
|
||||||
predicate argHasPostUpdateExclude(Private::ArgumentNode n) {
|
predicate argHasPostUpdateExclude(Private::ArgumentNode n) {
|
||||||
// Is the null pointer (or something that's not really a pointer)
|
// Is the null pointer (or something that's not really a pointer)
|
||||||
exists(n.asExpr().getValue())
|
exists(n.asExpr().getValue())
|
||||||
|
@ -26,4 +26,4 @@ private module Input implements InputSig<CppOldDataFlow> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module Consistency = MakeConsistency<CppOldDataFlow, CppOldTaintTracking, Input>;
|
module Consistency = MakeConsistency<Location, CppOldDataFlow, CppOldTaintTracking, Input>;
|
||||||
|
|
|
@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* Provides C++-specific definitions for use in the data flow library.
|
* Provides C++-specific definitions for use in the data flow library.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
|
|
||||||
module Private {
|
module Private {
|
||||||
|
@ -15,7 +16,7 @@ module Public {
|
||||||
import DataFlowUtil
|
import DataFlowUtil
|
||||||
}
|
}
|
||||||
|
|
||||||
module CppOldDataFlow implements InputSig {
|
module CppOldDataFlow implements InputSig<Location> {
|
||||||
import Private
|
import Private
|
||||||
import Public
|
import Public
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ class Node extends TNode {
|
||||||
* For more information, see
|
* For more information, see
|
||||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||||
*/
|
*/
|
||||||
predicate hasLocationInfo(
|
deprecated predicate hasLocationInfo(
|
||||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||||
) {
|
) {
|
||||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
* Provides C++-specific definitions for use in the taint tracking library.
|
* Provides C++-specific definitions for use in the taint tracking library.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
private import codeql.dataflow.TaintTracking
|
private import codeql.dataflow.TaintTracking
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
|
|
||||||
module CppOldTaintTracking implements InputSig<CppOldDataFlow> {
|
module CppOldTaintTracking implements InputSig<Location, CppOldDataFlow> {
|
||||||
import TaintTrackingUtil
|
import TaintTrackingUtil
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,6 @@ import cpp
|
||||||
module DataFlow {
|
module DataFlow {
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
import DataFlowMake<CppDataFlow>
|
import DataFlowMake<Location, CppDataFlow>
|
||||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
|
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ module TaintTracking {
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.TaintTracking
|
private import codeql.dataflow.TaintTracking
|
||||||
import TaintFlowMake<CppDataFlow, CppTaintTracking>
|
private import semmle.code.cpp.Location
|
||||||
|
import TaintFlowMake<Location, CppDataFlow, CppTaintTracking>
|
||||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||||
}
|
}
|
||||||
|
|
|
@ -1322,3 +1322,23 @@ class CoYieldExpr extends UnaryOperation, @co_yield {
|
||||||
|
|
||||||
override int getPrecedence() { result = 2 }
|
override int getPrecedence() { result = 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An expression representing the re-use of another expression.
|
||||||
|
*
|
||||||
|
* In some specific cases an expression may be referred to outside its
|
||||||
|
* original context. A re-use expression wraps any such reference. A
|
||||||
|
* re-use expression can for example occur as the qualifier of an implicit
|
||||||
|
* destructor called on a temporary object, where the original use of the
|
||||||
|
* expression is in the definition of the temporary.
|
||||||
|
*/
|
||||||
|
class ReuseExpr extends Expr, @reuseexpr {
|
||||||
|
override string getAPrimaryQlClass() { result = "ReuseExpr" }
|
||||||
|
|
||||||
|
override string toString() { result = "reuse of " + this.getReusedExpr().toString() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the expression that is being re-used.
|
||||||
|
*/
|
||||||
|
Expr getReusedExpr() { expr_reuse(underlyingElement(this), unresolveElement(result)) }
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,6 @@ import cpp
|
||||||
module DataFlow {
|
module DataFlow {
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
import DataFlowMake<CppDataFlow>
|
import DataFlowMake<Location, CppDataFlow>
|
||||||
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
|
import semmle.code.cpp.ir.dataflow.internal.DataFlowImpl1
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,6 @@ module TaintTracking {
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
private import semmle.code.cpp.ir.dataflow.internal.TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.TaintTracking
|
private import codeql.dataflow.TaintTracking
|
||||||
import TaintFlowMake<CppDataFlow, CppTaintTracking>
|
import TaintFlowMake<Location, CppDataFlow, CppTaintTracking>
|
||||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImpl
|
private import codeql.dataflow.internal.DataFlowImpl
|
||||||
import MakeImpl<CppDataFlow>
|
import MakeImpl<Location, CppDataFlow>
|
||||||
|
|
|
@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|
|
@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|
|
@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|
|
@ -285,6 +285,8 @@ deprecated private module Config implements FullStateConfigSig {
|
||||||
|
|
||||||
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
int fieldFlowBranchLimit() { result = min(any(Configuration config).fieldFlowBranchLimit()) }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 5 }
|
||||||
|
|
||||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||||
|
|
||||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImplCommon
|
private import codeql.dataflow.internal.DataFlowImplCommon
|
||||||
import MakeImplCommon<CppDataFlow>
|
import MakeImplCommon<Location, CppDataFlow>
|
||||||
|
|
|
@ -8,7 +8,7 @@ private import DataFlowImplSpecific
|
||||||
private import TaintTrackingImplSpecific
|
private import TaintTrackingImplSpecific
|
||||||
private import codeql.dataflow.internal.DataFlowImplConsistency
|
private import codeql.dataflow.internal.DataFlowImplConsistency
|
||||||
|
|
||||||
private module Input implements InputSig<CppDataFlow> {
|
private module Input implements InputSig<Location, CppDataFlow> {
|
||||||
predicate argHasPostUpdateExclude(Private::ArgumentNode n) {
|
predicate argHasPostUpdateExclude(Private::ArgumentNode n) {
|
||||||
// The rules for whether an IR argument gets a post-update node are too
|
// The rules for whether an IR argument gets a post-update node are too
|
||||||
// complex to model here.
|
// complex to model here.
|
||||||
|
@ -16,4 +16,4 @@ private module Input implements InputSig<CppDataFlow> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module Consistency = MakeConsistency<CppDataFlow, CppTaintTracking, Input>;
|
module Consistency = MakeConsistency<Location, CppDataFlow, CppTaintTracking, Input>;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private import codeql.dataflow.DataFlow
|
private import codeql.dataflow.DataFlow
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
|
|
||||||
module Private {
|
module Private {
|
||||||
import DataFlowPrivate
|
import DataFlowPrivate
|
||||||
|
@ -13,7 +14,7 @@ module Public {
|
||||||
import DataFlowUtil
|
import DataFlowUtil
|
||||||
}
|
}
|
||||||
|
|
||||||
module CppDataFlow implements InputSig {
|
module CppDataFlow implements InputSig<Location> {
|
||||||
import Private
|
import Private
|
||||||
import Public
|
import Public
|
||||||
|
|
||||||
|
|
|
@ -448,7 +448,7 @@ class Node extends TIRDataFlowNode {
|
||||||
* For more information, see
|
* For more information, see
|
||||||
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
* [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
|
||||||
*/
|
*/
|
||||||
predicate hasLocationInfo(
|
deprecated predicate hasLocationInfo(
|
||||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||||
) {
|
) {
|
||||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||||
|
@ -1331,6 +1331,7 @@ private import GetConvertedResultExpression
|
||||||
|
|
||||||
/** Holds if `node` is an `OperandNode` that should map `node.asExpr()` to `e`. */
|
/** Holds if `node` is an `OperandNode` that should map `node.asExpr()` to `e`. */
|
||||||
predicate exprNodeShouldBeOperand(OperandNode node, Expr e, int n) {
|
predicate exprNodeShouldBeOperand(OperandNode node, Expr e, int n) {
|
||||||
|
not exprNodeShouldBeIndirectOperand(_, e, n) and
|
||||||
exists(Instruction def |
|
exists(Instruction def |
|
||||||
unique( | | getAUse(def)) = node.getOperand() and
|
unique( | | getAUse(def)) = node.getOperand() and
|
||||||
e = getConvertedResultExpression(def, n)
|
e = getConvertedResultExpression(def, n)
|
||||||
|
@ -1347,6 +1348,22 @@ private predicate indirectExprNodeShouldBeIndirectOperand(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Holds if `node` should be an `IndirectOperand` that maps `node.asExpr()` to `e`. */
|
||||||
|
private predicate exprNodeShouldBeIndirectOperand(IndirectOperand node, Expr e, int n) {
|
||||||
|
exists(ArgumentOperand operand |
|
||||||
|
// When an argument (qualifier or positional) is a prvalue and the
|
||||||
|
// parameter (qualifier or positional) is a (const) reference, IR
|
||||||
|
// construction introduces a temporary `IRVariable`. The `VariableAddress`
|
||||||
|
// instruction has the argument as its `getConvertedResultExpression`
|
||||||
|
// result. However, the instruction actually represents the _address_ of
|
||||||
|
// the argument. So to fix this mismatch, we have the indirection of the
|
||||||
|
// `VariableAddressInstruction` map to the expression.
|
||||||
|
node.hasOperandAndIndirectionIndex(operand, 1) and
|
||||||
|
e = getConvertedResultExpression(operand.getDef(), n) and
|
||||||
|
operand.getDef().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node, Expr e, int n) {
|
private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node, Expr e, int n) {
|
||||||
exists(CallInstruction call |
|
exists(CallInstruction call |
|
||||||
call.getStaticCallTarget() instanceof Constructor and
|
call.getStaticCallTarget() instanceof Constructor and
|
||||||
|
@ -1359,6 +1376,7 @@ private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node,
|
||||||
predicate exprNodeShouldBeInstruction(Node node, Expr e, int n) {
|
predicate exprNodeShouldBeInstruction(Node node, Expr e, int n) {
|
||||||
not exprNodeShouldBeOperand(_, e, n) and
|
not exprNodeShouldBeOperand(_, e, n) and
|
||||||
not exprNodeShouldBeIndirectOutNode(_, e, n) and
|
not exprNodeShouldBeIndirectOutNode(_, e, n) and
|
||||||
|
not exprNodeShouldBeIndirectOperand(_, e, n) and
|
||||||
e = getConvertedResultExpression(node.asInstruction(), n)
|
e = getConvertedResultExpression(node.asInstruction(), n)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1391,7 +1409,8 @@ abstract private class ExprNodeBase extends Node {
|
||||||
private predicate exprNodeShouldBe(Expr e, int n) {
|
private predicate exprNodeShouldBe(Expr e, int n) {
|
||||||
exprNodeShouldBeInstruction(_, e, n) or
|
exprNodeShouldBeInstruction(_, e, n) or
|
||||||
exprNodeShouldBeOperand(_, e, n) or
|
exprNodeShouldBeOperand(_, e, n) or
|
||||||
exprNodeShouldBeIndirectOutNode(_, e, n)
|
exprNodeShouldBeIndirectOutNode(_, e, n) or
|
||||||
|
exprNodeShouldBeIndirectOperand(_, e, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InstructionExprNode extends ExprNodeBase, InstructionNode {
|
private class InstructionExprNode extends ExprNodeBase, InstructionNode {
|
||||||
|
@ -1533,6 +1552,12 @@ private class IndirectArgumentOutExprNode extends ExprNodeBase, IndirectArgument
|
||||||
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOutNode(this, result, n) }
|
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOutNode(this, result, n) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class IndirectOperandExprNode extends ExprNodeBase instanceof IndirectOperand {
|
||||||
|
IndirectOperandExprNode() { exprNodeShouldBeIndirectOperand(this, _, _) }
|
||||||
|
|
||||||
|
final override Expr getConvertedExpr(int n) { exprNodeShouldBeIndirectOperand(this, result, n) }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An expression, viewed as a node in a data flow graph.
|
* An expression, viewed as a node in a data flow graph.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
|
|
||||||
private import codeql.dataflow.TaintTracking
|
private import codeql.dataflow.TaintTracking
|
||||||
private import DataFlowImplSpecific
|
private import DataFlowImplSpecific
|
||||||
|
private import semmle.code.cpp.Location
|
||||||
|
|
||||||
module CppTaintTracking implements InputSig<CppDataFlow> {
|
module CppTaintTracking implements InputSig<Location, CppDataFlow> {
|
||||||
import TaintTrackingUtil
|
import TaintTrackingUtil
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,6 +90,15 @@ class CaseEdge extends EdgeKind, TCaseEdge {
|
||||||
* Gets the largest value of the switch expression for which control will flow along this edge.
|
* Gets the largest value of the switch expression for which control will flow along this edge.
|
||||||
*/
|
*/
|
||||||
final string getMaxValue() { result = maxValue }
|
final string getMaxValue() { result = maxValue }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the unique value of the switch expression for which control will
|
||||||
|
* flow along this edge, if any.
|
||||||
|
*/
|
||||||
|
final string getValue() {
|
||||||
|
minValue = maxValue and
|
||||||
|
result = minValue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -38,6 +38,12 @@ private int getBinaryInstructionValue(BinaryInstruction instr) {
|
||||||
or
|
or
|
||||||
instr instanceof DivInstruction and result = div(left, right)
|
instr instanceof DivInstruction and result = div(left, right)
|
||||||
or
|
or
|
||||||
|
instr instanceof BitOrInstruction and result = bitOr(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitAndInstruction and result = bitAnd(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitXorInstruction and result = bitXor(left, right)
|
||||||
|
or
|
||||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||||
or
|
or
|
||||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||||
|
|
|
@ -38,6 +38,12 @@ private int getBinaryInstructionValue(BinaryInstruction instr) {
|
||||||
or
|
or
|
||||||
instr instanceof DivInstruction and result = div(left, right)
|
instr instanceof DivInstruction and result = div(left, right)
|
||||||
or
|
or
|
||||||
|
instr instanceof BitOrInstruction and result = bitOr(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitAndInstruction and result = bitAnd(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitXorInstruction and result = bitXor(left, right)
|
||||||
|
or
|
||||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||||
or
|
or
|
||||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||||
|
|
|
@ -40,12 +40,43 @@ IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) {
|
||||||
result.getTag() = tag
|
result.getTag() = tag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets an operand of `op`. */
|
||||||
|
private Expr getAnOperand(Operation op) { result = op.getAnOperand() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of nested operands of `op`. For example,
|
||||||
|
* `getNumberOfNestedBinaryOperands((1 + 2) + 3))` is `3`.
|
||||||
|
*/
|
||||||
|
private int getNumberOfNestedBinaryOperands(Operation op) { result = count(getAnOperand*(op)) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `op` should not be translated to a `ConstantInstruction` as part of
|
||||||
|
* IR generation, even if the value of `op` is constant.
|
||||||
|
*/
|
||||||
|
private predicate ignoreConstantValue(Operation op) {
|
||||||
|
op instanceof BitwiseAndExpr
|
||||||
|
or
|
||||||
|
op instanceof BitwiseOrExpr
|
||||||
|
or
|
||||||
|
op instanceof BitwiseXorExpr
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `expr` is a constant of a type that can be replaced directly with
|
* Holds if `expr` is a constant of a type that can be replaced directly with
|
||||||
* its value in the IR. This does not include address constants as we have no
|
* its value in the IR. This does not include address constants as we have no
|
||||||
* means to express those as QL values.
|
* means to express those as QL values.
|
||||||
*/
|
*/
|
||||||
predicate isIRConstant(Expr expr) { exists(expr.getValue()) }
|
predicate isIRConstant(Expr expr) {
|
||||||
|
exists(expr.getValue()) and
|
||||||
|
// We avoid constant folding certain operations since it's often useful to
|
||||||
|
// mark one of those as a source in dataflow, and if the operation is
|
||||||
|
// constant folded it's not possible to mark its operands as a source (or
|
||||||
|
// sink).
|
||||||
|
// But to avoid creating an outrageous amount of IR from very large
|
||||||
|
// constant expressions we fall back to constant folding if the operation
|
||||||
|
// has more than 50 operands (i.e., 1 + 2 + 3 + 4 + ... + 50)
|
||||||
|
if ignoreConstantValue(expr) then getNumberOfNestedBinaryOperands(expr) > 50 else any()
|
||||||
|
}
|
||||||
|
|
||||||
// Pulled out for performance. See
|
// Pulled out for performance. See
|
||||||
// https://github.com/github/codeql-coreql-team/issues/1044.
|
// https://github.com/github/codeql-coreql-team/issues/1044.
|
||||||
|
@ -99,6 +130,11 @@ private predicate ignoreExprAndDescendants(Expr expr) {
|
||||||
or
|
or
|
||||||
// suppress destructors of temporary variables until proper support is added for them.
|
// suppress destructors of temporary variables until proper support is added for them.
|
||||||
exists(Expr parent | parent.getAnImplicitDestructorCall() = expr)
|
exists(Expr parent | parent.getAnImplicitDestructorCall() = expr)
|
||||||
|
or
|
||||||
|
exists(Stmt parent |
|
||||||
|
parent.getAnImplicitDestructorCall() = expr and
|
||||||
|
expr.(DestructorCall).getQualifier() instanceof ReuseExpr
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -248,9 +248,19 @@ abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt {
|
||||||
final override TranslatedElement getChild(int id) {
|
final override TranslatedElement getChild(int id) {
|
||||||
result = this.getChildInternal(id)
|
result = this.getChildInternal(id)
|
||||||
or
|
or
|
||||||
exists(int destructorIndex |
|
exists(int destructorIndex, int tempDestructorCount |
|
||||||
result.(TranslatedExpr).getExpr() = stmt.getImplicitDestructorCall(destructorIndex) and
|
result.(TranslatedExpr).getExpr() = stmt.getImplicitDestructorCall(destructorIndex) and
|
||||||
id = this.getFirstDestructorCallIndex() + destructorIndex
|
id = this.getFirstDestructorCallIndex() + destructorIndex - tempDestructorCount and
|
||||||
|
// suppress destructors of temporary variables until proper support is added for them.
|
||||||
|
tempDestructorCount =
|
||||||
|
count(DestructorCall call, int tempIndex |
|
||||||
|
stmt.getImplicitDestructorCall(tempIndex) = call and
|
||||||
|
tempIndex < destructorIndex and
|
||||||
|
call.getQualifier() instanceof ReuseExpr
|
||||||
|
|
|
||||||
|
call
|
||||||
|
) and
|
||||||
|
not stmt.getImplicitDestructorCall(destructorIndex).getQualifier() instanceof ReuseExpr
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +271,11 @@ abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt {
|
||||||
}
|
}
|
||||||
|
|
||||||
final override predicate hasAnImplicitDestructorCall() {
|
final override predicate hasAnImplicitDestructorCall() {
|
||||||
exists(stmt.getAnImplicitDestructorCall())
|
exists(stmt.getAnImplicitDestructorCall()) and
|
||||||
|
// suppress destructors of temporary variables until proper support is added for them.
|
||||||
|
exists(Expr expr | stmt.getAnImplicitDestructorCall().getQualifier() = expr |
|
||||||
|
not expr instanceof ReuseExpr
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
final override string toString() { result = stmt.toString() }
|
final override string toString() { result = stmt.toString() }
|
||||||
|
|
|
@ -38,6 +38,12 @@ private int getBinaryInstructionValue(BinaryInstruction instr) {
|
||||||
or
|
or
|
||||||
instr instanceof DivInstruction and result = div(left, right)
|
instr instanceof DivInstruction and result = div(left, right)
|
||||||
or
|
or
|
||||||
|
instr instanceof BitOrInstruction and result = bitOr(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitAndInstruction and result = bitAnd(left, right)
|
||||||
|
or
|
||||||
|
instr instanceof BitXorInstruction and result = bitXor(left, right)
|
||||||
|
or
|
||||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||||
or
|
or
|
||||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||||
|
|
|
@ -89,6 +89,18 @@ int compareLE(int a, int b) { if a <= b then result = 1 else result = 0 }
|
||||||
bindingset[a, b]
|
bindingset[a, b]
|
||||||
int compareGE(int a, int b) { if a >= b then result = 1 else result = 0 }
|
int compareGE(int a, int b) { if a >= b then result = 1 else result = 0 }
|
||||||
|
|
||||||
|
/** Returns `a | b`. */
|
||||||
|
bindingset[a, b]
|
||||||
|
int bitOr(int a, int b) { result = a.bitOr(b) }
|
||||||
|
|
||||||
|
/** Returns `a & b`. */
|
||||||
|
bindingset[a, b]
|
||||||
|
int bitAnd(int a, int b) { result = a.bitAnd(b) }
|
||||||
|
|
||||||
|
/** Returns `a ^ b`. */
|
||||||
|
bindingset[a, b]
|
||||||
|
int bitXor(int a, int b) { result = a.bitXor(b) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns `-a`. If the negation would overflow, there is no result.
|
* Returns `-a`. If the negation would overflow, there is no result.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -36,7 +36,9 @@ private class MallocAllocationFunction extends AllocationFunction {
|
||||||
"CRYPTO_malloc", // CRYPTO_malloc(size_t num, const char *file, int line)
|
"CRYPTO_malloc", // CRYPTO_malloc(size_t num, const char *file, int line)
|
||||||
"CRYPTO_zalloc", // CRYPTO_zalloc(size_t num, const char *file, int line)
|
"CRYPTO_zalloc", // CRYPTO_zalloc(size_t num, const char *file, int line)
|
||||||
"CRYPTO_secure_malloc", // CRYPTO_secure_malloc(size_t num, const char *file, int line)
|
"CRYPTO_secure_malloc", // CRYPTO_secure_malloc(size_t num, const char *file, int line)
|
||||||
"CRYPTO_secure_zalloc" // CRYPTO_secure_zalloc(size_t num, const char *file, int line)
|
"CRYPTO_secure_zalloc", // CRYPTO_secure_zalloc(size_t num, const char *file, int line)
|
||||||
|
"g_malloc", // g_malloc (n_bytes);
|
||||||
|
"g_try_malloc" // g_try_malloc(n_bytes);
|
||||||
]) and
|
]) and
|
||||||
sizeArg = 0
|
sizeArg = 0
|
||||||
or
|
or
|
||||||
|
@ -139,7 +141,9 @@ private class ReallocAllocationFunction extends AllocationFunction, TaintFunctio
|
||||||
// --- Windows COM allocation
|
// --- Windows COM allocation
|
||||||
"CoTaskMemRealloc", // CoTaskMemRealloc(ptr, size)
|
"CoTaskMemRealloc", // CoTaskMemRealloc(ptr, size)
|
||||||
// --- OpenSSL memory allocation
|
// --- OpenSSL memory allocation
|
||||||
"CRYPTO_realloc" // CRYPTO_realloc(void *addr, size_t num, const char *file, int line)
|
"CRYPTO_realloc", // CRYPTO_realloc(void *addr, size_t num, const char *file, int line)
|
||||||
|
"g_realloc", // g_realloc(mem, n_bytes);
|
||||||
|
"g_try_realloc" // g_try_realloc(mem, n_bytes);
|
||||||
]) and
|
]) and
|
||||||
sizeArg = 1 and
|
sizeArg = 1 and
|
||||||
reallocArg = 0
|
reallocArg = 0
|
||||||
|
|
|
@ -20,8 +20,10 @@ private class StandardDeallocationFunction extends DeallocationFunction {
|
||||||
freedArg = 0
|
freedArg = 0
|
||||||
or
|
or
|
||||||
this.hasGlobalName([
|
this.hasGlobalName([
|
||||||
// --- OpenSSL memory allocation
|
// --- OpenSSL memory deallocation
|
||||||
"CRYPTO_free", "CRYPTO_secure_free"
|
"CRYPTO_free", "CRYPTO_secure_free",
|
||||||
|
// --- glib memory deallocation
|
||||||
|
"g_free"
|
||||||
]) and
|
]) and
|
||||||
freedArg = 0
|
freedArg = 0
|
||||||
or
|
or
|
||||||
|
|
|
@ -9,6 +9,8 @@ import cpp
|
||||||
import semmle.code.cpp.models.interfaces.Taint
|
import semmle.code.cpp.models.interfaces.Taint
|
||||||
import semmle.code.cpp.models.interfaces.DataFlow
|
import semmle.code.cpp.models.interfaces.DataFlow
|
||||||
import semmle.code.cpp.models.interfaces.Iterator
|
import semmle.code.cpp.models.interfaces.Iterator
|
||||||
|
import semmle.code.cpp.models.interfaces.Alias
|
||||||
|
import semmle.code.cpp.models.interfaces.SideEffect
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instantiation of the `std::iterator_traits` template.
|
* An instantiation of the `std::iterator_traits` template.
|
||||||
|
@ -438,7 +440,7 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe
|
||||||
* A `begin` or `end` member function, or a related member function, that
|
* A `begin` or `end` member function, or a related member function, that
|
||||||
* returns an iterator.
|
* returns an iterator.
|
||||||
*/
|
*/
|
||||||
private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction {
|
class BeginOrEndFunction extends MemberFunction {
|
||||||
BeginOrEndFunction() {
|
BeginOrEndFunction() {
|
||||||
this.hasName([
|
this.hasName([
|
||||||
"begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin",
|
"begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin",
|
||||||
|
@ -446,7 +448,11 @@ private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetItera
|
||||||
]) and
|
]) and
|
||||||
this.getType().getUnspecifiedType() instanceof Iterator
|
this.getType().getUnspecifiedType() instanceof Iterator
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BeginOrEndFunctionModels extends BeginOrEndFunction, TaintFunction,
|
||||||
|
GetIteratorFunction, AliasFunction, SideEffectFunction
|
||||||
|
{
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
input.isQualifierObject() and
|
input.isQualifierObject() and
|
||||||
output.isReturnValue()
|
output.isReturnValue()
|
||||||
|
@ -456,6 +462,22 @@ private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetItera
|
||||||
input.isQualifierObject() and
|
input.isQualifierObject() and
|
||||||
output.isReturnValue()
|
output.isReturnValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override predicate parameterNeverEscapes(int index) { index = -1 }
|
||||||
|
|
||||||
|
override predicate parameterEscapesOnlyViaReturn(int index) { none() }
|
||||||
|
|
||||||
|
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||||
|
|
||||||
|
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||||
|
|
||||||
|
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||||
|
none()
|
||||||
|
}
|
||||||
|
|
||||||
|
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||||
|
i = -1 and buffer = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -253,13 +253,15 @@ private class StdSequenceContainerAssign extends TaintFunction {
|
||||||
/**
|
/**
|
||||||
* The standard container functions `at` and `operator[]`.
|
* The standard container functions `at` and `operator[]`.
|
||||||
*/
|
*/
|
||||||
private class StdSequenceContainerAt extends TaintFunction {
|
class StdSequenceContainerAt extends MemberFunction {
|
||||||
StdSequenceContainerAt() {
|
StdSequenceContainerAt() {
|
||||||
this.getClassAndName(["at", "operator[]"]) instanceof Array or
|
this.getClassAndName(["at", "operator[]"]) instanceof Array or
|
||||||
this.getClassAndName(["at", "operator[]"]) instanceof Deque or
|
this.getClassAndName(["at", "operator[]"]) instanceof Deque or
|
||||||
this.getClassAndName(["at", "operator[]"]) instanceof Vector
|
this.getClassAndName(["at", "operator[]"]) instanceof Vector
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StdSequenceContainerAtModel extends StdSequenceContainerAt, TaintFunction {
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
// flow from qualifier to referenced return value
|
// flow from qualifier to referenced return value
|
||||||
input.isQualifierObject() and
|
input.isQualifierObject() and
|
||||||
|
|
|
@ -129,9 +129,11 @@ private class StdMapMerge extends TaintFunction {
|
||||||
/**
|
/**
|
||||||
* The standard map functions `at` and `operator[]`.
|
* The standard map functions `at` and `operator[]`.
|
||||||
*/
|
*/
|
||||||
private class StdMapAt extends TaintFunction {
|
class StdMapAt extends MemberFunction {
|
||||||
StdMapAt() { this.getClassAndName(["at", "operator[]"]) instanceof MapOrUnorderedMap }
|
StdMapAt() { this.getClassAndName(["at", "operator[]"]) instanceof MapOrUnorderedMap }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class StdMapAtModels extends StdMapAt, TaintFunction {
|
||||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||||
// flow from qualifier to referenced return value
|
// flow from qualifier to referenced return value
|
||||||
input.isQualifierObject() and
|
input.isQualifierObject() and
|
||||||
|
|
|
@ -1513,6 +1513,11 @@ exprs(
|
||||||
int location: @location_expr ref
|
int location: @location_expr ref
|
||||||
);
|
);
|
||||||
|
|
||||||
|
expr_reuse(
|
||||||
|
int reuse: @expr ref,
|
||||||
|
int original: @expr ref
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
case @value.category of
|
case @value.category of
|
||||||
1 = prval
|
1 = prval
|
||||||
|
@ -1741,6 +1746,7 @@ case @expr.kind of
|
||||||
| 360 = @isunsigned
|
| 360 = @isunsigned
|
||||||
| 361 = @isvoid
|
| 361 = @isvoid
|
||||||
| 362 = @isvolatile
|
| 362 = @isvolatile
|
||||||
|
| 363 = @reuseexpr
|
||||||
;
|
;
|
||||||
|
|
||||||
@var_args_expr = @vastartexpr
|
@var_args_expr = @vastartexpr
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,2 @@
|
||||||
|
description: Introduce re-use expressions
|
||||||
|
compatibility: backwards
|
|
@ -1,3 +1,14 @@
|
||||||
|
## 0.9.7
|
||||||
|
|
||||||
|
No user-facing changes.
|
||||||
|
|
||||||
|
## 0.9.6
|
||||||
|
|
||||||
|
### Minor Analysis Improvements
|
||||||
|
|
||||||
|
* The "non-constant format string" query (`cpp/non-constant-format`) has been converted to a `path-problem` query.
|
||||||
|
* The new C/C++ dataflow and taint-tracking libraries (`semmle.code.cpp.dataflow.new.DataFlow` and `semmle.code.cpp.dataflow.new.TaintTracking`) now implicitly assume that dataflow and taint modelled via `DataFlowFunction` and `TaintFunction` always fully overwrite their buffers and thus act as flow barriers. As a result, many dataflow and taint-tracking queries now produce fewer false positives. To remove this assumption and go back to the previous behavior for a given model, one can override the new `isPartialWrite` predicate.
|
||||||
|
|
||||||
## 0.9.5
|
## 0.9.5
|
||||||
|
|
||||||
### Minor Analysis Improvements
|
### Minor Analysis Improvements
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* @name Missing return-value check for a 'scanf'-like function
|
* @name Missing return-value check for a 'scanf'-like function
|
||||||
* @description Failing to check that a call to 'scanf' actually writes to an
|
* @description Failing to check that a call to 'scanf' actually writes to an
|
||||||
* output variable can lead to unexpected behavior at reading time.
|
* output variable can lead to unexpected behavior at reading time.
|
||||||
* @kind problem
|
* @kind path-problem
|
||||||
* @problem.severity warning
|
* @problem.severity warning
|
||||||
* @security-severity 7.5
|
* @security-severity 7.5
|
||||||
* @precision medium
|
* @precision medium
|
||||||
|
@ -18,16 +18,9 @@ import semmle.code.cpp.commons.Scanf
|
||||||
import semmle.code.cpp.controlflow.Guards
|
import semmle.code.cpp.controlflow.Guards
|
||||||
import semmle.code.cpp.dataflow.new.DataFlow::DataFlow
|
import semmle.code.cpp.dataflow.new.DataFlow::DataFlow
|
||||||
import semmle.code.cpp.ir.IR
|
import semmle.code.cpp.ir.IR
|
||||||
import semmle.code.cpp.ir.ValueNumbering
|
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||||
import ScanfChecks
|
import ScanfChecks
|
||||||
|
import ScanfToUseFlow::PathGraph
|
||||||
/** Holds if `n` reaches an argument to a call to a `scanf`-like function. */
|
|
||||||
pragma[nomagic]
|
|
||||||
predicate revFlow0(Node n) {
|
|
||||||
isSink(_, _, n, _)
|
|
||||||
or
|
|
||||||
exists(Node succ | revFlow0(succ) | localFlowStep(n, succ))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `n` represents an uninitialized stack-allocated variable, or a
|
* Holds if `n` represents an uninitialized stack-allocated variable, or a
|
||||||
|
@ -38,30 +31,45 @@ predicate isUninitialized(Node n) {
|
||||||
n.asIndirectExpr(1) instanceof AllocationExpr
|
n.asIndirectExpr(1) instanceof AllocationExpr
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
predicate fwdFlow0(Node n) {
|
|
||||||
revFlow0(n) and
|
|
||||||
(
|
|
||||||
isUninitialized(n)
|
|
||||||
or
|
|
||||||
exists(Node prev |
|
|
||||||
fwdFlow0(prev) and
|
|
||||||
localFlowStep(prev, n)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate isSink(ScanfFunctionCall call, int index, Node n, Expr input) {
|
predicate isSink(ScanfFunctionCall call, int index, Node n, Expr input) {
|
||||||
input = call.getOutputArgument(index) and
|
input = call.getOutputArgument(index) and
|
||||||
n.asIndirectExpr() = input
|
n.asIndirectExpr() = input
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A configuration to track a uninitialized data flowing to a `scanf`-like
|
||||||
|
* output parameter position.
|
||||||
|
*
|
||||||
|
* This is meant to be a simple flow to rule out cases like:
|
||||||
|
* ```
|
||||||
|
* int x = 0;
|
||||||
|
* scanf(..., &x);
|
||||||
|
* use(x);
|
||||||
|
* ```
|
||||||
|
* since `x` is already initialized it's not a security concern that `x` is
|
||||||
|
* used without checking the return value of `scanf`.
|
||||||
|
*
|
||||||
|
* Since this flow is meant to be simple, we disable field flow and require the
|
||||||
|
* source and the sink to be in the same callable.
|
||||||
|
*/
|
||||||
|
module UninitializedToScanfConfig implements ConfigSig {
|
||||||
|
predicate isSource(Node source) { isUninitialized(source) }
|
||||||
|
|
||||||
|
predicate isSink(Node sink) { isSink(_, _, sink, _) }
|
||||||
|
|
||||||
|
FlowFeature getAFeature() { result instanceof FeatureEqualSourceSinkCallContext }
|
||||||
|
|
||||||
|
int accessPathLimit() { result = 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
module UninitializedToScanfFlow = Global<UninitializedToScanfConfig>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `call` is a `scanf`-like call and `output` is the `index`'th
|
* Holds if `call` is a `scanf`-like call and `output` is the `index`'th
|
||||||
* argument that has not been previously initialized.
|
* argument that has not been previously initialized.
|
||||||
*/
|
*/
|
||||||
predicate isRelevantScanfCall(ScanfFunctionCall call, int index, Expr output) {
|
predicate isRelevantScanfCall(ScanfFunctionCall call, int index, Expr output) {
|
||||||
exists(Node n | fwdFlow0(n) and isSink(call, index, n, output)) and
|
exists(Node n | UninitializedToScanfFlow::flowTo(n) and isSink(call, index, n, output)) and
|
||||||
// Exclude results from incorrectky checked scanf query
|
// Exclude results from incorrectky checked scanf query
|
||||||
not incorrectlyCheckedScanf(call)
|
not incorrectlyCheckedScanf(call)
|
||||||
}
|
}
|
||||||
|
@ -77,31 +85,6 @@ predicate isSource(ScanfFunctionCall call, int index, Node n, Expr output) {
|
||||||
n.asDefiningArgument() = output
|
n.asDefiningArgument() = output
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `n` is reachable from an output argument of a relevant call to
|
|
||||||
* a `scanf`-like function.
|
|
||||||
*/
|
|
||||||
pragma[nomagic]
|
|
||||||
predicate fwdFlow(Node n) {
|
|
||||||
isSource(_, _, n, _)
|
|
||||||
or
|
|
||||||
exists(Node prev |
|
|
||||||
fwdFlow(prev) and
|
|
||||||
localFlowStep(prev, n) and
|
|
||||||
not isSanitizerOut(prev)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if `n` should not have outgoing flow. */
|
|
||||||
predicate isSanitizerOut(Node n) {
|
|
||||||
// We disable flow out of sinks to reduce result duplication
|
|
||||||
isSink(n, _)
|
|
||||||
or
|
|
||||||
// If the node is being passed to a function it may be
|
|
||||||
// modified, and thus it's safe to later read the value.
|
|
||||||
exists(n.asIndirectArgument())
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `n` is a node such that `n.asExpr() = e` and `e` is not an
|
* Holds if `n` is a node such that `n.asExpr() = e` and `e` is not an
|
||||||
* argument of a deallocation expression.
|
* argument of a deallocation expression.
|
||||||
|
@ -112,40 +95,37 @@ predicate isSink(Node n, Expr e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `n` is part of a path from a call to a `scanf`-like function
|
* A configuration to track flow from the output argument of a call to a
|
||||||
* to a use of the written variable.
|
* `scanf`-like function, and to a use of the defined variable.
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
module ScanfToUseConfig implements ConfigSig {
|
||||||
predicate revFlow(Node n) {
|
predicate isSource(Node source) { isSource(_, _, source, _) }
|
||||||
fwdFlow(n) and
|
|
||||||
(
|
predicate isSink(Node sink) { isSink(sink, _) }
|
||||||
|
|
||||||
|
predicate isBarrierOut(Node n) {
|
||||||
|
// We disable flow out of sinks to reduce result duplication
|
||||||
isSink(n, _)
|
isSink(n, _)
|
||||||
or
|
or
|
||||||
exists(Node succ |
|
// If the node is being passed to a function it may be
|
||||||
revFlow(succ) and
|
// modified, and thus it's safe to later read the value.
|
||||||
localFlowStep(n, succ) and
|
exists(n.asIndirectArgument())
|
||||||
not isSanitizerOut(n)
|
}
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A local flow step, restricted to relevant dataflow nodes. */
|
module ScanfToUseFlow = Global<ScanfToUseConfig>;
|
||||||
private predicate step(Node n1, Node n2) {
|
|
||||||
revFlow(n1) and
|
|
||||||
revFlow(n2) and
|
|
||||||
localFlowStep(n1, n2)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate hasFlow(Node n1, Node n2) = fastTC(step/2)(n1, n2)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `source` is the `index`'th argument to the `scanf`-like call `call`, and `sink` is
|
* Holds if `source` is the `index`'th argument to the `scanf`-like call `call`, and `sink` is
|
||||||
* a dataflow node that represents the expression `e`.
|
* a dataflow node that represents the expression `e`.
|
||||||
*/
|
*/
|
||||||
predicate hasFlow(Node source, ScanfFunctionCall call, int index, Node sink, Expr e) {
|
predicate flowPath(
|
||||||
isSource(call, index, source, _) and
|
ScanfToUseFlow::PathNode source, ScanfFunctionCall call, int index, ScanfToUseFlow::PathNode sink,
|
||||||
hasFlow(source, sink) and
|
Expr e
|
||||||
isSink(sink, e)
|
) {
|
||||||
|
isSource(call, index, source.getNode(), _) and
|
||||||
|
ScanfToUseFlow::flowPath(source, sink) and
|
||||||
|
isSink(sink.getNode(), e)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -167,39 +147,33 @@ int getMinimumGuardConstant(ScanfFunctionCall call, int index) {
|
||||||
* Holds the access to `e` isn't guarded by a check that ensures that `call` returned
|
* Holds the access to `e` isn't guarded by a check that ensures that `call` returned
|
||||||
* at least `minGuard`.
|
* at least `minGuard`.
|
||||||
*/
|
*/
|
||||||
predicate hasNonGuardedAccess(ScanfFunctionCall call, Expr e, int minGuard) {
|
predicate hasNonGuardedAccess(
|
||||||
|
ScanfToUseFlow::PathNode source, ScanfFunctionCall call, ScanfToUseFlow::PathNode sink, Expr e,
|
||||||
|
int minGuard
|
||||||
|
) {
|
||||||
exists(int index |
|
exists(int index |
|
||||||
hasFlow(_, call, index, _, e) and
|
flowPath(source, call, index, sink, e) and
|
||||||
minGuard = getMinimumGuardConstant(call, index)
|
minGuard = getMinimumGuardConstant(call, index)
|
||||||
|
|
|
|
||||||
not exists(int value |
|
not exists(GuardCondition guard |
|
||||||
e.getBasicBlock() = blockGuardedBy(value, "==", call) and minGuard <= value
|
// call == k and k >= minGuard so call >= minGuard
|
||||||
|
guard
|
||||||
|
.ensuresEq(globalValueNumber(call).getAnExpr(), any(int k | minGuard <= k),
|
||||||
|
e.getBasicBlock(), true)
|
||||||
or
|
or
|
||||||
e.getBasicBlock() = blockGuardedBy(value, "<", call) and minGuard - 1 <= value
|
// call >= k and k >= minGuard so call >= minGuard
|
||||||
or
|
guard
|
||||||
e.getBasicBlock() = blockGuardedBy(value, "<=", call) and minGuard <= value
|
.ensuresLt(globalValueNumber(call).getAnExpr(), any(int k | minGuard <= k),
|
||||||
|
e.getBasicBlock(), false)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a block guarded by the assertion of `value op call` */
|
from
|
||||||
BasicBlock blockGuardedBy(int value, string op, ScanfFunctionCall call) {
|
ScanfToUseFlow::PathNode source, ScanfToUseFlow::PathNode sink, ScanfFunctionCall call, Expr e,
|
||||||
exists(GuardCondition g, Expr left, Expr right |
|
int minGuard
|
||||||
right = g.getAChild() and
|
where hasNonGuardedAccess(source, call, sink, e, minGuard)
|
||||||
value = left.getValue().toInt() and
|
select e, source, sink,
|
||||||
localExprFlow(call, right)
|
|
||||||
|
|
|
||||||
g.ensuresEq(left, right, 0, result, true) and op = "=="
|
|
||||||
or
|
|
||||||
g.ensuresLt(left, right, 0, result, true) and op = "<"
|
|
||||||
or
|
|
||||||
g.ensuresLt(left, right, 1, result, true) and op = "<="
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
from ScanfFunctionCall call, Expr e, int minGuard
|
|
||||||
where hasNonGuardedAccess(call, e, minGuard)
|
|
||||||
select e,
|
|
||||||
"This variable is read, but may not have been written. " +
|
"This variable is read, but may not have been written. " +
|
||||||
"It should be guarded by a check that the $@ returns at least " + minGuard + ".", call,
|
"It should be guarded by a check that the $@ returns at least " + minGuard + ".", call,
|
||||||
call.toString()
|
call.toString()
|
||||||
|
|
|
@ -3,15 +3,11 @@ private import semmle.code.cpp.commons.Scanf
|
||||||
private import semmle.code.cpp.controlflow.IRGuards
|
private import semmle.code.cpp.controlflow.IRGuards
|
||||||
private import semmle.code.cpp.ir.ValueNumbering
|
private import semmle.code.cpp.ir.ValueNumbering
|
||||||
|
|
||||||
private ConstantInstruction getZeroInstruction() { result.getValue() = "0" }
|
|
||||||
|
|
||||||
private Operand zero() { result.getDef() = getZeroInstruction() }
|
|
||||||
|
|
||||||
private predicate exprInBooleanContext(Expr e) {
|
private predicate exprInBooleanContext(Expr e) {
|
||||||
exists(IRGuardCondition gc |
|
exists(IRGuardCondition gc |
|
||||||
exists(Instruction i |
|
exists(Instruction i |
|
||||||
i.getUnconvertedResultExpression() = e and
|
i.getUnconvertedResultExpression() = e and
|
||||||
gc.comparesEq(valueNumber(i).getAUse(), zero(), 0, _, _)
|
gc.comparesEq(valueNumber(i).getAUse(), 0, _, _)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
gc.getUnconvertedResultExpression() = e
|
gc.getUnconvertedResultExpression() = e
|
||||||
|
@ -36,10 +32,6 @@ private string getEofValue() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConstantInstruction getEofInstruction() { result.getValue() = getEofValue() }
|
|
||||||
|
|
||||||
private Operand eof() { result.getDef() = getEofInstruction() }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if the value of `call` has been checked to not equal `EOF`.
|
* Holds if the value of `call` has been checked to not equal `EOF`.
|
||||||
*/
|
*/
|
||||||
|
@ -47,10 +39,10 @@ private predicate checkedForEof(ScanfFunctionCall call) {
|
||||||
exists(IRGuardCondition gc |
|
exists(IRGuardCondition gc |
|
||||||
exists(Instruction i | i.getUnconvertedResultExpression() = call |
|
exists(Instruction i | i.getUnconvertedResultExpression() = call |
|
||||||
// call == EOF
|
// call == EOF
|
||||||
gc.comparesEq(valueNumber(i).getAUse(), eof(), 0, _, _)
|
gc.comparesEq(valueNumber(i).getAUse(), getEofValue().toInt(), _, _)
|
||||||
or
|
or
|
||||||
// call < 0 (EOF is guaranteed to be negative)
|
// call < 0 (EOF is guaranteed to be negative)
|
||||||
gc.comparesLt(valueNumber(i).getAUse(), zero(), 0, true, _)
|
gc.comparesLt(valueNumber(i).getAUse(), 0, true, _)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,37 @@ class UncalledFunction extends Function {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The `unsigned short` type. */
|
||||||
|
class UnsignedShort extends ShortType {
|
||||||
|
UnsignedShort() { this.isUnsigned() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `t` cannot refer to a string. That is, it's a built-in
|
||||||
|
* or arithmetic type that is not a "`char` like" type.
|
||||||
|
*/
|
||||||
|
predicate cannotContainString(Type t) {
|
||||||
|
exists(Type unspecified |
|
||||||
|
unspecified = t.getUnspecifiedType() and
|
||||||
|
not unspecified instanceof UnknownType and
|
||||||
|
not unspecified instanceof CharType and
|
||||||
|
not unspecified instanceof WideCharType and
|
||||||
|
not unspecified instanceof Char8Type and
|
||||||
|
not unspecified instanceof Char16Type and
|
||||||
|
not unspecified instanceof Char32Type and
|
||||||
|
// C often defines `wchar_t` as `unsigned short`
|
||||||
|
not unspecified instanceof UnsignedShort
|
||||||
|
|
|
||||||
|
unspecified instanceof ArithmeticType or
|
||||||
|
unspecified instanceof BuiltInType
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate dataFlowOrTaintFlowFunction(Function func, FunctionOutput output) {
|
||||||
|
func.(DataFlowFunction).hasDataFlow(_, output) or
|
||||||
|
func.(TaintFunction).hasTaintFlow(_, output)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` is a non-constant source of data flow for non-const format string detection.
|
* Holds if `node` is a non-constant source of data flow for non-const format string detection.
|
||||||
* This is defined as either:
|
* This is defined as either:
|
||||||
|
@ -69,7 +100,9 @@ predicate isNonConst(DataFlow::Node node) {
|
||||||
// Parameters of uncalled functions that aren't const
|
// Parameters of uncalled functions that aren't const
|
||||||
exists(UncalledFunction f, Parameter p |
|
exists(UncalledFunction f, Parameter p |
|
||||||
f.getAParameter() = p and
|
f.getAParameter() = p and
|
||||||
p = node.asParameter() and
|
// We pick the indirection of the parameter since this query is focused
|
||||||
|
// on strings.
|
||||||
|
p = node.asParameter(1) and
|
||||||
// Ignore main's argv parameter as it is already considered a `FlowSource`
|
// Ignore main's argv parameter as it is already considered a `FlowSource`
|
||||||
// not ignoring it will result in path redundancies
|
// not ignoring it will result in path redundancies
|
||||||
(f.getName() = "main" implies p != f.getParameter(1))
|
(f.getName() = "main" implies p != f.getParameter(1))
|
||||||
|
@ -82,30 +115,27 @@ predicate isNonConst(DataFlow::Node node) {
|
||||||
// are considered as possible non-const sources
|
// are considered as possible non-const sources
|
||||||
// The function's output must also not be const to be considered a non-const source
|
// The function's output must also not be const to be considered a non-const source
|
||||||
exists(Function func, CallInstruction call |
|
exists(Function func, CallInstruction call |
|
||||||
// NOTE: could use `Call` getAnArgument() instead of `CallInstruction` but requires two
|
not func.hasDefinition() and
|
||||||
// variables representing the same call in ordoer to use `callOutput` below.
|
func = call.getStaticCallTarget()
|
||||||
exists(Expr arg |
|
|
||||||
call.getPositionalArgumentOperand(_).getDef().getUnconvertedResultExpression() = arg and
|
|
||||||
arg = node.asDefiningArgument()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
call.getUnconvertedResultExpression() = node.asIndirectExpr()
|
|
||||||
|
|
|
|
||||||
func = call.getStaticCallTarget() and
|
// Case 1: It's a known dataflow or taintflow function with flow to the return value
|
||||||
|
call.getUnconvertedResultExpression() = node.asIndirectExpr() and
|
||||||
not exists(FunctionOutput output |
|
not exists(FunctionOutput output |
|
||||||
// NOTE: we must include dataflow and taintflow. e.g., including only dataflow we will find sprintf
|
dataFlowOrTaintFlowFunction(func, output) and
|
||||||
// variant function's output are now possible non-const sources
|
output.isReturnValueDeref(_) and
|
||||||
pragma[only_bind_out](func).(DataFlowFunction).hasDataFlow(_, output) or
|
|
||||||
pragma[only_bind_out](func).(TaintFunction).hasTaintFlow(_, output)
|
|
||||||
|
|
|
||||||
node = callOutput(call, output)
|
node = callOutput(call, output)
|
||||||
)
|
)
|
||||||
) and
|
or
|
||||||
not exists(Call c |
|
// Case 2: It's a known dataflow or taintflow function with flow to an output parameter
|
||||||
c.getTarget().hasDefinition() and
|
exists(int i |
|
||||||
if node instanceof DataFlow::DefinitionByReferenceNode
|
call.getPositionalArgumentOperand(i).getDef().getUnconvertedResultExpression() =
|
||||||
then c.getAnArgument() = node.asDefiningArgument()
|
node.asDefiningArgument() and
|
||||||
else c = [node.asExpr(), node.asIndirectExpr()]
|
not exists(FunctionOutput output |
|
||||||
|
dataFlowOrTaintFlowFunction(func, output) and
|
||||||
|
output.isParameterDeref(i, _) and
|
||||||
|
node = callOutput(call, output)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,18 +144,29 @@ predicate isNonConst(DataFlow::Node node) {
|
||||||
* `FormattingFunctionCall`.
|
* `FormattingFunctionCall`.
|
||||||
*/
|
*/
|
||||||
predicate isSinkImpl(DataFlow::Node sink, Expr formatString) {
|
predicate isSinkImpl(DataFlow::Node sink, Expr formatString) {
|
||||||
[sink.asExpr(), sink.asIndirectExpr()] = formatString and
|
sink.asIndirectExpr() = formatString and
|
||||||
exists(FormattingFunctionCall fc | formatString = fc.getArgument(fc.getFormatParameterIndex()))
|
exists(FormattingFunctionCall fc | formatString = fc.getArgument(fc.getFormatParameterIndex()))
|
||||||
}
|
}
|
||||||
|
|
||||||
module NonConstFlowConfig implements DataFlow::ConfigSig {
|
module NonConstFlowConfig implements DataFlow::ConfigSig {
|
||||||
predicate isSource(DataFlow::Node source) { isNonConst(source) }
|
predicate isSource(DataFlow::Node source) {
|
||||||
|
exists(Type t |
|
||||||
|
isNonConst(source) and
|
||||||
|
t = source.getType() and
|
||||||
|
not cannotContainString(t)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
|
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
|
||||||
|
|
||||||
predicate isBarrier(DataFlow::Node node) {
|
predicate isBarrier(DataFlow::Node node) {
|
||||||
// Ignore tracing non-const through array indices
|
// Ignore tracing non-const through array indices
|
||||||
exists(ArrayExpr a | a.getArrayOffset() = node.asExpr())
|
exists(ArrayExpr a | a.getArrayOffset() = node.asIndirectExpr())
|
||||||
|
or
|
||||||
|
exists(Type t |
|
||||||
|
t = node.getType() and
|
||||||
|
cannotContainString(t)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* @name Potentially uninitialized local variable
|
* @name Potentially uninitialized local variable
|
||||||
* @description Reading from a local variable that has not been assigned to
|
* @description Reading from a local variable that has not been assigned to
|
||||||
* will typically yield garbage.
|
* will typically yield garbage.
|
||||||
* @kind problem
|
* @kind path-problem
|
||||||
* @id cpp/uninitialized-local
|
* @id cpp/uninitialized-local
|
||||||
* @problem.severity warning
|
* @problem.severity warning
|
||||||
* @security-severity 7.8
|
* @security-severity 7.8
|
||||||
|
@ -15,6 +15,7 @@
|
||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.ir.IR
|
import semmle.code.cpp.ir.IR
|
||||||
import semmle.code.cpp.ir.dataflow.MustFlow
|
import semmle.code.cpp.ir.dataflow.MustFlow
|
||||||
|
import PathGraph
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Auxiliary predicate: Types that don't require initialization
|
* Auxiliary predicate: Types that don't require initialization
|
||||||
|
@ -89,4 +90,4 @@ where
|
||||||
conf.hasFlowPath(source, sink) and
|
conf.hasFlowPath(source, sink) and
|
||||||
isSinkImpl(sink.getInstruction(), va) and
|
isSinkImpl(sink.getInstruction(), va) and
|
||||||
v = va.getTarget()
|
v = va.getTarget()
|
||||||
select va, "The variable $@ may not be initialized at this access.", v, v.getName()
|
select va, source, sink, "The variable $@ may not be initialized at this access.", v, v.getName()
|
||||||
|
|
|
@ -12,34 +12,42 @@
|
||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.security.boostorg.asio.protocols
|
import semmle.code.cpp.security.boostorg.asio.protocols
|
||||||
|
|
||||||
module ExistsAnyFlowConfig implements DataFlow::ConfigSig {
|
predicate isSourceImpl(DataFlow::Node source, ConstructorCall cc) {
|
||||||
predicate isSource(DataFlow::Node source) {
|
exists(BoostorgAsio::SslContextClass c | c.getAContructorCall() = cc and cc = source.asExpr())
|
||||||
exists(BoostorgAsio::SslContextClass c | c.getAContructorCall() = source.asExpr())
|
}
|
||||||
}
|
|
||||||
|
|
||||||
predicate isSink(DataFlow::Node sink) {
|
predicate isSinkImpl(DataFlow::Node sink, FunctionCall fcSetOptions) {
|
||||||
exists(BoostorgAsio::SslSetOptionsFunction f, FunctionCall fcSetOptions |
|
exists(BoostorgAsio::SslSetOptionsFunction f |
|
||||||
f.getACallToThisFunction() = fcSetOptions and
|
f.getACallToThisFunction() = fcSetOptions and
|
||||||
fcSetOptions.getQualifier() = sink.asExpr()
|
fcSetOptions.getQualifier() = sink.asIndirectExpr()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module ExistsAnyFlowConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node source) { isSourceImpl(source, _) }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
|
||||||
}
|
}
|
||||||
|
|
||||||
module ExistsAnyFlow = DataFlow::Global<ExistsAnyFlowConfig>;
|
module ExistsAnyFlow = DataFlow::Global<ExistsAnyFlowConfig>;
|
||||||
|
|
||||||
bindingset[flag]
|
bindingset[flag]
|
||||||
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
|
predicate isOptionSet(ConstructorCall cc, int flag, FunctionCall fcSetOptions) {
|
||||||
exists(VariableAccess contextSetOptions |
|
exists(
|
||||||
ExistsAnyFlow::flow(DataFlow::exprNode(cc), DataFlow::exprNode(contextSetOptions)) and
|
VariableAccess contextSetOptions, BoostorgAsio::SslSetOptionsFunction f, DataFlow::Node source,
|
||||||
exists(BoostorgAsio::SslSetOptionsFunction f | f.getACallToThisFunction() = fcSetOptions |
|
DataFlow::Node sink
|
||||||
contextSetOptions = fcSetOptions.getQualifier() and
|
|
|
||||||
forall(Expr optionArgument, Expr optionArgumentSource |
|
isSourceImpl(source, cc) and
|
||||||
optionArgument = fcSetOptions.getArgument(0) and
|
isSinkImpl(sink, fcSetOptions) and
|
||||||
BoostorgAsio::SslOptionFlow::flow(DataFlow::exprNode(optionArgumentSource),
|
ExistsAnyFlow::flow(source, sink) and
|
||||||
DataFlow::exprNode(optionArgument))
|
f.getACallToThisFunction() = fcSetOptions and
|
||||||
|
|
contextSetOptions = fcSetOptions.getQualifier() and
|
||||||
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
forall(Expr optionArgument, Expr optionArgumentSource |
|
||||||
)
|
optionArgument = fcSetOptions.getArgument(0) and
|
||||||
|
BoostorgAsio::SslOptionFlow::flow(DataFlow::exprNode(optionArgumentSource),
|
||||||
|
DataFlow::exprNode(optionArgument))
|
||||||
|
|
|
||||||
|
optionArgument.getValue().toInt().bitShiftRight(16).bitAnd(flag) = flag
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
Certain casts in C and C++ place no restrictions on the target type. For
|
||||||
|
example, C style casts such as <code>(MyClass*)p</code> allows the programmer
|
||||||
|
to cast any pointer <code>p</code> to an expression of type <code>MyClass*</code>.
|
||||||
|
If the runtime type of <code>p</code> turns out to be a type that's incompatible
|
||||||
|
with <code>MyClass</code>, this results in undefined behavior.
|
||||||
|
</p>
|
||||||
|
</overview>
|
||||||
|
|
||||||
|
<recommendation>
|
||||||
|
<p>
|
||||||
|
If possible, use <code>dynamic_cast</code> to safely cast between polymorphic types.
|
||||||
|
If <code>dynamic_cast</code> is not an option, use <code>static_cast</code> to restrict
|
||||||
|
the kinds of conversions that the compiler is allowed to perform. If C++ style casts is
|
||||||
|
not an option, carefully check that all casts are safe.
|
||||||
|
</p>
|
||||||
|
</recommendation>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<p>
|
||||||
|
Consider the following class hierachy where we define a base class <code>Shape</code> and two
|
||||||
|
derived classes <code>Circle</code> and <code>Square</code> that are mutually incompatible:
|
||||||
|
</p>
|
||||||
|
<sample src="TypeConfusionCommon.cpp"/>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The following code demonstrates a type confusion vulnerability where the programmer
|
||||||
|
assumes that the runtime type of <code>p</code> is always a <code>Square</code>.
|
||||||
|
However, if <code>p</code> is a <code>Circle</code>, the cast will result in undefined behavior.
|
||||||
|
</p>
|
||||||
|
<sample src="TypeConfusionBad.cpp"/>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The following code fixes the vulnerability by using <code>dynamic_cast</code> to
|
||||||
|
safely cast between polymorphic types. If the cast fails, <code>dynamic_cast</code>
|
||||||
|
returns a null pointer, which can be checked for and handled appropriately.
|
||||||
|
</p>
|
||||||
|
<sample src="TypeConfusionGood.cpp"/>
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<references>
|
||||||
|
<li>
|
||||||
|
Microsoft Learn: <a href="https://learn.microsoft.com/en-us/cpp/cpp/type-conversions-and-type-safety-modern-cpp">Type conversions and type safety</a>.
|
||||||
|
</li>
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
|
@ -0,0 +1,263 @@
|
||||||
|
/**
|
||||||
|
* @name Type confusion
|
||||||
|
* @description Casting a value to an incompatible type can lead to undefined behavior.
|
||||||
|
* @kind path-problem
|
||||||
|
* @problem.severity warning
|
||||||
|
* @security-severity 9.3
|
||||||
|
* @precision medium
|
||||||
|
* @id cpp/type-confusion
|
||||||
|
* @tags security
|
||||||
|
* external/cwe/cwe-843
|
||||||
|
*/
|
||||||
|
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.dataflow.new.DataFlow
|
||||||
|
import Flow::PathGraph
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `f` is a field located at byte offset `offset` in `c`.
|
||||||
|
*
|
||||||
|
* Note that predicate is recursive, so that given the following:
|
||||||
|
* ```cpp
|
||||||
|
* struct S1 {
|
||||||
|
* int a;
|
||||||
|
* void* b;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* struct S2 {
|
||||||
|
* S1 s1;
|
||||||
|
* char c;
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
* both `hasAFieldWithOffset(S2, s1, 0)` and `hasAFieldWithOffset(S2, a, 0)`
|
||||||
|
* holds.
|
||||||
|
*/
|
||||||
|
predicate hasAFieldWithOffset(Class c, Field f, int offset) {
|
||||||
|
// Base case: `f` is a field in `c`.
|
||||||
|
f = c.getAField() and
|
||||||
|
offset = f.getByteOffset() and
|
||||||
|
not f.getUnspecifiedType().(Class).hasDefinition()
|
||||||
|
or
|
||||||
|
// Otherwise, we find the struct that is a field of `c` which then has
|
||||||
|
// the field `f` as a member.
|
||||||
|
exists(Field g |
|
||||||
|
g = c.getAField() and
|
||||||
|
// Find the field with the largest offset that's less than or equal to
|
||||||
|
// offset. That's the struct we need to search recursively.
|
||||||
|
g =
|
||||||
|
max(Field cand, int candOffset |
|
||||||
|
cand = c.getAField() and
|
||||||
|
candOffset = cand.getByteOffset() and
|
||||||
|
offset >= candOffset
|
||||||
|
|
|
||||||
|
cand order by candOffset
|
||||||
|
) and
|
||||||
|
hasAFieldWithOffset(g.getUnspecifiedType(), f, offset - g.getByteOffset())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Holds if `f` is the last field of its declaring class. */
|
||||||
|
predicate lastField(Field f) {
|
||||||
|
exists(Class c | c = f.getDeclaringType() |
|
||||||
|
f =
|
||||||
|
max(Field cand, int byteOffset |
|
||||||
|
cand.getDeclaringType() = c and byteOffset = f.getByteOffset()
|
||||||
|
|
|
||||||
|
cand order by byteOffset
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if there exists a field in `c2` at offset `offset` that's compatible
|
||||||
|
* with `f1`.
|
||||||
|
*/
|
||||||
|
bindingset[f1, offset, c2]
|
||||||
|
pragma[inline_late]
|
||||||
|
predicate hasCompatibleFieldAtOffset(Field f1, int offset, Class c2) {
|
||||||
|
exists(Field f2 | hasAFieldWithOffset(c2, f2, offset) |
|
||||||
|
// Let's not deal with bit-fields for now.
|
||||||
|
f2 instanceof BitField
|
||||||
|
or
|
||||||
|
f1.getUnspecifiedType().getSize() = f2.getUnspecifiedType().getSize()
|
||||||
|
or
|
||||||
|
lastField(f1) and
|
||||||
|
f1.getUnspecifiedType().getSize() <= f2.getUnspecifiedType().getSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `c1` is a prefix of `c2`.
|
||||||
|
*/
|
||||||
|
bindingset[c1, c2]
|
||||||
|
pragma[inline_late]
|
||||||
|
predicate prefix(Class c1, Class c2) {
|
||||||
|
not c1.isPolymorphic() and
|
||||||
|
not c2.isPolymorphic() and
|
||||||
|
if c1 instanceof Union
|
||||||
|
then
|
||||||
|
// If it's a union we just verify that one of it's variants is compatible with the other class
|
||||||
|
exists(Field f1, int offset |
|
||||||
|
// Let's not deal with bit-fields for now.
|
||||||
|
not f1 instanceof BitField and
|
||||||
|
hasAFieldWithOffset(c1, f1, offset)
|
||||||
|
|
|
||||||
|
hasCompatibleFieldAtOffset(f1, offset, c2)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
forall(Field f1, int offset |
|
||||||
|
// Let's not deal with bit-fields for now.
|
||||||
|
not f1 instanceof BitField and
|
||||||
|
hasAFieldWithOffset(c1, f1, offset)
|
||||||
|
|
|
||||||
|
hasCompatibleFieldAtOffset(f1, offset, c2)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An unsafe cast is any explicit cast that is not
|
||||||
|
* a `dynamic_cast`.
|
||||||
|
*/
|
||||||
|
class UnsafeCast extends Cast {
|
||||||
|
private Class toType;
|
||||||
|
|
||||||
|
UnsafeCast() {
|
||||||
|
(
|
||||||
|
this instanceof CStyleCast
|
||||||
|
or
|
||||||
|
this instanceof StaticCast
|
||||||
|
or
|
||||||
|
this instanceof ReinterpretCast
|
||||||
|
) and
|
||||||
|
toType = this.getExplicitlyConverted().getUnspecifiedType().stripType() and
|
||||||
|
not this.isImplicit() and
|
||||||
|
exists(TypeDeclarationEntry tde |
|
||||||
|
tde = toType.getDefinition() and
|
||||||
|
not tde.isFromUninstantiatedTemplate(_)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Class getConvertedType() { result = toType }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if the result of this cast can safely be interpreted as a value of
|
||||||
|
* type `t`.
|
||||||
|
*
|
||||||
|
* The compatibility rules are as follows:
|
||||||
|
*
|
||||||
|
* 1. the result of `(T)x` is compatible with the type `T` for any `T`
|
||||||
|
* 2. the result of `(T)x` is compatible with the type `U` for any `U` such
|
||||||
|
* that `U` is a subtype of `T`, or `T` is a subtype of `U`.
|
||||||
|
* 3. the result of `(T)x` is compatible with the type `U` if the list
|
||||||
|
* of fields of `T` is a prefix of the list of fields of `U`.
|
||||||
|
* For example, if `U` is `struct { unsigned char x; int y; };`
|
||||||
|
* and `T` is `struct { unsigned char uc; };`.
|
||||||
|
* 4. the result of `(T)x` is compatible with the type `U` if the list
|
||||||
|
* of fields of `U` is a prefix of the list of fields of `T`.
|
||||||
|
*
|
||||||
|
* Condition 4 is a bit controversial, since it assumes that the additional
|
||||||
|
* fields in `T` won't be accessed. This may result in some FNs.
|
||||||
|
*/
|
||||||
|
bindingset[this, t]
|
||||||
|
pragma[inline_late]
|
||||||
|
predicate compatibleWith(Type t) {
|
||||||
|
// Conition 1
|
||||||
|
t.stripType() = this.getConvertedType()
|
||||||
|
or
|
||||||
|
// Condition 3
|
||||||
|
prefix(this.getConvertedType(), t.stripType())
|
||||||
|
or
|
||||||
|
// Condition 4
|
||||||
|
prefix(t.stripType(), this.getConvertedType())
|
||||||
|
or
|
||||||
|
// Condition 2 (a)
|
||||||
|
t.stripType().(Class).getABaseClass+() = this.getConvertedType()
|
||||||
|
or
|
||||||
|
// Condition 2 (b)
|
||||||
|
t.stripType() = this.getConvertedType().getABaseClass+()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `source` is an allocation that allocates a value of type `type`.
|
||||||
|
*/
|
||||||
|
predicate isSourceImpl(DataFlow::Node source, Class type) {
|
||||||
|
exists(AllocationExpr alloc |
|
||||||
|
alloc = source.asExpr() and
|
||||||
|
type = alloc.getAllocatedElementType().stripType() and
|
||||||
|
not exists(
|
||||||
|
alloc
|
||||||
|
.(NewOrNewArrayExpr)
|
||||||
|
.getAllocator()
|
||||||
|
.(OperatorNewAllocationFunction)
|
||||||
|
.getPlacementArgument()
|
||||||
|
)
|
||||||
|
) and
|
||||||
|
exists(TypeDeclarationEntry tde |
|
||||||
|
tde = type.getDefinition() and
|
||||||
|
not tde.isFromUninstantiatedTemplate(_)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** A configuration describing flow from an allocation to a potentially unsafe cast. */
|
||||||
|
module Config implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node source) { isSourceImpl(source, _) }
|
||||||
|
|
||||||
|
predicate isBarrier(DataFlow::Node node) {
|
||||||
|
// We disable flow through global variables to reduce FPs from infeasible paths
|
||||||
|
node instanceof DataFlow::VariableNode
|
||||||
|
or
|
||||||
|
exists(Class c | c = node.getType().stripType() |
|
||||||
|
not c.hasDefinition()
|
||||||
|
or
|
||||||
|
exists(TypeDeclarationEntry tde |
|
||||||
|
tde = c.getDefinition() and
|
||||||
|
tde.isFromUninstantiatedTemplate(_)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(UnsafeCast cast).getUnconverted() }
|
||||||
|
|
||||||
|
int fieldFlowBranchLimit() { result = 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
module Flow = DataFlow::Global<Config>;
|
||||||
|
|
||||||
|
predicate relevantType(DataFlow::Node sink, Class allocatedType) {
|
||||||
|
exists(DataFlow::Node source |
|
||||||
|
Flow::flow(source, sink) and
|
||||||
|
isSourceImpl(source, allocatedType)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isSinkImpl(
|
||||||
|
DataFlow::Node sink, Class allocatedType, Type convertedType, boolean compatible
|
||||||
|
) {
|
||||||
|
exists(UnsafeCast cast |
|
||||||
|
relevantType(sink, allocatedType) and
|
||||||
|
sink.asExpr() = cast.getUnconverted() and
|
||||||
|
convertedType = cast.getConvertedType()
|
||||||
|
|
|
||||||
|
if cast.compatibleWith(allocatedType) then compatible = true else compatible = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
from
|
||||||
|
Flow::PathNode source, Flow::PathNode sink, Type badSourceType, Type sinkType,
|
||||||
|
DataFlow::Node sinkNode
|
||||||
|
where
|
||||||
|
Flow::flowPath(source, sink) and
|
||||||
|
sinkNode = sink.getNode() and
|
||||||
|
isSourceImpl(source.getNode(), badSourceType) and
|
||||||
|
isSinkImpl(sinkNode, badSourceType, sinkType, false) and
|
||||||
|
// If there is any flow that would result in a valid cast then we don't
|
||||||
|
// report an alert here. This reduces the number of FPs from infeasible paths
|
||||||
|
// significantly.
|
||||||
|
not exists(DataFlow::Node goodSource, Type goodSourceType |
|
||||||
|
isSourceImpl(goodSource, goodSourceType) and
|
||||||
|
isSinkImpl(sinkNode, goodSourceType, sinkType, true) and
|
||||||
|
Flow::flow(goodSource, sinkNode)
|
||||||
|
)
|
||||||
|
select sinkNode, source, sink, "Conversion from $@ to $@ is invalid.", badSourceType,
|
||||||
|
badSourceType.toString(), sinkType, sinkType.toString()
|
|
@ -0,0 +1,7 @@
|
||||||
|
void allocate_and_draw_bad() {
|
||||||
|
Shape* shape = new Circle;
|
||||||
|
// ...
|
||||||
|
// BAD: Assumes that shape is always a Square
|
||||||
|
Square* square = static_cast<Square*>(shape);
|
||||||
|
int length = square->getLength();
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
struct Shape {
|
||||||
|
virtual ~Shape();
|
||||||
|
|
||||||
|
virtual void draw() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Circle : public Shape {
|
||||||
|
Circle();
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
int getRadius();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Square : public Shape {
|
||||||
|
Square();
|
||||||
|
|
||||||
|
void draw() override {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
int getLength();
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
void allocate_and_draw_good() {
|
||||||
|
Shape* shape = new Circle;
|
||||||
|
// ...
|
||||||
|
// GOOD: Dynamically checks if shape is a Square
|
||||||
|
Square* square = dynamic_cast<Square*>(shape);
|
||||||
|
if(square) {
|
||||||
|
int length = square->getLength();
|
||||||
|
} else {
|
||||||
|
// handle error
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
* @kind metric
|
* @kind metric
|
||||||
* @tags summary
|
* @tags summary
|
||||||
* lines-of-code
|
* lines-of-code
|
||||||
|
* debug
|
||||||
* @id cpp/summary/lines-of-user-code
|
* @id cpp/summary/lines-of-user-code
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
---
|
|
||||||
category: minorAnalysis
|
|
||||||
---
|
|
||||||
* The "non-constant format string" query (`cpp/non-constant-format`) has been converted to a `path-problem` query.
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
category: newQuery
|
||||||
|
---
|
||||||
|
* Added a new query, `cpp/type-confusion`, to detect casts to invalid types.
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* Added models for `GLib` allocation and deallocation functions.
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* The "Potentially uninitialized local variable" query (`cpp/uninitialized-local`) has been converted to a `path-problem` query.
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
category: minorAnalysis
|
||||||
|
---
|
||||||
|
* The "Missing return-value check for a 'scanf'-like function" query (`cpp/missing-check-scanf`) has been converted to a `path-problem` query.
|
|
@ -1,4 +1,6 @@
|
||||||
---
|
## 0.9.6
|
||||||
category: minorAnalysis
|
|
||||||
---
|
### Minor Analysis Improvements
|
||||||
|
|
||||||
|
* The "non-constant format string" query (`cpp/non-constant-format`) has been converted to a `path-problem` query.
|
||||||
* The new C/C++ dataflow and taint-tracking libraries (`semmle.code.cpp.dataflow.new.DataFlow` and `semmle.code.cpp.dataflow.new.TaintTracking`) now implicitly assume that dataflow and taint modelled via `DataFlowFunction` and `TaintFunction` always fully overwrite their buffers and thus act as flow barriers. As a result, many dataflow and taint-tracking queries now produce fewer false positives. To remove this assumption and go back to the previous behavior for a given model, one can override the new `isPartialWrite` predicate.
|
* The new C/C++ dataflow and taint-tracking libraries (`semmle.code.cpp.dataflow.new.DataFlow` and `semmle.code.cpp.dataflow.new.TaintTracking`) now implicitly assume that dataflow and taint modelled via `DataFlowFunction` and `TaintFunction` always fully overwrite their buffers and thus act as flow barriers. As a result, many dataflow and taint-tracking queries now produce fewer false positives. To remove this assumption and go back to the previous behavior for a given model, one can override the new `isPartialWrite` predicate.
|
|
@ -0,0 +1,3 @@
|
||||||
|
## 0.9.7
|
||||||
|
|
||||||
|
No user-facing changes.
|
|
@ -1,2 +1,2 @@
|
||||||
---
|
---
|
||||||
lastReleaseVersion: 0.9.5
|
lastReleaseVersion: 0.9.7
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
<overview>
|
||||||
|
<p>
|
||||||
|
Using an iterator owned by a container after the lifetime of the container has expired can lead to undefined behavior.
|
||||||
|
This is because the iterator may be invalidated when the container is destroyed, and dereferencing an invalidated iterator is undefined behavior.
|
||||||
|
These problems can be hard to spot due to C++'s complex rules for temporary object lifetimes and their extensions.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</overview>
|
||||||
|
<recommendation>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Never create an iterator to a temporary container when the iterator is expected to be used after the container's lifetime has expired.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</recommendation>
|
||||||
|
<example>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The rules for lifetime extension ensures that the code in <code>lifetime_of_temp_extended</code> is well-defined. This is because the
|
||||||
|
lifetime of the temporary container returned by <code>get_vector</code> is extended to the end of the loop. However, prior to C++23,
|
||||||
|
the lifetime extension rules do not ensure that the container returned by <code>get_vector</code> is extended in <code>lifetime_of_temp_not_extended</code>.
|
||||||
|
This is because the temporary container is not bound to a rvalue reference.
|
||||||
|
</p>
|
||||||
|
<sample src="IteratorToExpiredContainerExtendedLifetime.cpp" />
|
||||||
|
|
||||||
|
</example>
|
||||||
|
<references>
|
||||||
|
|
||||||
|
<li>CERT C Coding Standard:
|
||||||
|
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MEM30-C.+Do+not+access+freed+memory">MEM30-C. Do not access freed memory</a>.</li>
|
||||||
|
<li>
|
||||||
|
OWASP:
|
||||||
|
<a href="https://owasp.org/www-community/vulnerabilities/Using_freed_memory">Using freed memory</a>.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/Lifetime.pdf">Lifetime safety: Preventing common dangling</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://en.cppreference.com/w/cpp/container">Containers library</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://en.cppreference.com/w/cpp/language/range-for">Range-based for loop (since C++11)</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
|
@ -0,0 +1,103 @@
|
||||||
|
/**
|
||||||
|
* @name Iterator to expired container
|
||||||
|
* @description Using an iterator owned by a container whose lifetime has expired may lead to unexpected behavior.
|
||||||
|
* @kind problem
|
||||||
|
* @precision high
|
||||||
|
* @id cpp/iterator-to-expired-container
|
||||||
|
* @problem.severity warning
|
||||||
|
* @tags reliability
|
||||||
|
* security
|
||||||
|
* external/cwe/cwe-416
|
||||||
|
* external/cwe/cwe-664
|
||||||
|
*/
|
||||||
|
|
||||||
|
// IMPORTANT: This query does not currently find anything since it relies on extractor and analysis improvements that hasn't yet been released
|
||||||
|
import cpp
|
||||||
|
import semmle.code.cpp.ir.IR
|
||||||
|
import semmle.code.cpp.dataflow.new.DataFlow
|
||||||
|
import semmle.code.cpp.models.implementations.StdContainer
|
||||||
|
import semmle.code.cpp.models.implementations.StdMap
|
||||||
|
import semmle.code.cpp.models.implementations.Iterator
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A configuration to track flow from a temporary variable to the qualifier of
|
||||||
|
* a destructor call
|
||||||
|
*/
|
||||||
|
module TempToDestructorConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node source) {
|
||||||
|
source.asInstruction().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) {
|
||||||
|
sink.asOperand().(ThisArgumentOperand).getCall().getStaticCallTarget() instanceof Destructor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module TempToDestructorFlow = DataFlow::Global<TempToDestructorConfig>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a `DataFlow::Node` that represents a temporary that will be destroyed
|
||||||
|
* by a call to a destructor, or a `DataFlow::Node` that will transitively be
|
||||||
|
* destroyed by a call to a destructor.
|
||||||
|
*
|
||||||
|
* For the latter case, consider something like:
|
||||||
|
* ```
|
||||||
|
* std::vector<std::vector<int>> get_2d_vector();
|
||||||
|
* auto& v = get_2d_vector()[0];
|
||||||
|
* ```
|
||||||
|
* Given the above, this predicate returns the node corresponding
|
||||||
|
* to `get_2d_vector()[0]` since the temporary `get_2d_vector()` gets
|
||||||
|
* destroyed by a call to `std::vector<std::vector<int>>::~vector`,
|
||||||
|
* and thus the result of `get_2d_vector()[0]` is also an invalid reference.
|
||||||
|
*/
|
||||||
|
DataFlow::Node getADestroyedNode() {
|
||||||
|
exists(TempToDestructorFlow::PathNode destroyedTemp | destroyedTemp.isSource() |
|
||||||
|
result = destroyedTemp.getNode()
|
||||||
|
or
|
||||||
|
exists(CallInstruction call |
|
||||||
|
result.asInstruction() = call and
|
||||||
|
DataFlow::localFlow(destroyedTemp.getNode(),
|
||||||
|
DataFlow::operandNode(call.getThisArgumentOperand()))
|
||||||
|
|
|
||||||
|
call.getStaticCallTarget() instanceof StdSequenceContainerAt or
|
||||||
|
call.getStaticCallTarget() instanceof StdMapAt
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
predicate isSinkImpl(DataFlow::Node sink, FunctionCall fc) {
|
||||||
|
exists(CallInstruction call |
|
||||||
|
call = sink.asOperand().(ThisArgumentOperand).getCall() and
|
||||||
|
fc = call.getUnconvertedResultExpression() and
|
||||||
|
call.getStaticCallTarget() instanceof BeginOrEndFunction
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flow from any destroyed object to the qualifier of a `begin` or `end` call
|
||||||
|
*/
|
||||||
|
module DestroyedToBeginConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node source) { source = getADestroyedNode() }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
|
||||||
|
|
||||||
|
DataFlow::FlowFeature getAFeature() {
|
||||||
|
// By blocking argument-to-parameter flow we ensure that we don't enter a
|
||||||
|
// function body where the temporary outlives anything inside the function.
|
||||||
|
// This prevents false positives in cases like:
|
||||||
|
// ```cpp
|
||||||
|
// void foo(const std::vector<int>& v) {
|
||||||
|
// for(auto x : v) { ... } // this is fine since v outlives the loop
|
||||||
|
// }
|
||||||
|
// ...
|
||||||
|
// foo(create_temporary())
|
||||||
|
// ```
|
||||||
|
result instanceof DataFlow::FeatureHasSinkCallContext
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module DestroyedToBeginFlow = DataFlow::Global<DestroyedToBeginConfig>;
|
||||||
|
|
||||||
|
from DataFlow::Node source, DataFlow::Node sink, FunctionCall beginOrEnd
|
||||||
|
where DestroyedToBeginFlow::flow(source, sink) and isSinkImpl(sink, beginOrEnd)
|
||||||
|
select source, "This object is destroyed before $@ is called.", beginOrEnd, beginOrEnd.toString()
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
std::vector<int> get_vector();
|
||||||
|
|
||||||
|
void use(int);
|
||||||
|
|
||||||
|
void lifetime_of_temp_extended() {
|
||||||
|
for(auto x : get_vector()) {
|
||||||
|
use(x); // GOOD: The lifetime of the vector returned by `get_vector()` is extended until the end of the loop.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes the the values of `v` to an external log and returns it unchanged.
|
||||||
|
const std::vector<int>& log_and_return_argument(const std::vector<int>& v);
|
||||||
|
|
||||||
|
void lifetime_of_temp_not_extended() {
|
||||||
|
for(auto x : log_and_return_argument(get_vector())) {
|
||||||
|
use(x); // BAD: The lifetime of the vector returned by `get_vector()` is not extended, and the behavior is undefined.
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
name: codeql/cpp-queries
|
name: codeql/cpp-queries
|
||||||
version: 0.9.6-dev
|
version: 0.9.8-dev
|
||||||
groups:
|
groups:
|
||||||
- cpp
|
- cpp
|
||||||
- queries
|
- queries
|
||||||
|
|
|
@ -105,10 +105,24 @@ ConditionDecl.cpp:
|
||||||
# 3| getVariableAccess(): [VariableAccess] k
|
# 3| getVariableAccess(): [VariableAccess] k
|
||||||
# 3| Type = [IntType] int
|
# 3| Type = [IntType] int
|
||||||
# 3| ValueCategory = prvalue(load)
|
# 3| ValueCategory = prvalue(load)
|
||||||
|
# 3| getInitializingExpr(): [LTExpr] ... < ...
|
||||||
|
# 3| Type = [BoolType] bool
|
||||||
|
# 3| ValueCategory = prvalue
|
||||||
|
# 3| getLesserOperand(): [VariableAccess] j
|
||||||
|
# 3| Type = [IntType] int
|
||||||
|
# 3| ValueCategory = prvalue(load)
|
||||||
|
# 3| getGreaterOperand(): [Literal] 5
|
||||||
|
# 3| Type = [IntType] int
|
||||||
|
# 3| Value = [Literal] 5
|
||||||
|
# 3| ValueCategory = prvalue
|
||||||
# 3| getVariableAccess().getFullyConverted(): [CStyleCast] (bool)...
|
# 3| getVariableAccess().getFullyConverted(): [CStyleCast] (bool)...
|
||||||
# 3| Conversion = [BoolConversion] conversion to bool
|
# 3| Conversion = [BoolConversion] conversion to bool
|
||||||
# 3| Type = [BoolType] bool
|
# 3| Type = [BoolType] bool
|
||||||
# 3| ValueCategory = prvalue
|
# 3| ValueCategory = prvalue
|
||||||
|
# 3| getInitializingExpr().getFullyConverted(): [CStyleCast] (int)...
|
||||||
|
# 3| Conversion = [IntegralConversion] integral conversion
|
||||||
|
# 3| Type = [IntType] int
|
||||||
|
# 3| ValueCategory = prvalue
|
||||||
# 3| getStmt(): [BlockStmt] { ... }
|
# 3| getStmt(): [BlockStmt] { ... }
|
||||||
# 5| getStmt(2): [ReturnStmt] return ...
|
# 5| getStmt(2): [ReturnStmt] return ...
|
||||||
ConstructorCall.cpp:
|
ConstructorCall.cpp:
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql
|
|
@ -0,0 +1,768 @@
|
||||||
|
|
||||||
|
typedef unsigned long size_t;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct remove_const { typedef T type; };
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct remove_const<const T> { typedef T type; };
|
||||||
|
|
||||||
|
// `remove_const_t<T>` removes any `const` specifier from `T`
|
||||||
|
template<class T>
|
||||||
|
using remove_const_t = typename remove_const<T>::type;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct remove_reference { typedef T type; };
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct remove_reference<T &> { typedef T type; };
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct remove_reference<T &&> { typedef T type; };
|
||||||
|
|
||||||
|
// `remove_reference_t<T>` removes any `&` from `T`
|
||||||
|
template<class T>
|
||||||
|
using remove_reference_t = typename remove_reference<T>::type;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct decay_impl {
|
||||||
|
typedef T type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, size_t t_size>
|
||||||
|
struct decay_impl<T[t_size]> {
|
||||||
|
typedef T* type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
using decay_t = typename decay_impl<remove_reference_t<T>>::type;
|
||||||
|
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template<class T> constexpr T&& forward(remove_reference_t<T>& t) noexcept;
|
||||||
|
template<class T> constexpr T&& forward(remove_reference_t<T>&& t) noexcept;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- iterator ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
struct ptrdiff_t;
|
||||||
|
|
||||||
|
template<class I> struct iterator_traits;
|
||||||
|
|
||||||
|
template <class Category,
|
||||||
|
class value_type,
|
||||||
|
class difference_type = ptrdiff_t,
|
||||||
|
class pointer_type = value_type*,
|
||||||
|
class reference_type = value_type&>
|
||||||
|
struct iterator {
|
||||||
|
typedef Category iterator_category;
|
||||||
|
|
||||||
|
iterator();
|
||||||
|
iterator(iterator<Category, remove_const_t<value_type> > const &other); // non-const -> const conversion constructor
|
||||||
|
|
||||||
|
iterator &operator++();
|
||||||
|
iterator operator++(int);
|
||||||
|
iterator &operator--();
|
||||||
|
iterator operator--(int);
|
||||||
|
bool operator==(iterator other) const;
|
||||||
|
bool operator!=(iterator other) const;
|
||||||
|
reference_type operator*() const;
|
||||||
|
pointer_type operator->() const;
|
||||||
|
iterator operator+(int);
|
||||||
|
iterator operator-(int);
|
||||||
|
iterator &operator+=(int);
|
||||||
|
iterator &operator-=(int);
|
||||||
|
int operator-(iterator);
|
||||||
|
reference_type operator[](int);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct input_iterator_tag {};
|
||||||
|
struct forward_iterator_tag : public input_iterator_tag {};
|
||||||
|
struct bidirectional_iterator_tag : public forward_iterator_tag {};
|
||||||
|
struct random_access_iterator_tag : public bidirectional_iterator_tag {};
|
||||||
|
|
||||||
|
struct output_iterator_tag {};
|
||||||
|
|
||||||
|
template<class Container>
|
||||||
|
class back_insert_iterator {
|
||||||
|
protected:
|
||||||
|
Container* container = nullptr;
|
||||||
|
public:
|
||||||
|
using iterator_category = output_iterator_tag;
|
||||||
|
using value_type = void;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = void;
|
||||||
|
using container_type = Container;
|
||||||
|
constexpr back_insert_iterator() noexcept = default;
|
||||||
|
constexpr explicit back_insert_iterator(Container& x);
|
||||||
|
back_insert_iterator& operator=(const typename Container::value_type& value);
|
||||||
|
back_insert_iterator& operator=(typename Container::value_type&& value);
|
||||||
|
back_insert_iterator& operator*();
|
||||||
|
back_insert_iterator& operator++();
|
||||||
|
back_insert_iterator operator++(int);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Container>
|
||||||
|
constexpr back_insert_iterator<Container> back_inserter(Container& x) {
|
||||||
|
return back_insert_iterator<Container>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Container>
|
||||||
|
class front_insert_iterator {
|
||||||
|
protected:
|
||||||
|
Container* container = nullptr;
|
||||||
|
public:
|
||||||
|
using iterator_category = output_iterator_tag;
|
||||||
|
using value_type = void;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = void;
|
||||||
|
using container_type = Container;
|
||||||
|
constexpr front_insert_iterator() noexcept = default;
|
||||||
|
constexpr explicit front_insert_iterator(Container& x);
|
||||||
|
constexpr front_insert_iterator& operator=(const typename Container::value_type& value);
|
||||||
|
constexpr front_insert_iterator& operator=(typename Container::value_type&& value);
|
||||||
|
constexpr front_insert_iterator& operator*();
|
||||||
|
constexpr front_insert_iterator& operator++();
|
||||||
|
constexpr front_insert_iterator operator++(int);
|
||||||
|
};
|
||||||
|
template<class Container>
|
||||||
|
constexpr front_insert_iterator<Container> front_inserter(Container& x) {
|
||||||
|
return front_insert_iterator<Container>(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- string ---
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template<class charT> struct char_traits;
|
||||||
|
|
||||||
|
typedef size_t streamsize;
|
||||||
|
|
||||||
|
template <class T> class allocator {
|
||||||
|
public:
|
||||||
|
allocator() throw();
|
||||||
|
typedef size_t size_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT> >
|
||||||
|
class basic_string {
|
||||||
|
public:
|
||||||
|
using value_type = charT;
|
||||||
|
using reference = value_type&;
|
||||||
|
using const_reference = const value_type&;
|
||||||
|
typedef typename Allocator::size_type size_type;
|
||||||
|
static const size_type npos = -1;
|
||||||
|
|
||||||
|
explicit basic_string(const Allocator& a = Allocator());
|
||||||
|
basic_string(const charT* s, const Allocator& a = Allocator());
|
||||||
|
template<class InputIterator> basic_string(InputIterator begin, InputIterator end, const Allocator& a = Allocator());
|
||||||
|
|
||||||
|
const charT* c_str() const;
|
||||||
|
charT* data() noexcept;
|
||||||
|
size_t length() const;
|
||||||
|
|
||||||
|
typedef std::iterator<random_access_iterator_tag, charT> iterator;
|
||||||
|
typedef std::iterator<random_access_iterator_tag, const charT> const_iterator;
|
||||||
|
|
||||||
|
iterator begin();
|
||||||
|
iterator end();
|
||||||
|
const_iterator begin() const;
|
||||||
|
const_iterator end() const;
|
||||||
|
const_iterator cbegin() const;
|
||||||
|
const_iterator cend() const;
|
||||||
|
|
||||||
|
void push_back(charT c);
|
||||||
|
|
||||||
|
const charT& front() const;
|
||||||
|
charT& front();
|
||||||
|
const charT& back() const;
|
||||||
|
charT& back();
|
||||||
|
|
||||||
|
const_reference operator[](size_type pos) const;
|
||||||
|
reference operator[](size_type pos);
|
||||||
|
const_reference at(size_type n) const;
|
||||||
|
reference at(size_type n);
|
||||||
|
template<class T> basic_string& operator+=(const T& t);
|
||||||
|
basic_string& operator+=(const charT* s);
|
||||||
|
basic_string& append(const basic_string& str);
|
||||||
|
basic_string& append(const charT* s);
|
||||||
|
basic_string& append(size_type n, charT c);
|
||||||
|
template<class InputIterator> basic_string& append(InputIterator first, InputIterator last);
|
||||||
|
basic_string& assign(const basic_string& str);
|
||||||
|
basic_string& assign(size_type n, charT c);
|
||||||
|
template<class InputIterator> basic_string& assign(InputIterator first, InputIterator last);
|
||||||
|
basic_string& insert(size_type pos, const basic_string& str);
|
||||||
|
basic_string& insert(size_type pos, size_type n, charT c);
|
||||||
|
basic_string& insert(size_type pos, const charT* s);
|
||||||
|
iterator insert(const_iterator p, size_type n, charT c);
|
||||||
|
template<class InputIterator> iterator insert(const_iterator p, InputIterator first, InputIterator last);
|
||||||
|
basic_string& replace(size_type pos1, size_type n1, const basic_string& str);
|
||||||
|
basic_string& replace(size_type pos1, size_type n1, size_type n2, charT c);
|
||||||
|
size_type copy(charT* s, size_type n, size_type pos = 0) const;
|
||||||
|
void clear() noexcept;
|
||||||
|
basic_string substr(size_type pos = 0, size_type n = npos) const;
|
||||||
|
void swap(basic_string& s) noexcept/*(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value)*/;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> operator+(const basic_string<charT, traits, Allocator>& lhs, const basic_string<charT, traits, Allocator>& rhs);
|
||||||
|
template<class charT, class traits, class Allocator> basic_string<charT, traits, Allocator> operator+(const basic_string<charT, traits, Allocator>& lhs, const charT* rhs);
|
||||||
|
|
||||||
|
typedef basic_string<char> string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- istring / ostream / stringstream ---
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <class charT, class traits = char_traits<charT> >
|
||||||
|
class basic_istream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||||
|
public:
|
||||||
|
using char_type = charT;
|
||||||
|
using int_type = int; //typename traits::int_type;
|
||||||
|
|
||||||
|
basic_istream<charT, traits>& operator>>(int& n);
|
||||||
|
|
||||||
|
int_type get();
|
||||||
|
basic_istream<charT, traits>& get(char_type& c);
|
||||||
|
basic_istream<charT, traits>& get(char_type* s, streamsize n);
|
||||||
|
int_type peek();
|
||||||
|
basic_istream<charT, traits>& read (char_type* s, streamsize n);
|
||||||
|
streamsize readsome(char_type* s, streamsize n);
|
||||||
|
basic_istream<charT, traits>& putback(char_type c);
|
||||||
|
basic_istream<charT,traits>& unget();
|
||||||
|
|
||||||
|
basic_istream<charT,traits>& getline(char_type* s, streamsize n);
|
||||||
|
basic_istream<charT,traits>& getline(char_type* s, streamsize n, char_type delim);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class charT, class traits> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>&, charT*);
|
||||||
|
template<class charT, class traits, class Allocator> basic_istream<charT, traits>& operator>>(basic_istream<charT, traits>& is, basic_string<charT, traits, Allocator>& str);
|
||||||
|
|
||||||
|
template<class charT, class traits, class Allocator> basic_istream<charT,traits>& getline(basic_istream<charT,traits>& is, basic_string<charT,traits,Allocator>& str, charT delim);
|
||||||
|
template<class charT, class traits, class Allocator> basic_istream<charT,traits>& getline(basic_istream<charT,traits>& is, basic_string<charT,traits,Allocator>& str);
|
||||||
|
|
||||||
|
template <class charT, class traits = char_traits<charT> >
|
||||||
|
class basic_ostream /*: virtual public basic_ios<charT,traits> - not needed for this test */ {
|
||||||
|
public:
|
||||||
|
typedef charT char_type;
|
||||||
|
|
||||||
|
basic_ostream<charT, traits>& operator<<(int n);
|
||||||
|
|
||||||
|
basic_ostream<charT, traits>& put(char_type c);
|
||||||
|
basic_ostream<charT, traits>& write(const char_type* s, streamsize n);
|
||||||
|
basic_ostream<charT,traits>& flush();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class charT, class traits> basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);
|
||||||
|
template<class charT, class traits, class Allocator> basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>& os, const basic_string<charT, traits, Allocator>& str);
|
||||||
|
|
||||||
|
template<class charT, class traits = char_traits<charT>>
|
||||||
|
class basic_iostream : public basic_istream<charT, traits>, public basic_ostream<charT, traits> {
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
|
||||||
|
class basic_stringstream : public basic_iostream<charT, traits> {
|
||||||
|
public:
|
||||||
|
explicit basic_stringstream(/*ios_base::openmode which = ios_base::out|ios_base::in - not needed for this test*/);
|
||||||
|
explicit basic_stringstream( const basic_string<charT, traits, Allocator>& str/*, ios_base::openmode which = ios_base::out | ios_base::in*/);
|
||||||
|
basic_stringstream(const basic_stringstream& rhs) = delete;
|
||||||
|
basic_stringstream(basic_stringstream&& rhs);
|
||||||
|
basic_stringstream& operator=(const basic_stringstream& rhs) = delete;
|
||||||
|
basic_stringstream& operator=(basic_stringstream&& rhs);
|
||||||
|
|
||||||
|
void swap(basic_stringstream& rhs);
|
||||||
|
|
||||||
|
basic_string<charT, traits, Allocator> str() const;
|
||||||
|
void str(const basic_string<charT, traits, Allocator>& str);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef basic_istream<char> istream;
|
||||||
|
typedef basic_ostream<char> ostream;
|
||||||
|
extern istream cin;
|
||||||
|
extern ostream cout;
|
||||||
|
|
||||||
|
using stringstream = basic_stringstream<char>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- vector ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<class T, class Allocator = allocator<T>>
|
||||||
|
class vector {
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
using reference = value_type&;
|
||||||
|
using const_reference = const value_type&;
|
||||||
|
using size_type = unsigned int;
|
||||||
|
using iterator = std::iterator<random_access_iterator_tag, T>;
|
||||||
|
using const_iterator = std::iterator<random_access_iterator_tag, const T>;
|
||||||
|
|
||||||
|
vector() noexcept(noexcept(Allocator()));
|
||||||
|
vector(const std::vector<T, Allocator>&);
|
||||||
|
explicit vector(const Allocator&) noexcept;
|
||||||
|
explicit vector(size_type n, const Allocator& = Allocator());
|
||||||
|
vector(size_type n, const T& value, const Allocator& = Allocator());
|
||||||
|
template<class InputIterator, class IteratorCategory = typename InputIterator::iterator_category> vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
|
||||||
|
// use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or
|
||||||
|
// similar that should match a different overload (SFINAE).
|
||||||
|
~vector();
|
||||||
|
|
||||||
|
vector& operator=(const vector& x);
|
||||||
|
vector& operator=(vector&& x) noexcept/*(allocator_traits<Allocator>::propagate_on_container_move_assignment::value || allocator_traits<Allocator>::is_always_equal::value)*/;
|
||||||
|
template<class InputIterator, class IteratorCategory = typename InputIterator::iterator_category> void assign(InputIterator first, InputIterator last);
|
||||||
|
// use of `iterator_category` makes sure InputIterator is (probably) an iterator, and not an `int` or
|
||||||
|
// similar that should match a different overload (SFINAE).
|
||||||
|
void assign(size_type n, const T& u);
|
||||||
|
|
||||||
|
iterator begin() noexcept;
|
||||||
|
const_iterator begin() const noexcept;
|
||||||
|
iterator end() noexcept;
|
||||||
|
const_iterator end() const noexcept;
|
||||||
|
|
||||||
|
size_type size() const noexcept;
|
||||||
|
|
||||||
|
reference operator[](size_type n);
|
||||||
|
const_reference operator[](size_type n) const;
|
||||||
|
const_reference at(size_type n) const;
|
||||||
|
reference at(size_type n);
|
||||||
|
reference front();
|
||||||
|
const_reference front() const;
|
||||||
|
reference back();
|
||||||
|
const_reference back() const;
|
||||||
|
|
||||||
|
T* data() noexcept;
|
||||||
|
const T* data() const noexcept;
|
||||||
|
|
||||||
|
void push_back(const T& x);
|
||||||
|
void push_back(T&& x);
|
||||||
|
|
||||||
|
iterator insert(const_iterator position, const T& x);
|
||||||
|
iterator insert(const_iterator position, T&& x);
|
||||||
|
iterator insert(const_iterator position, size_type n, const T& x);
|
||||||
|
template<class InputIterator> iterator insert(const_iterator position, InputIterator first, InputIterator last);
|
||||||
|
|
||||||
|
template <class... Args> iterator emplace (const_iterator position, Args&&... args);
|
||||||
|
template <class... Args> void emplace_back (Args&&... args);
|
||||||
|
|
||||||
|
void swap(vector&) noexcept/*(allocator_traits<Allocator>::propagate_on_container_swap::value || allocator_traits<Allocator>::is_always_equal::value)*/;
|
||||||
|
|
||||||
|
void clear() noexcept;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- make_shared / make_unique ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<typename T>
|
||||||
|
class shared_ptr {
|
||||||
|
public:
|
||||||
|
shared_ptr() noexcept;
|
||||||
|
explicit shared_ptr(T*);
|
||||||
|
shared_ptr(const shared_ptr&) noexcept;
|
||||||
|
template<class U> shared_ptr(const shared_ptr<U>&) noexcept;
|
||||||
|
template<class U> shared_ptr(shared_ptr<U>&&) noexcept;
|
||||||
|
|
||||||
|
shared_ptr<T>& operator=(const shared_ptr<T>&) noexcept;
|
||||||
|
shared_ptr<T>& operator=(shared_ptr<T>&&) noexcept;
|
||||||
|
|
||||||
|
T& operator*() const noexcept;
|
||||||
|
T* operator->() const noexcept;
|
||||||
|
|
||||||
|
T* get() const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class unique_ptr {
|
||||||
|
public:
|
||||||
|
constexpr unique_ptr() noexcept;
|
||||||
|
explicit unique_ptr(T*) noexcept;
|
||||||
|
unique_ptr(unique_ptr<T>&&) noexcept;
|
||||||
|
|
||||||
|
unique_ptr<T>& operator=(unique_ptr<T>&&) noexcept;
|
||||||
|
|
||||||
|
T& operator*() const;
|
||||||
|
T* operator->() const noexcept;
|
||||||
|
|
||||||
|
T* get() const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, class... Args> unique_ptr<T> make_unique(Args&&...);
|
||||||
|
|
||||||
|
template<typename T, class... Args> shared_ptr<T> make_shared(Args&&...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- pair ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <class T1, class T2>
|
||||||
|
struct pair {
|
||||||
|
typedef T1 first_type;
|
||||||
|
typedef T2 second_type;
|
||||||
|
|
||||||
|
T1 first;
|
||||||
|
T2 second;
|
||||||
|
pair();
|
||||||
|
pair(const T1& x, const T2& y);
|
||||||
|
template<class U, class V> pair(const pair<U, V> &p);
|
||||||
|
|
||||||
|
void swap(pair& p) /*noexcept(...)*/;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T1, class T2> constexpr pair<decay_t<T1>, decay_t<T2>> make_pair(T1&& x, T2&& y) {
|
||||||
|
return pair<decay_t<T1>, decay_t<T2>>(std::forward<T1>(x), std::forward<T2>(y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- map ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<class T = void> struct less;
|
||||||
|
|
||||||
|
template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>>
|
||||||
|
class map {
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using mapped_type = T;
|
||||||
|
using value_type = pair<const Key, T>;
|
||||||
|
using iterator = std::iterator<random_access_iterator_tag, value_type >;
|
||||||
|
using const_iterator = std::iterator<random_access_iterator_tag, const value_type >;
|
||||||
|
|
||||||
|
map();
|
||||||
|
map(const map& x);
|
||||||
|
map(map&& x);
|
||||||
|
~map();
|
||||||
|
|
||||||
|
map& operator=(const map& x);
|
||||||
|
map& operator=(map&& x) /*noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Compare>)*/;
|
||||||
|
|
||||||
|
iterator begin() noexcept;
|
||||||
|
const_iterator begin() const noexcept;
|
||||||
|
iterator end() noexcept;
|
||||||
|
const_iterator end() const noexcept;
|
||||||
|
|
||||||
|
T& operator[](const key_type& x);
|
||||||
|
T& operator[](key_type&& x);
|
||||||
|
T& at(const key_type& x);
|
||||||
|
const T& at(const key_type& x) const;
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> emplace(Args&&... args);
|
||||||
|
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
|
||||||
|
|
||||||
|
pair<iterator, bool> insert(const value_type& x);
|
||||||
|
pair<iterator, bool> insert(value_type&& x);
|
||||||
|
iterator insert(const_iterator position, const value_type& x);
|
||||||
|
iterator insert(const_iterator position, value_type&& x);
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
|
||||||
|
template<class... Args> pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
|
||||||
|
template<class... Args> iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);
|
||||||
|
template<class... Args> iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);
|
||||||
|
template<class M> pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);
|
||||||
|
template<class M> pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj);
|
||||||
|
template<class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);
|
||||||
|
template<class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);
|
||||||
|
|
||||||
|
iterator erase(iterator position);
|
||||||
|
iterator erase(const_iterator position);
|
||||||
|
iterator erase(const_iterator first, const_iterator last);
|
||||||
|
void swap(map&) /*noexcept(/*==allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Compare>)*/;
|
||||||
|
void clear() noexcept;
|
||||||
|
|
||||||
|
template<class C2> void merge(map<Key, T, C2, Allocator>& source);
|
||||||
|
template<class C2> void merge(map<Key, T, C2, Allocator>&& source);
|
||||||
|
|
||||||
|
iterator find(const key_type& x);
|
||||||
|
const_iterator find(const key_type& x) const;
|
||||||
|
|
||||||
|
iterator lower_bound(const key_type& x);
|
||||||
|
const_iterator lower_bound(const key_type& x) const;
|
||||||
|
iterator upper_bound(const key_type& x);
|
||||||
|
const_iterator upper_bound(const key_type& x) const;
|
||||||
|
|
||||||
|
pair<iterator, iterator> equal_range(const key_type& x);
|
||||||
|
pair<const_iterator, const_iterator> equal_range(const key_type& x) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T> struct hash;
|
||||||
|
template<class T = void> struct equal_to;
|
||||||
|
|
||||||
|
template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>>
|
||||||
|
class unordered_map {
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using mapped_type = T;
|
||||||
|
using value_type = pair<const Key, T>;
|
||||||
|
using iterator = std::iterator<random_access_iterator_tag, value_type >;
|
||||||
|
using const_iterator = std::iterator<random_access_iterator_tag, const value_type >;
|
||||||
|
|
||||||
|
unordered_map();
|
||||||
|
unordered_map(const unordered_map&);
|
||||||
|
unordered_map(unordered_map&&);
|
||||||
|
~unordered_map();
|
||||||
|
|
||||||
|
unordered_map& operator=(const unordered_map&);
|
||||||
|
unordered_map& operator=(unordered_map&&) /*noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Hash> && is_nothrow_move_assignable_v<Pred>)*/;
|
||||||
|
|
||||||
|
iterator begin() noexcept;
|
||||||
|
const_iterator begin() const noexcept;
|
||||||
|
iterator end() noexcept;
|
||||||
|
const_iterator end() const noexcept;
|
||||||
|
|
||||||
|
mapped_type& operator[](const key_type& k);
|
||||||
|
mapped_type& operator[](key_type&& k);
|
||||||
|
mapped_type& at(const key_type& k);
|
||||||
|
const mapped_type& at(const key_type& k) const;
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> emplace(Args&&... args);
|
||||||
|
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
|
||||||
|
|
||||||
|
pair<iterator, bool> insert(const value_type& obj);
|
||||||
|
pair<iterator, bool> insert(value_type&& obj);
|
||||||
|
iterator insert(const_iterator hint, const value_type& obj);
|
||||||
|
iterator insert(const_iterator hint, value_type&& obj);
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> try_emplace(const key_type& k, Args&&... args);
|
||||||
|
template<class... Args> pair<iterator, bool> try_emplace(key_type&& k, Args&&... args);
|
||||||
|
template<class... Args> iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);
|
||||||
|
template<class... Args> iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);
|
||||||
|
template<class M> pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj);
|
||||||
|
template<class M> pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj);
|
||||||
|
template<class M> iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);
|
||||||
|
template<class M> iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);
|
||||||
|
|
||||||
|
iterator erase(iterator position);
|
||||||
|
iterator erase(const_iterator position);
|
||||||
|
iterator erase(const_iterator first, const_iterator last);
|
||||||
|
void swap(unordered_map&) /*noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Hash> && is_nothrow_swappable_v<Pred>)*/;
|
||||||
|
void clear() noexcept;
|
||||||
|
|
||||||
|
template<class H2, class P2> void merge(unordered_map<Key, T, H2, P2, Allocator>& source);
|
||||||
|
template<class H2, class P2> void merge(unordered_map<Key, T, H2, P2, Allocator>&& source);
|
||||||
|
|
||||||
|
iterator find(const key_type& k);
|
||||||
|
const_iterator find(const key_type& k) const;
|
||||||
|
|
||||||
|
pair<iterator, iterator> equal_range(const key_type& k);
|
||||||
|
pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- set ---
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
|
||||||
|
class set {
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using value_type = Key;
|
||||||
|
using size_type = size_t;
|
||||||
|
using allocator_type = Allocator;
|
||||||
|
using iterator = std::iterator<random_access_iterator_tag, value_type >;
|
||||||
|
using const_iterator = std::iterator<random_access_iterator_tag, const value_type >;
|
||||||
|
|
||||||
|
set();
|
||||||
|
set(const set& x);
|
||||||
|
set(set&& x);
|
||||||
|
template<class InputIterator> set(InputIterator first, InputIterator last/*, const Compare& comp = Compare(), const Allocator& = Allocator()*/);
|
||||||
|
~set();
|
||||||
|
|
||||||
|
set& operator=(const set& x);
|
||||||
|
set& operator=(set&& x) noexcept/*(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Compare>)*/;
|
||||||
|
|
||||||
|
iterator begin() noexcept;
|
||||||
|
const_iterator begin() const noexcept;
|
||||||
|
iterator end() noexcept;
|
||||||
|
const_iterator end() const noexcept;
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> emplace(Args&&... args);
|
||||||
|
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
|
||||||
|
pair<iterator,bool> insert(const value_type& x);
|
||||||
|
pair<iterator,bool> insert(value_type&& x);
|
||||||
|
iterator insert(const_iterator position, const value_type& x);
|
||||||
|
iterator insert(const_iterator position, value_type&& x);
|
||||||
|
template<class InputIterator> void insert(InputIterator first, InputIterator last);
|
||||||
|
|
||||||
|
iterator erase(iterator position);
|
||||||
|
iterator erase(const_iterator position);
|
||||||
|
iterator erase(const_iterator first, const_iterator last);
|
||||||
|
void swap(set&) noexcept/*(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Compare>)*/;
|
||||||
|
void clear() noexcept;
|
||||||
|
|
||||||
|
template<class C2> void merge(set<Key, C2, Allocator>& source);
|
||||||
|
template<class C2> void merge(set<Key, C2, Allocator>&& source);
|
||||||
|
|
||||||
|
iterator find(const key_type& x);
|
||||||
|
const_iterator find(const key_type& x) const;
|
||||||
|
|
||||||
|
iterator lower_bound(const key_type& x);
|
||||||
|
const_iterator lower_bound(const key_type& x) const;
|
||||||
|
iterator upper_bound(const key_type& x);
|
||||||
|
const_iterator upper_bound(const key_type& x) const;
|
||||||
|
pair<iterator, iterator> equal_range(const key_type& x);
|
||||||
|
pair<const_iterator, const_iterator> equal_range(const key_type& x) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Key, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<Key>>
|
||||||
|
class unordered_set {
|
||||||
|
public:
|
||||||
|
using key_type = Key;
|
||||||
|
using value_type = Key;
|
||||||
|
using hasher = Hash;
|
||||||
|
using key_equal = Pred;
|
||||||
|
using allocator_type = Allocator;
|
||||||
|
using size_type = size_t;
|
||||||
|
using iterator = std::iterator<random_access_iterator_tag, value_type >;
|
||||||
|
using const_iterator = std::iterator<random_access_iterator_tag, const value_type >;
|
||||||
|
|
||||||
|
unordered_set();
|
||||||
|
unordered_set(const unordered_set&);
|
||||||
|
unordered_set(unordered_set&&);
|
||||||
|
template<class InputIterator> unordered_set(InputIterator f, InputIterator l, size_type n = 0/*, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()*/);
|
||||||
|
~unordered_set();
|
||||||
|
|
||||||
|
unordered_set& operator=(const unordered_set&);
|
||||||
|
unordered_set& operator=(unordered_set&&) noexcept/*(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Hash> && is_nothrow_move_assignable_v<Pred>)*/;
|
||||||
|
|
||||||
|
iterator begin() noexcept;
|
||||||
|
const_iterator begin() const noexcept;
|
||||||
|
iterator end() noexcept;
|
||||||
|
const_iterator end() const noexcept;
|
||||||
|
|
||||||
|
template<class... Args> pair<iterator, bool> emplace(Args&&... args);
|
||||||
|
template<class... Args> iterator emplace_hint(const_iterator position, Args&&... args);
|
||||||
|
pair<iterator, bool> insert(const value_type& obj);
|
||||||
|
pair<iterator, bool> insert(value_type&& obj);
|
||||||
|
iterator insert(const_iterator hint, const value_type& obj);
|
||||||
|
iterator insert(const_iterator hint, value_type&& obj);
|
||||||
|
template<class InputIterator> void insert(InputIterator first, InputIterator last);
|
||||||
|
|
||||||
|
iterator erase(iterator position);
|
||||||
|
iterator erase(const_iterator position);
|
||||||
|
iterator erase(const_iterator first, const_iterator last);
|
||||||
|
void swap(unordered_set&) noexcept/*(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Hash> && is_nothrow_swappable_v<Pred>)*/;
|
||||||
|
void clear() noexcept;
|
||||||
|
|
||||||
|
template<class H2, class P2> void merge(unordered_set<Key, H2, P2, Allocator>& source);
|
||||||
|
template<class H2, class P2> void merge(unordered_set<Key, H2, P2, Allocator>&& source);
|
||||||
|
|
||||||
|
iterator find(const key_type& k);
|
||||||
|
const_iterator find(const key_type& k) const;
|
||||||
|
pair<iterator, iterator> equal_range(const key_type& k);
|
||||||
|
pair<const_iterator, const_iterator> equal_range(const key_type& k) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> returnValue();
|
||||||
|
std::vector<std::vector<int>>& returnRef();
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> external_by_value(std::vector<std::vector<int>>);
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> external_by_const_ref(const std::vector<std::vector<int>>&);
|
||||||
|
|
||||||
|
// *: Will be detected once extract destruction of unnamed temporaries and generate IR for them
|
||||||
|
|
||||||
|
const std::vector<std::vector<int>>& return_self_by_ref(const std::vector<std::vector<int>>& v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> return_self_by_value(const std::vector<std::vector<int>>& v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test() {
|
||||||
|
for (auto x : returnValue()) {} // GOOD
|
||||||
|
for (auto x : returnValue()[0]) {} // BAD [NOT DETECTED] (see *)
|
||||||
|
for (auto x : external_by_value(returnValue())) {} // GOOD
|
||||||
|
for (auto x : external_by_const_ref(returnValue())) {} // GOOD
|
||||||
|
for (auto x : returnValue().at(0)) {} // BAD [NOT DETECTED] (see *)
|
||||||
|
|
||||||
|
for (auto x : returnRef()) {} // GOOD
|
||||||
|
for (auto x : returnRef()[0]) {} // GOOD
|
||||||
|
for (auto x : returnRef().at(0)) {} // GOOD
|
||||||
|
|
||||||
|
for(auto it = returnValue().begin(); it != returnValue().end(); ++it) {} // BAD
|
||||||
|
|
||||||
|
{
|
||||||
|
auto v = returnValue();
|
||||||
|
for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto&& v = returnValue();
|
||||||
|
for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto&& v = returnValue()[0];
|
||||||
|
for(auto it = v.begin(); it != v.end(); ++it) {} // BAD [NOT DETECTED] (see *)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto&& v = returnRef();
|
||||||
|
for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto&& v = returnRef()[0];
|
||||||
|
for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto x : return_self_by_ref(returnValue())) {} // BAD [NOT DETECTED] (see *)
|
||||||
|
|
||||||
|
for (auto x : return_self_by_value(returnValue())) {} // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void iterate(const std::vector<T>& v) {
|
||||||
|
for (auto x : v) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int>& ref_to_first_in_returnValue_1() {
|
||||||
|
return returnValue()[0]; // BAD [NOT DETECTED] (see *)
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int>& ref_to_first_in_returnValue_2() {
|
||||||
|
return returnValue()[0]; // BAD [NOT DETECTED]
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int>& ref_to_first_in_returnValue_3() {
|
||||||
|
return returnValue()[0]; // BAD [NOT DETECTED] (see *)
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> first_in_returnValue_1() {
|
||||||
|
return returnValue()[0]; // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> first_in_returnValue_2() {
|
||||||
|
return returnValue()[0]; // GOOD
|
||||||
|
}
|
||||||
|
|
||||||
|
void test2() {
|
||||||
|
iterate(returnValue()); // GOOD
|
||||||
|
iterate(returnValue()[0]); // GOOD
|
||||||
|
|
||||||
|
for (auto x : ref_to_first_in_returnValue_1()) {}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto value = ref_to_first_in_returnValue_2();
|
||||||
|
for (auto x : value) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto& ref = ref_to_first_in_returnValue_3();
|
||||||
|
for (auto x : ref) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto x : first_in_returnValue_1()) {}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto value = first_in_returnValue_2();
|
||||||
|
for (auto x : value) {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -62,7 +62,9 @@ astGuardsCompare
|
||||||
| 26 | x >= 0+1 when ... > ... is true |
|
| 26 | x >= 0+1 when ... > ... is true |
|
||||||
| 31 | - ... != x+0 when ... == ... is false |
|
| 31 | - ... != x+0 when ... == ... is false |
|
||||||
| 31 | - ... == x+0 when ... == ... is true |
|
| 31 | - ... == x+0 when ... == ... is true |
|
||||||
|
| 31 | x != -1 when ... == ... is false |
|
||||||
| 31 | x != - ...+0 when ... == ... is false |
|
| 31 | x != - ...+0 when ... == ... is false |
|
||||||
|
| 31 | x == -1 when ... == ... is true |
|
||||||
| 31 | x == - ...+0 when ... == ... is true |
|
| 31 | x == - ...+0 when ... == ... is true |
|
||||||
| 34 | 10 < j+1 when ... < ... is false |
|
| 34 | 10 < j+1 when ... < ... is false |
|
||||||
| 34 | 10 >= j+1 when ... < ... is true |
|
| 34 | 10 >= j+1 when ... < ... is true |
|
||||||
|
@ -86,15 +88,20 @@ astGuardsCompare
|
||||||
| 58 | 0 < y+1 when ... \|\| ... is false |
|
| 58 | 0 < y+1 when ... \|\| ... is false |
|
||||||
| 58 | 0 == x+0 when ... == ... is true |
|
| 58 | 0 == x+0 when ... == ... is true |
|
||||||
| 58 | 0 >= y+1 when ... < ... is true |
|
| 58 | 0 >= y+1 when ... < ... is true |
|
||||||
|
| 58 | x != 0 when ... == ... is false |
|
||||||
|
| 58 | x != 0 when ... \|\| ... is false |
|
||||||
| 58 | x != 0+0 when ... == ... is false |
|
| 58 | x != 0+0 when ... == ... is false |
|
||||||
| 58 | x != 0+0 when ... \|\| ... is false |
|
| 58 | x != 0+0 when ... \|\| ... is false |
|
||||||
|
| 58 | x == 0 when ... == ... is true |
|
||||||
| 58 | x == 0+0 when ... == ... is true |
|
| 58 | x == 0+0 when ... == ... is true |
|
||||||
| 58 | y < 0+0 when ... < ... is true |
|
| 58 | y < 0+0 when ... < ... is true |
|
||||||
| 58 | y >= 0+0 when ... < ... is false |
|
| 58 | y >= 0+0 when ... < ... is false |
|
||||||
| 58 | y >= 0+0 when ... \|\| ... is false |
|
| 58 | y >= 0+0 when ... \|\| ... is false |
|
||||||
| 75 | 0 != x+0 when ... == ... is false |
|
| 75 | 0 != x+0 when ... == ... is false |
|
||||||
| 75 | 0 == x+0 when ... == ... is true |
|
| 75 | 0 == x+0 when ... == ... is true |
|
||||||
|
| 75 | x != 0 when ... == ... is false |
|
||||||
| 75 | x != 0+0 when ... == ... is false |
|
| 75 | x != 0+0 when ... == ... is false |
|
||||||
|
| 75 | x == 0 when ... == ... is true |
|
||||||
| 75 | x == 0+0 when ... == ... is true |
|
| 75 | x == 0+0 when ... == ... is true |
|
||||||
| 85 | 0 != x+0 when ... == ... is false |
|
| 85 | 0 != x+0 when ... == ... is false |
|
||||||
| 85 | 0 != y+0 when ... != ... is true |
|
| 85 | 0 != y+0 when ... != ... is true |
|
||||||
|
@ -102,15 +109,23 @@ astGuardsCompare
|
||||||
| 85 | 0 == x+0 when ... && ... is true |
|
| 85 | 0 == x+0 when ... && ... is true |
|
||||||
| 85 | 0 == x+0 when ... == ... is true |
|
| 85 | 0 == x+0 when ... == ... is true |
|
||||||
| 85 | 0 == y+0 when ... != ... is false |
|
| 85 | 0 == y+0 when ... != ... is false |
|
||||||
|
| 85 | x != 0 when ... == ... is false |
|
||||||
| 85 | x != 0+0 when ... == ... is false |
|
| 85 | x != 0+0 when ... == ... is false |
|
||||||
|
| 85 | x == 0 when ... && ... is true |
|
||||||
|
| 85 | x == 0 when ... == ... is true |
|
||||||
| 85 | x == 0+0 when ... && ... is true |
|
| 85 | x == 0+0 when ... && ... is true |
|
||||||
| 85 | x == 0+0 when ... == ... is true |
|
| 85 | x == 0+0 when ... == ... is true |
|
||||||
|
| 85 | y != 0 when ... != ... is true |
|
||||||
|
| 85 | y != 0 when ... && ... is true |
|
||||||
| 85 | y != 0+0 when ... != ... is true |
|
| 85 | y != 0+0 when ... != ... is true |
|
||||||
| 85 | y != 0+0 when ... && ... is true |
|
| 85 | y != 0+0 when ... && ... is true |
|
||||||
|
| 85 | y == 0 when ... != ... is false |
|
||||||
| 85 | y == 0+0 when ... != ... is false |
|
| 85 | y == 0+0 when ... != ... is false |
|
||||||
| 94 | 0 != x+0 when ... != ... is true |
|
| 94 | 0 != x+0 when ... != ... is true |
|
||||||
| 94 | 0 == x+0 when ... != ... is false |
|
| 94 | 0 == x+0 when ... != ... is false |
|
||||||
|
| 94 | x != 0 when ... != ... is true |
|
||||||
| 94 | x != 0+0 when ... != ... is true |
|
| 94 | x != 0+0 when ... != ... is true |
|
||||||
|
| 94 | x == 0 when ... != ... is false |
|
||||||
| 94 | x == 0+0 when ... != ... is false |
|
| 94 | x == 0+0 when ... != ... is false |
|
||||||
| 102 | 10 < j+1 when ... < ... is false |
|
| 102 | 10 < j+1 when ... < ... is false |
|
||||||
| 102 | 10 >= j+1 when ... < ... is true |
|
| 102 | 10 >= j+1 when ... < ... is true |
|
||||||
|
@ -122,8 +137,11 @@ astGuardsCompare
|
||||||
| 109 | 0 < y+1 when ... \|\| ... is false |
|
| 109 | 0 < y+1 when ... \|\| ... is false |
|
||||||
| 109 | 0 == x+0 when ... == ... is true |
|
| 109 | 0 == x+0 when ... == ... is true |
|
||||||
| 109 | 0 >= y+1 when ... < ... is true |
|
| 109 | 0 >= y+1 when ... < ... is true |
|
||||||
|
| 109 | x != 0 when ... == ... is false |
|
||||||
|
| 109 | x != 0 when ... \|\| ... is false |
|
||||||
| 109 | x != 0+0 when ... == ... is false |
|
| 109 | x != 0+0 when ... == ... is false |
|
||||||
| 109 | x != 0+0 when ... \|\| ... is false |
|
| 109 | x != 0+0 when ... \|\| ... is false |
|
||||||
|
| 109 | x == 0 when ... == ... is true |
|
||||||
| 109 | x == 0+0 when ... == ... is true |
|
| 109 | x == 0+0 when ... == ... is true |
|
||||||
| 109 | y < 0+0 when ... < ... is true |
|
| 109 | y < 0+0 when ... < ... is true |
|
||||||
| 109 | y >= 0+0 when ... < ... is false |
|
| 109 | y >= 0+0 when ... < ... is false |
|
||||||
|
@ -162,7 +180,9 @@ astGuardsCompare
|
||||||
| 165 | y >= x+43 when ... < ... is true |
|
| 165 | y >= x+43 when ... < ... is true |
|
||||||
| 175 | 0 != call to foo+0 when ... == ... is false |
|
| 175 | 0 != call to foo+0 when ... == ... is false |
|
||||||
| 175 | 0 == call to foo+0 when ... == ... is true |
|
| 175 | 0 == call to foo+0 when ... == ... is true |
|
||||||
|
| 175 | call to foo != 0 when ... == ... is false |
|
||||||
| 175 | call to foo != 0+0 when ... == ... is false |
|
| 175 | call to foo != 0+0 when ... == ... is false |
|
||||||
|
| 175 | call to foo == 0 when ... == ... is true |
|
||||||
| 175 | call to foo == 0+0 when ... == ... is true |
|
| 175 | call to foo == 0+0 when ... == ... is true |
|
||||||
astGuardsControl
|
astGuardsControl
|
||||||
| test.c:7:9:7:13 | ... > ... | false | 10 | 11 |
|
| test.c:7:9:7:13 | ... > ... | false | 10 | 11 |
|
||||||
|
@ -443,6 +463,34 @@ astGuardsEnsure
|
||||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | != | test.cpp:31:7:31:7 | x | 0 | 34 | 34 |
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | != | test.cpp:31:7:31:7 | x | 0 | 34 | 34 |
|
||||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 30 | 30 |
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 30 | 30 |
|
||||||
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 31 | 32 |
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:12:31:13 | - ... | == | test.cpp:31:7:31:7 | x | 0 | 31 | 32 |
|
||||||
|
astGuardsEnsure_const
|
||||||
|
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | 0 | 58 | 58 |
|
||||||
|
| test.c:58:9:58:14 | ... == ... | test.c:58:9:58:9 | x | != | 0 | 62 | 62 |
|
||||||
|
| test.c:58:9:58:23 | ... \|\| ... | test.c:58:9:58:9 | x | != | 0 | 62 | 62 |
|
||||||
|
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | != | 0 | 78 | 79 |
|
||||||
|
| test.c:75:9:75:14 | ... == ... | test.c:75:9:75:9 | x | == | 0 | 75 | 77 |
|
||||||
|
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 85 | 85 |
|
||||||
|
| test.c:85:8:85:13 | ... == ... | test.c:85:8:85:8 | x | == | 0 | 86 | 86 |
|
||||||
|
| test.c:85:8:85:23 | ... && ... | test.c:85:8:85:8 | x | == | 0 | 86 | 86 |
|
||||||
|
| test.c:85:8:85:23 | ... && ... | test.c:85:18:85:18 | y | != | 0 | 86 | 86 |
|
||||||
|
| test.c:85:18:85:23 | ... != ... | test.c:85:18:85:18 | y | != | 0 | 86 | 86 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | != | 0 | 94 | 96 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 70 | 70 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 99 | 102 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 102 | 102 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 107 | 109 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 109 | 109 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 109 | 117 |
|
||||||
|
| test.c:94:11:94:16 | ... != ... | test.c:94:11:94:11 | x | == | 0 | 113 | 113 |
|
||||||
|
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | 0 | 109 | 109 |
|
||||||
|
| test.c:109:9:109:14 | ... == ... | test.c:109:9:109:9 | x | != | 0 | 113 | 113 |
|
||||||
|
| test.c:109:9:109:23 | ... \|\| ... | test.c:109:9:109:9 | x | != | 0 | 113 | 113 |
|
||||||
|
| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | != | 0 | 175 | 175 |
|
||||||
|
| test.c:175:13:175:32 | ... == ... | test.c:175:13:175:15 | call to foo | == | 0 | 175 | 175 |
|
||||||
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 30 | 30 |
|
||||||
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | != | -1 | 34 | 34 |
|
||||||
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 30 | 30 |
|
||||||
|
| test.cpp:31:7:31:13 | ... == ... | test.cpp:31:7:31:7 | x | == | -1 | 31 | 32 |
|
||||||
irGuards
|
irGuards
|
||||||
| test.c:7:9:7:13 | CompareGT: ... > ... |
|
| test.c:7:9:7:13 | CompareGT: ... > ... |
|
||||||
| test.c:17:8:17:12 | CompareLT: ... < ... |
|
| test.c:17:8:17:12 | CompareLT: ... < ... |
|
||||||
|
@ -482,74 +530,110 @@ irGuardsCompare
|
||||||
| 7 | 0 < x+0 when CompareGT: ... > ... is true |
|
| 7 | 0 < x+0 when CompareGT: ... > ... is true |
|
||||||
| 7 | 0 >= x+0 when CompareGT: ... > ... is false |
|
| 7 | 0 >= x+0 when CompareGT: ... > ... is false |
|
||||||
| 7 | x < 0+1 when CompareGT: ... > ... is false |
|
| 7 | x < 0+1 when CompareGT: ... > ... is false |
|
||||||
|
| 7 | x < 1 when CompareGT: ... > ... is false |
|
||||||
| 7 | x >= 0+1 when CompareGT: ... > ... is true |
|
| 7 | x >= 0+1 when CompareGT: ... > ... is true |
|
||||||
|
| 7 | x >= 1 when CompareGT: ... > ... is true |
|
||||||
| 17 | 0 < x+1 when CompareLT: ... < ... is false |
|
| 17 | 0 < x+1 when CompareLT: ... < ... is false |
|
||||||
| 17 | 0 >= x+1 when CompareLT: ... < ... is true |
|
| 17 | 0 >= x+1 when CompareLT: ... < ... is true |
|
||||||
| 17 | 1 < y+0 when CompareGT: ... > ... is true |
|
| 17 | 1 < y+0 when CompareGT: ... > ... is true |
|
||||||
| 17 | 1 >= y+0 when CompareGT: ... > ... is false |
|
| 17 | 1 >= y+0 when CompareGT: ... > ... is false |
|
||||||
|
| 17 | x < 0 when CompareLT: ... < ... is true |
|
||||||
| 17 | x < 0+0 when CompareLT: ... < ... is true |
|
| 17 | x < 0+0 when CompareLT: ... < ... is true |
|
||||||
|
| 17 | x >= 0 when CompareLT: ... < ... is false |
|
||||||
| 17 | x >= 0+0 when CompareLT: ... < ... is false |
|
| 17 | x >= 0+0 when CompareLT: ... < ... is false |
|
||||||
| 17 | y < 1+1 when CompareGT: ... > ... is false |
|
| 17 | y < 1+1 when CompareGT: ... > ... is false |
|
||||||
|
| 17 | y < 2 when CompareGT: ... > ... is false |
|
||||||
| 17 | y >= 1+1 when CompareGT: ... > ... is true |
|
| 17 | y >= 1+1 when CompareGT: ... > ... is true |
|
||||||
|
| 17 | y >= 2 when CompareGT: ... > ... is true |
|
||||||
| 26 | 0 < x+0 when CompareGT: ... > ... is true |
|
| 26 | 0 < x+0 when CompareGT: ... > ... is true |
|
||||||
| 26 | 0 >= x+0 when CompareGT: ... > ... is false |
|
| 26 | 0 >= x+0 when CompareGT: ... > ... is false |
|
||||||
| 26 | x < 0+1 when CompareGT: ... > ... is false |
|
| 26 | x < 0+1 when CompareGT: ... > ... is false |
|
||||||
|
| 26 | x < 1 when CompareGT: ... > ... is false |
|
||||||
| 26 | x >= 0+1 when CompareGT: ... > ... is true |
|
| 26 | x >= 0+1 when CompareGT: ... > ... is true |
|
||||||
|
| 26 | x >= 1 when CompareGT: ... > ... is true |
|
||||||
| 31 | - ... != x+0 when CompareEQ: ... == ... is false |
|
| 31 | - ... != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 31 | - ... == x+0 when CompareEQ: ... == ... is true |
|
| 31 | - ... == x+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 31 | x != -1 when CompareEQ: ... == ... is false |
|
||||||
| 31 | x != - ...+0 when CompareEQ: ... == ... is false |
|
| 31 | x != - ...+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 31 | x == -1 when CompareEQ: ... == ... is true |
|
||||||
| 31 | x == - ...+0 when CompareEQ: ... == ... is true |
|
| 31 | x == - ...+0 when CompareEQ: ... == ... is true |
|
||||||
| 34 | 10 < j+1 when CompareLT: ... < ... is false |
|
| 34 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||||
| 34 | 10 >= j+1 when CompareLT: ... < ... is true |
|
| 34 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||||
|
| 34 | j < 10 when CompareLT: ... < ... is true |
|
||||||
| 34 | j < 10+0 when CompareLT: ... < ... is true |
|
| 34 | j < 10+0 when CompareLT: ... < ... is true |
|
||||||
|
| 34 | j >= 10 when CompareLT: ... < ... is false |
|
||||||
| 34 | j >= 10+0 when CompareLT: ... < ... is false |
|
| 34 | j >= 10+0 when CompareLT: ... < ... is false |
|
||||||
| 42 | 10 < j+1 when CompareLT: ... < ... is false |
|
| 42 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||||
| 42 | 10 >= j+1 when CompareLT: ... < ... is true |
|
| 42 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||||
|
| 42 | j < 10 when CompareLT: ... < ... is true |
|
||||||
| 42 | j < 10+0 when CompareLT: ... < ... is true |
|
| 42 | j < 10+0 when CompareLT: ... < ... is true |
|
||||||
|
| 42 | j >= 10 when CompareLT: ... < ... is false |
|
||||||
| 42 | j >= 10+0 when CompareLT: ... < ... is false |
|
| 42 | j >= 10+0 when CompareLT: ... < ... is false |
|
||||||
| 44 | 0 < z+0 when CompareGT: ... > ... is true |
|
| 44 | 0 < z+0 when CompareGT: ... > ... is true |
|
||||||
| 44 | 0 >= z+0 when CompareGT: ... > ... is false |
|
| 44 | 0 >= z+0 when CompareGT: ... > ... is false |
|
||||||
| 44 | z < 0+1 when CompareGT: ... > ... is false |
|
| 44 | z < 0+1 when CompareGT: ... > ... is false |
|
||||||
|
| 44 | z < 1 when CompareGT: ... > ... is false |
|
||||||
| 44 | z >= 0+1 when CompareGT: ... > ... is true |
|
| 44 | z >= 0+1 when CompareGT: ... > ... is true |
|
||||||
|
| 44 | z >= 1 when CompareGT: ... > ... is true |
|
||||||
| 45 | 0 < y+0 when CompareGT: ... > ... is true |
|
| 45 | 0 < y+0 when CompareGT: ... > ... is true |
|
||||||
| 45 | 0 >= y+0 when CompareGT: ... > ... is false |
|
| 45 | 0 >= y+0 when CompareGT: ... > ... is false |
|
||||||
| 45 | y < 0+1 when CompareGT: ... > ... is false |
|
| 45 | y < 0+1 when CompareGT: ... > ... is false |
|
||||||
|
| 45 | y < 1 when CompareGT: ... > ... is false |
|
||||||
| 45 | y >= 0+1 when CompareGT: ... > ... is true |
|
| 45 | y >= 0+1 when CompareGT: ... > ... is true |
|
||||||
|
| 45 | y >= 1 when CompareGT: ... > ... is true |
|
||||||
| 58 | 0 != x+0 when CompareEQ: ... == ... is false |
|
| 58 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 58 | 0 < y+1 when CompareLT: ... < ... is false |
|
| 58 | 0 < y+1 when CompareLT: ... < ... is false |
|
||||||
| 58 | 0 == x+0 when CompareEQ: ... == ... is true |
|
| 58 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||||
| 58 | 0 >= y+1 when CompareLT: ... < ... is true |
|
| 58 | 0 >= y+1 when CompareLT: ... < ... is true |
|
||||||
|
| 58 | x != 0 when CompareEQ: ... == ... is false |
|
||||||
| 58 | x != 0+0 when CompareEQ: ... == ... is false |
|
| 58 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 58 | x == 0 when CompareEQ: ... == ... is true |
|
||||||
| 58 | x == 0+0 when CompareEQ: ... == ... is true |
|
| 58 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 58 | y < 0 when CompareLT: ... < ... is true |
|
||||||
| 58 | y < 0+0 when CompareLT: ... < ... is true |
|
| 58 | y < 0+0 when CompareLT: ... < ... is true |
|
||||||
|
| 58 | y >= 0 when CompareLT: ... < ... is false |
|
||||||
| 58 | y >= 0+0 when CompareLT: ... < ... is false |
|
| 58 | y >= 0+0 when CompareLT: ... < ... is false |
|
||||||
| 75 | 0 != x+0 when CompareEQ: ... == ... is false |
|
| 75 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 75 | 0 == x+0 when CompareEQ: ... == ... is true |
|
| 75 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 75 | x != 0 when CompareEQ: ... == ... is false |
|
||||||
| 75 | x != 0+0 when CompareEQ: ... == ... is false |
|
| 75 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 75 | x == 0 when CompareEQ: ... == ... is true |
|
||||||
| 75 | x == 0+0 when CompareEQ: ... == ... is true |
|
| 75 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||||
| 85 | 0 != x+0 when CompareEQ: ... == ... is false |
|
| 85 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 85 | 0 != y+0 when CompareNE: ... != ... is true |
|
| 85 | 0 != y+0 when CompareNE: ... != ... is true |
|
||||||
| 85 | 0 == x+0 when CompareEQ: ... == ... is true |
|
| 85 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||||
| 85 | 0 == y+0 when CompareNE: ... != ... is false |
|
| 85 | 0 == y+0 when CompareNE: ... != ... is false |
|
||||||
|
| 85 | x != 0 when CompareEQ: ... == ... is false |
|
||||||
| 85 | x != 0+0 when CompareEQ: ... == ... is false |
|
| 85 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 85 | x == 0 when CompareEQ: ... == ... is true |
|
||||||
| 85 | x == 0+0 when CompareEQ: ... == ... is true |
|
| 85 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 85 | y != 0 when CompareNE: ... != ... is true |
|
||||||
| 85 | y != 0+0 when CompareNE: ... != ... is true |
|
| 85 | y != 0+0 when CompareNE: ... != ... is true |
|
||||||
|
| 85 | y == 0 when CompareNE: ... != ... is false |
|
||||||
| 85 | y == 0+0 when CompareNE: ... != ... is false |
|
| 85 | y == 0+0 when CompareNE: ... != ... is false |
|
||||||
| 94 | 0 != x+0 when CompareNE: ... != ... is true |
|
| 94 | 0 != x+0 when CompareNE: ... != ... is true |
|
||||||
| 94 | 0 == x+0 when CompareNE: ... != ... is false |
|
| 94 | 0 == x+0 when CompareNE: ... != ... is false |
|
||||||
|
| 94 | x != 0 when CompareNE: ... != ... is true |
|
||||||
| 94 | x != 0+0 when CompareNE: ... != ... is true |
|
| 94 | x != 0+0 when CompareNE: ... != ... is true |
|
||||||
|
| 94 | x == 0 when CompareNE: ... != ... is false |
|
||||||
| 94 | x == 0+0 when CompareNE: ... != ... is false |
|
| 94 | x == 0+0 when CompareNE: ... != ... is false |
|
||||||
| 102 | 10 < j+1 when CompareLT: ... < ... is false |
|
| 102 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||||
| 102 | 10 >= j+1 when CompareLT: ... < ... is true |
|
| 102 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||||
|
| 102 | j < 10 when CompareLT: ... < ... is true |
|
||||||
| 102 | j < 10+0 when CompareLT: ... < ... is true |
|
| 102 | j < 10+0 when CompareLT: ... < ... is true |
|
||||||
|
| 102 | j >= 10 when CompareLT: ... < ... is false |
|
||||||
| 102 | j >= 10+0 when CompareLT: ... < ... is false |
|
| 102 | j >= 10+0 when CompareLT: ... < ... is false |
|
||||||
| 109 | 0 != x+0 when CompareEQ: ... == ... is false |
|
| 109 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 109 | 0 < y+1 when CompareLT: ... < ... is false |
|
| 109 | 0 < y+1 when CompareLT: ... < ... is false |
|
||||||
| 109 | 0 == x+0 when CompareEQ: ... == ... is true |
|
| 109 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||||
| 109 | 0 >= y+1 when CompareLT: ... < ... is true |
|
| 109 | 0 >= y+1 when CompareLT: ... < ... is true |
|
||||||
|
| 109 | x != 0 when CompareEQ: ... == ... is false |
|
||||||
| 109 | x != 0+0 when CompareEQ: ... == ... is false |
|
| 109 | x != 0+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 109 | x == 0 when CompareEQ: ... == ... is true |
|
||||||
| 109 | x == 0+0 when CompareEQ: ... == ... is true |
|
| 109 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 109 | y < 0 when CompareLT: ... < ... is true |
|
||||||
| 109 | y < 0+0 when CompareLT: ... < ... is true |
|
| 109 | y < 0+0 when CompareLT: ... < ... is true |
|
||||||
|
| 109 | y >= 0 when CompareLT: ... < ... is false |
|
||||||
| 109 | y >= 0+0 when CompareLT: ... < ... is false |
|
| 109 | y >= 0+0 when CompareLT: ... < ... is false |
|
||||||
| 156 | ... + ... != x+0 when CompareEQ: ... == ... is false |
|
| 156 | ... + ... != x+0 when CompareEQ: ... == ... is false |
|
||||||
| 156 | ... + ... == x+0 when CompareEQ: ... == ... is true |
|
| 156 | ... + ... == x+0 when CompareEQ: ... == ... is true |
|
||||||
|
@ -585,7 +669,9 @@ irGuardsCompare
|
||||||
| 165 | y >= x+43 when CompareLT: ... < ... is true |
|
| 165 | y >= x+43 when CompareLT: ... < ... is true |
|
||||||
| 175 | 0 != call to foo+0 when CompareEQ: ... == ... is false |
|
| 175 | 0 != call to foo+0 when CompareEQ: ... == ... is false |
|
||||||
| 175 | 0 == call to foo+0 when CompareEQ: ... == ... is true |
|
| 175 | 0 == call to foo+0 when CompareEQ: ... == ... is true |
|
||||||
|
| 175 | call to foo != 0 when CompareEQ: ... == ... is false |
|
||||||
| 175 | call to foo != 0+0 when CompareEQ: ... == ... is false |
|
| 175 | call to foo != 0+0 when CompareEQ: ... == ... is false |
|
||||||
|
| 175 | call to foo == 0 when CompareEQ: ... == ... is true |
|
||||||
| 175 | call to foo == 0+0 when CompareEQ: ... == ... is true |
|
| 175 | call to foo == 0+0 when CompareEQ: ... == ... is true |
|
||||||
irGuardsControl
|
irGuardsControl
|
||||||
| test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 |
|
| test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 |
|
||||||
|
@ -841,3 +927,75 @@ irGuardsEnsure
|
||||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | != | test.cpp:31:7:31:7 | Load: x | 0 | 34 | 34 |
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | != | test.cpp:31:7:31:7 | Load: x | 0 | 34 | 34 |
|
||||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 30 | 30 |
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 30 | 30 |
|
||||||
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 32 | 32 |
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:12:31:13 | Constant: - ... | == | test.cpp:31:7:31:7 | Load: x | 0 | 32 | 32 |
|
||||||
|
irGuardsEnsure_const
|
||||||
|
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:9:7:9 | Load: x | < | 1 | 11 | 11 |
|
||||||
|
| test.c:7:9:7:13 | CompareGT: ... > ... | test.c:7:9:7:9 | Load: x | >= | 1 | 8 | 8 |
|
||||||
|
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:8:17:8 | Load: x | < | 0 | 17 | 17 |
|
||||||
|
| test.c:17:8:17:12 | CompareLT: ... < ... | test.c:17:8:17:8 | Load: x | < | 0 | 18 | 18 |
|
||||||
|
| test.c:17:17:17:21 | CompareGT: ... > ... | test.c:17:17:17:17 | Load: y | >= | 2 | 18 | 18 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 2 | 2 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 31 | 31 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 34 | 34 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 35 | 35 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 39 | 39 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 42 | 42 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 43 | 43 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 45 | 45 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 46 | 46 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 52 | 52 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 56 | 56 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 58 | 58 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 59 | 59 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | < | 1 | 62 | 62 |
|
||||||
|
| test.c:26:11:26:15 | CompareGT: ... > ... | test.c:26:11:26:11 | Load: x | >= | 1 | 27 | 27 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | < | 10 | 35 | 35 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 2 | 2 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 39 | 39 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 42 | 42 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 43 | 43 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 45 | 45 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 46 | 46 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 52 | 52 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 56 | 56 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 58 | 58 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 59 | 59 |
|
||||||
|
| test.c:34:16:34:21 | CompareLT: ... < ... | test.c:34:16:34:16 | Load: j | >= | 10 | 62 | 62 |
|
||||||
|
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 43 | 43 |
|
||||||
|
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 45 | 45 |
|
||||||
|
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 46 | 46 |
|
||||||
|
| test.c:42:16:42:21 | CompareLT: ... < ... | test.c:42:16:42:16 | Load: j | < | 10 | 52 | 52 |
|
||||||
|
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | < | 1 | 52 | 52 |
|
||||||
|
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | 1 | 45 | 45 |
|
||||||
|
| test.c:44:12:44:16 | CompareGT: ... > ... | test.c:44:12:44:12 | Load: z | >= | 1 | 46 | 46 |
|
||||||
|
| test.c:45:16:45:20 | CompareGT: ... > ... | test.c:45:16:45:16 | Load: y | >= | 1 | 46 | 46 |
|
||||||
|
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | 0 | 58 | 58 |
|
||||||
|
| test.c:58:9:58:14 | CompareEQ: ... == ... | test.c:58:9:58:9 | Load: x | != | 0 | 62 | 62 |
|
||||||
|
| test.c:58:19:58:23 | CompareLT: ... < ... | test.c:58:19:58:19 | Load: y | >= | 0 | 62 | 62 |
|
||||||
|
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | != | 0 | 79 | 79 |
|
||||||
|
| test.c:75:9:75:14 | CompareEQ: ... == ... | test.c:75:9:75:9 | Load: x | == | 0 | 76 | 76 |
|
||||||
|
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | 0 | 85 | 85 |
|
||||||
|
| test.c:85:8:85:13 | CompareEQ: ... == ... | test.c:85:8:85:8 | Load: x | == | 0 | 86 | 86 |
|
||||||
|
| test.c:85:18:85:23 | CompareNE: ... != ... | test.c:85:18:85:18 | Load: y | != | 0 | 86 | 86 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | != | 0 | 95 | 95 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 70 | 70 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 99 | 99 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 102 | 102 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 103 | 103 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 107 | 107 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 109 | 109 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 110 | 110 |
|
||||||
|
| test.c:94:11:94:16 | CompareNE: ... != ... | test.c:94:11:94:11 | Load: x | == | 0 | 113 | 113 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | < | 10 | 103 | 103 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 70 | 70 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 107 | 107 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 109 | 109 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 110 | 110 |
|
||||||
|
| test.c:102:16:102:21 | CompareLT: ... < ... | test.c:102:16:102:16 | Load: j | >= | 10 | 113 | 113 |
|
||||||
|
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:9 | Load: x | != | 0 | 109 | 109 |
|
||||||
|
| test.c:109:9:109:14 | CompareEQ: ... == ... | test.c:109:9:109:9 | Load: x | != | 0 | 113 | 113 |
|
||||||
|
| test.c:109:19:109:23 | CompareLT: ... < ... | test.c:109:19:109:19 | Load: y | >= | 0 | 113 | 113 |
|
||||||
|
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | != | 0 | 175 | 175 |
|
||||||
|
| test.c:175:13:175:32 | CompareEQ: ... == ... | test.c:175:13:175:15 | Call: call to foo | == | 0 | 175 | 175 |
|
||||||
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | != | -1 | 34 | 34 |
|
||||||
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | -1 | 30 | 30 |
|
||||||
|
| test.cpp:31:7:31:13 | CompareEQ: ... == ... | test.cpp:31:7:31:7 | Load: x | == | -1 | 32 | 32 |
|
||||||
|
|
|
@ -4,22 +4,34 @@ import semmle.code.cpp.controlflow.IRGuards
|
||||||
query predicate astGuards(GuardCondition guard) { any() }
|
query predicate astGuards(GuardCondition guard) { any() }
|
||||||
|
|
||||||
query predicate astGuardsCompare(int startLine, string msg) {
|
query predicate astGuardsCompare(int startLine, string msg) {
|
||||||
exists(GuardCondition guard, Expr left, Expr right, int k, string which, string op |
|
exists(GuardCondition guard, Expr left, int k, string op |
|
||||||
exists(boolean sense |
|
exists(boolean sense, string which |
|
||||||
sense = true and which = "true"
|
sense = true and which = "true"
|
||||||
or
|
or
|
||||||
sense = false and which = "false"
|
sense = false and which = "false"
|
||||||
|
|
|
|
||||||
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
exists(Expr right |
|
||||||
|
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
||||||
|
or
|
||||||
|
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
||||||
|
|
|
||||||
|
msg = left + op + right + "+" + k + " when " + guard + " is " + which
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(AbstractValue value |
|
||||||
|
guard.comparesEq(left, k, true, value) and op = " == "
|
||||||
or
|
or
|
||||||
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
guard.comparesEq(left, k, false, value) and op = " != "
|
||||||
or
|
|
|
||||||
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
msg = left + op + k + " when " + guard + " is " + value
|
||||||
or
|
)
|
||||||
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
|
|
||||||
) and
|
startLine = guard.getLocation().getStartLine()
|
||||||
startLine = guard.getLocation().getStartLine() and
|
|
||||||
msg = left + op + right + "+" + k + " when " + guard + " is " + which
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,28 +58,58 @@ query predicate astGuardsEnsure(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query predicate astGuardsEnsure_const(
|
||||||
|
GuardCondition guard, Expr left, string op, int k, int start, int end
|
||||||
|
) {
|
||||||
|
exists(BasicBlock block |
|
||||||
|
guard.ensuresEq(left, k, block, true) and op = "=="
|
||||||
|
or
|
||||||
|
guard.ensuresEq(left, k, block, false) and op = "!="
|
||||||
|
|
|
||||||
|
block.hasLocationInfo(_, start, _, end, _)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
query predicate irGuards(IRGuardCondition guard) { any() }
|
query predicate irGuards(IRGuardCondition guard) { any() }
|
||||||
|
|
||||||
query predicate irGuardsCompare(int startLine, string msg) {
|
query predicate irGuardsCompare(int startLine, string msg) {
|
||||||
exists(IRGuardCondition guard, Operand left, Operand right, int k, string which, string op |
|
exists(IRGuardCondition guard, Operand left, int k, string op |
|
||||||
exists(boolean sense |
|
exists(boolean sense, string which |
|
||||||
sense = true and which = "true"
|
sense = true and which = "true"
|
||||||
or
|
or
|
||||||
sense = false and which = "false"
|
sense = false and which = "false"
|
||||||
|
|
|
|
||||||
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
exists(Operand right |
|
||||||
|
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
||||||
|
or
|
||||||
|
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
||||||
|
|
|
||||||
|
msg =
|
||||||
|
left.getAnyDef().getUnconvertedResultExpression() + op +
|
||||||
|
right.getAnyDef().getUnconvertedResultExpression() + "+" + k + " when " + guard + " is "
|
||||||
|
+ which
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(AbstractValue value |
|
||||||
|
guard.comparesLt(left, k, true, value) and op = " < "
|
||||||
or
|
or
|
||||||
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
guard.comparesLt(left, k, false, value) and op = " >= "
|
||||||
or
|
or
|
||||||
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
guard.comparesEq(left, k, true, value) and op = " == "
|
||||||
or
|
or
|
||||||
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
guard.comparesEq(left, k, false, value) and op = " != "
|
||||||
) and
|
|
|
||||||
startLine = guard.getLocation().getStartLine() and
|
msg =
|
||||||
msg =
|
left.getAnyDef().getUnconvertedResultExpression() + op + k + " when " + guard + " is " +
|
||||||
left.getAnyDef().getUnconvertedResultExpression() + op +
|
value
|
||||||
right.getAnyDef().getUnconvertedResultExpression() + "+" + k + " when " + guard + " is " +
|
)
|
||||||
which
|
|
|
||||||
|
startLine = guard.getLocation().getStartLine()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,3 +137,20 @@ query predicate irGuardsEnsure(
|
||||||
block.getLocation().hasLocationInfo(_, start, _, end, _)
|
block.getLocation().hasLocationInfo(_, start, _, end, _)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query predicate irGuardsEnsure_const(
|
||||||
|
IRGuardCondition guard, Instruction left, string op, int k, int start, int end
|
||||||
|
) {
|
||||||
|
exists(IRBlock block, Operand leftOp |
|
||||||
|
guard.ensuresLt(leftOp, k, block, true) and op = "<"
|
||||||
|
or
|
||||||
|
guard.ensuresLt(leftOp, k, block, false) and op = ">="
|
||||||
|
or
|
||||||
|
guard.ensuresEq(leftOp, k, block, true) and op = "=="
|
||||||
|
or
|
||||||
|
guard.ensuresEq(leftOp, k, block, false) and op = "!="
|
||||||
|
|
|
||||||
|
leftOp = left.getAUse() and
|
||||||
|
block.getLocation().hasLocationInfo(_, start, _, end, _)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -29,3 +29,6 @@
|
||||||
| test.cpp:18:8:18:10 | call to get |
|
| test.cpp:18:8:18:10 | call to get |
|
||||||
| test.cpp:31:7:31:13 | ... == ... |
|
| test.cpp:31:7:31:13 | ... == ... |
|
||||||
| test.cpp:42:13:42:20 | call to getABool |
|
| test.cpp:42:13:42:20 | call to getABool |
|
||||||
|
| test.cpp:61:10:61:10 | i |
|
||||||
|
| test.cpp:74:10:74:10 | i |
|
||||||
|
| test.cpp:84:10:84:10 | i |
|
||||||
|
|
|
@ -1,58 +1,93 @@
|
||||||
| 7 | 0 < x+0 when ... > ... is true |
|
| 7 | 0 < x+0 when ... > ... is true |
|
||||||
| 7 | 0 >= x+0 when ... > ... is false |
|
| 7 | 0 >= x+0 when ... > ... is false |
|
||||||
| 7 | x < 0+1 when ... > ... is false |
|
| 7 | x < 0+1 when ... > ... is false |
|
||||||
|
| 7 | x < 1 when ... > ... is false |
|
||||||
| 7 | x >= 0+1 when ... > ... is true |
|
| 7 | x >= 0+1 when ... > ... is true |
|
||||||
|
| 7 | x >= 1 when ... > ... is true |
|
||||||
| 17 | 0 < x+1 when ... < ... is false |
|
| 17 | 0 < x+1 when ... < ... is false |
|
||||||
| 17 | 0 >= x+1 when ... && ... is true |
|
| 17 | 0 >= x+1 when ... && ... is true |
|
||||||
| 17 | 0 >= x+1 when ... < ... is true |
|
| 17 | 0 >= x+1 when ... < ... is true |
|
||||||
| 17 | 1 < y+0 when ... && ... is true |
|
| 17 | 1 < y+0 when ... && ... is true |
|
||||||
| 17 | 1 < y+0 when ... > ... is true |
|
| 17 | 1 < y+0 when ... > ... is true |
|
||||||
| 17 | 1 >= y+0 when ... > ... is false |
|
| 17 | 1 >= y+0 when ... > ... is false |
|
||||||
|
| 17 | x < 0 when ... && ... is true |
|
||||||
|
| 17 | x < 0 when ... < ... is true |
|
||||||
| 17 | x < 0+0 when ... && ... is true |
|
| 17 | x < 0+0 when ... && ... is true |
|
||||||
| 17 | x < 0+0 when ... < ... is true |
|
| 17 | x < 0+0 when ... < ... is true |
|
||||||
|
| 17 | x >= 0 when ... < ... is false |
|
||||||
| 17 | x >= 0+0 when ... < ... is false |
|
| 17 | x >= 0+0 when ... < ... is false |
|
||||||
| 17 | y < 1+1 when ... > ... is false |
|
| 17 | y < 1+1 when ... > ... is false |
|
||||||
|
| 17 | y < 2 when ... > ... is false |
|
||||||
| 17 | y >= 1+1 when ... && ... is true |
|
| 17 | y >= 1+1 when ... && ... is true |
|
||||||
| 17 | y >= 1+1 when ... > ... is true |
|
| 17 | y >= 1+1 when ... > ... is true |
|
||||||
|
| 17 | y >= 2 when ... && ... is true |
|
||||||
|
| 17 | y >= 2 when ... > ... is true |
|
||||||
| 26 | 0 < x+0 when ... > ... is true |
|
| 26 | 0 < x+0 when ... > ... is true |
|
||||||
| 26 | 0 >= x+0 when ... > ... is false |
|
| 26 | 0 >= x+0 when ... > ... is false |
|
||||||
| 26 | x < 0+1 when ... > ... is false |
|
| 26 | x < 0+1 when ... > ... is false |
|
||||||
|
| 26 | x < 1 when ... > ... is false |
|
||||||
| 26 | x >= 0+1 when ... > ... is true |
|
| 26 | x >= 0+1 when ... > ... is true |
|
||||||
|
| 26 | x >= 1 when ... > ... is true |
|
||||||
| 31 | - ... != x+0 when ... == ... is false |
|
| 31 | - ... != x+0 when ... == ... is false |
|
||||||
| 31 | - ... == x+0 when ... == ... is true |
|
| 31 | - ... == x+0 when ... == ... is true |
|
||||||
|
| 31 | x != -1 when ... == ... is false |
|
||||||
| 31 | x != - ...+0 when ... == ... is false |
|
| 31 | x != - ...+0 when ... == ... is false |
|
||||||
|
| 31 | x == -1 when ... == ... is true |
|
||||||
| 31 | x == - ...+0 when ... == ... is true |
|
| 31 | x == - ...+0 when ... == ... is true |
|
||||||
| 34 | 10 < j+1 when ... < ... is false |
|
| 34 | 10 < j+1 when ... < ... is false |
|
||||||
| 34 | 10 >= j+1 when ... < ... is true |
|
| 34 | 10 >= j+1 when ... < ... is true |
|
||||||
|
| 34 | j < 10 when ... < ... is true |
|
||||||
| 34 | j < 10+0 when ... < ... is true |
|
| 34 | j < 10+0 when ... < ... is true |
|
||||||
|
| 34 | j >= 10 when ... < ... is false |
|
||||||
| 34 | j >= 10+0 when ... < ... is false |
|
| 34 | j >= 10+0 when ... < ... is false |
|
||||||
| 42 | 10 < j+1 when ... < ... is false |
|
| 42 | 10 < j+1 when ... < ... is false |
|
||||||
| 42 | 10 >= j+1 when ... < ... is true |
|
| 42 | 10 >= j+1 when ... < ... is true |
|
||||||
|
| 42 | j < 10 when ... < ... is true |
|
||||||
| 42 | j < 10+0 when ... < ... is true |
|
| 42 | j < 10+0 when ... < ... is true |
|
||||||
|
| 42 | j >= 10 when ... < ... is false |
|
||||||
| 42 | j >= 10+0 when ... < ... is false |
|
| 42 | j >= 10+0 when ... < ... is false |
|
||||||
| 44 | 0 < z+0 when ... > ... is true |
|
| 44 | 0 < z+0 when ... > ... is true |
|
||||||
| 44 | 0 >= z+0 when ... > ... is false |
|
| 44 | 0 >= z+0 when ... > ... is false |
|
||||||
| 44 | z < 0+1 when ... > ... is false |
|
| 44 | z < 0+1 when ... > ... is false |
|
||||||
|
| 44 | z < 1 when ... > ... is false |
|
||||||
| 44 | z >= 0+1 when ... > ... is true |
|
| 44 | z >= 0+1 when ... > ... is true |
|
||||||
|
| 44 | z >= 1 when ... > ... is true |
|
||||||
| 45 | 0 < y+0 when ... > ... is true |
|
| 45 | 0 < y+0 when ... > ... is true |
|
||||||
| 45 | 0 >= y+0 when ... > ... is false |
|
| 45 | 0 >= y+0 when ... > ... is false |
|
||||||
| 45 | y < 0+1 when ... > ... is false |
|
| 45 | y < 0+1 when ... > ... is false |
|
||||||
|
| 45 | y < 1 when ... > ... is false |
|
||||||
| 45 | y >= 0+1 when ... > ... is true |
|
| 45 | y >= 0+1 when ... > ... is true |
|
||||||
|
| 45 | y >= 1 when ... > ... is true |
|
||||||
| 58 | 0 != x+0 when ... == ... is false |
|
| 58 | 0 != x+0 when ... == ... is false |
|
||||||
| 58 | 0 != x+0 when ... \|\| ... is false |
|
| 58 | 0 != x+0 when ... \|\| ... is false |
|
||||||
| 58 | 0 < y+1 when ... < ... is false |
|
| 58 | 0 < y+1 when ... < ... is false |
|
||||||
| 58 | 0 < y+1 when ... \|\| ... is false |
|
| 58 | 0 < y+1 when ... \|\| ... is false |
|
||||||
| 58 | 0 == x+0 when ... == ... is true |
|
| 58 | 0 == x+0 when ... == ... is true |
|
||||||
| 58 | 0 >= y+1 when ... < ... is true |
|
| 58 | 0 >= y+1 when ... < ... is true |
|
||||||
|
| 58 | x != 0 when ... == ... is false |
|
||||||
|
| 58 | x != 0 when ... \|\| ... is false |
|
||||||
| 58 | x != 0+0 when ... == ... is false |
|
| 58 | x != 0+0 when ... == ... is false |
|
||||||
| 58 | x != 0+0 when ... \|\| ... is false |
|
| 58 | x != 0+0 when ... \|\| ... is false |
|
||||||
|
| 58 | x == 0 when ... == ... is true |
|
||||||
| 58 | x == 0+0 when ... == ... is true |
|
| 58 | x == 0+0 when ... == ... is true |
|
||||||
|
| 58 | y < 0 when ... < ... is true |
|
||||||
| 58 | y < 0+0 when ... < ... is true |
|
| 58 | y < 0+0 when ... < ... is true |
|
||||||
|
| 58 | y >= 0 when ... < ... is false |
|
||||||
|
| 58 | y >= 0 when ... \|\| ... is false |
|
||||||
| 58 | y >= 0+0 when ... < ... is false |
|
| 58 | y >= 0+0 when ... < ... is false |
|
||||||
| 58 | y >= 0+0 when ... \|\| ... is false |
|
| 58 | y >= 0+0 when ... \|\| ... is false |
|
||||||
|
| 61 | i == 0 when i is Case[0] |
|
||||||
|
| 61 | i == 1 when i is Case[1] |
|
||||||
|
| 61 | i == 2 when i is Case[2] |
|
||||||
|
| 74 | i < 11 when i is Case[0..10] |
|
||||||
|
| 74 | i < 21 when i is Case[11..20] |
|
||||||
|
| 74 | i >= 0 when i is Case[0..10] |
|
||||||
|
| 74 | i >= 11 when i is Case[11..20] |
|
||||||
| 75 | 0 != x+0 when ... == ... is false |
|
| 75 | 0 != x+0 when ... == ... is false |
|
||||||
| 75 | 0 == x+0 when ... == ... is true |
|
| 75 | 0 == x+0 when ... == ... is true |
|
||||||
|
| 75 | x != 0 when ... == ... is false |
|
||||||
| 75 | x != 0+0 when ... == ... is false |
|
| 75 | x != 0+0 when ... == ... is false |
|
||||||
|
| 75 | x == 0 when ... == ... is true |
|
||||||
| 75 | x == 0+0 when ... == ... is true |
|
| 75 | x == 0+0 when ... == ... is true |
|
||||||
| 85 | 0 != x+0 when ... == ... is false |
|
| 85 | 0 != x+0 when ... == ... is false |
|
||||||
| 85 | 0 != y+0 when ... != ... is true |
|
| 85 | 0 != y+0 when ... != ... is true |
|
||||||
|
@ -60,19 +95,29 @@
|
||||||
| 85 | 0 == x+0 when ... && ... is true |
|
| 85 | 0 == x+0 when ... && ... is true |
|
||||||
| 85 | 0 == x+0 when ... == ... is true |
|
| 85 | 0 == x+0 when ... == ... is true |
|
||||||
| 85 | 0 == y+0 when ... != ... is false |
|
| 85 | 0 == y+0 when ... != ... is false |
|
||||||
|
| 85 | x != 0 when ... == ... is false |
|
||||||
| 85 | x != 0+0 when ... == ... is false |
|
| 85 | x != 0+0 when ... == ... is false |
|
||||||
|
| 85 | x == 0 when ... && ... is true |
|
||||||
|
| 85 | x == 0 when ... == ... is true |
|
||||||
| 85 | x == 0+0 when ... && ... is true |
|
| 85 | x == 0+0 when ... && ... is true |
|
||||||
| 85 | x == 0+0 when ... == ... is true |
|
| 85 | x == 0+0 when ... == ... is true |
|
||||||
|
| 85 | y != 0 when ... != ... is true |
|
||||||
|
| 85 | y != 0 when ... && ... is true |
|
||||||
| 85 | y != 0+0 when ... != ... is true |
|
| 85 | y != 0+0 when ... != ... is true |
|
||||||
| 85 | y != 0+0 when ... && ... is true |
|
| 85 | y != 0+0 when ... && ... is true |
|
||||||
|
| 85 | y == 0 when ... != ... is false |
|
||||||
| 85 | y == 0+0 when ... != ... is false |
|
| 85 | y == 0+0 when ... != ... is false |
|
||||||
| 94 | 0 != x+0 when ... != ... is true |
|
| 94 | 0 != x+0 when ... != ... is true |
|
||||||
| 94 | 0 == x+0 when ... != ... is false |
|
| 94 | 0 == x+0 when ... != ... is false |
|
||||||
|
| 94 | x != 0 when ... != ... is true |
|
||||||
| 94 | x != 0+0 when ... != ... is true |
|
| 94 | x != 0+0 when ... != ... is true |
|
||||||
|
| 94 | x == 0 when ... != ... is false |
|
||||||
| 94 | x == 0+0 when ... != ... is false |
|
| 94 | x == 0+0 when ... != ... is false |
|
||||||
| 102 | 10 < j+1 when ... < ... is false |
|
| 102 | 10 < j+1 when ... < ... is false |
|
||||||
| 102 | 10 >= j+1 when ... < ... is true |
|
| 102 | 10 >= j+1 when ... < ... is true |
|
||||||
|
| 102 | j < 10 when ... < ... is true |
|
||||||
| 102 | j < 10+0 when ... < ... is true |
|
| 102 | j < 10+0 when ... < ... is true |
|
||||||
|
| 102 | j >= 10 when ... < ... is false |
|
||||||
| 102 | j >= 10+0 when ... < ... is false |
|
| 102 | j >= 10+0 when ... < ... is false |
|
||||||
| 109 | 0 != x+0 when ... == ... is false |
|
| 109 | 0 != x+0 when ... == ... is false |
|
||||||
| 109 | 0 != x+0 when ... \|\| ... is false |
|
| 109 | 0 != x+0 when ... \|\| ... is false |
|
||||||
|
@ -80,9 +125,15 @@
|
||||||
| 109 | 0 < y+1 when ... \|\| ... is false |
|
| 109 | 0 < y+1 when ... \|\| ... is false |
|
||||||
| 109 | 0 == x+0 when ... == ... is true |
|
| 109 | 0 == x+0 when ... == ... is true |
|
||||||
| 109 | 0 >= y+1 when ... < ... is true |
|
| 109 | 0 >= y+1 when ... < ... is true |
|
||||||
|
| 109 | x != 0 when ... == ... is false |
|
||||||
|
| 109 | x != 0 when ... \|\| ... is false |
|
||||||
| 109 | x != 0+0 when ... == ... is false |
|
| 109 | x != 0+0 when ... == ... is false |
|
||||||
| 109 | x != 0+0 when ... \|\| ... is false |
|
| 109 | x != 0+0 when ... \|\| ... is false |
|
||||||
|
| 109 | x == 0 when ... == ... is true |
|
||||||
| 109 | x == 0+0 when ... == ... is true |
|
| 109 | x == 0+0 when ... == ... is true |
|
||||||
|
| 109 | y < 0 when ... < ... is true |
|
||||||
| 109 | y < 0+0 when ... < ... is true |
|
| 109 | y < 0+0 when ... < ... is true |
|
||||||
|
| 109 | y >= 0 when ... < ... is false |
|
||||||
|
| 109 | y >= 0 when ... \|\| ... is false |
|
||||||
| 109 | y >= 0+0 when ... < ... is false |
|
| 109 | y >= 0+0 when ... < ... is false |
|
||||||
| 109 | y >= 0+0 when ... \|\| ... is false |
|
| 109 | y >= 0+0 when ... \|\| ... is false |
|
||||||
|
|
|
@ -7,20 +7,35 @@
|
||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.controlflow.Guards
|
import semmle.code.cpp.controlflow.Guards
|
||||||
|
|
||||||
from GuardCondition guard, Expr left, Expr right, int k, string which, string op, string msg
|
from GuardCondition guard, Expr left, int k, string op, string msg
|
||||||
where
|
where
|
||||||
exists(boolean sense |
|
exists(boolean sense, string which |
|
||||||
sense = true and which = "true"
|
sense = true and which = "true"
|
||||||
or
|
or
|
||||||
sense = false and which = "false"
|
sense = false and which = "false"
|
||||||
|
|
|
|
||||||
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
exists(Expr right |
|
||||||
|
guard.comparesLt(left, right, k, true, sense) and op = " < "
|
||||||
|
or
|
||||||
|
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
||||||
|
or
|
||||||
|
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
||||||
|
|
|
||||||
|
msg = left + op + right + "+" + k + " when " + guard + " is " + which
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(AbstractValue value |
|
||||||
|
guard.comparesLt(left, k, true, value) and op = " < "
|
||||||
or
|
or
|
||||||
guard.comparesLt(left, right, k, false, sense) and op = " >= "
|
guard.comparesLt(left, k, false, value) and op = " >= "
|
||||||
or
|
or
|
||||||
guard.comparesEq(left, right, k, true, sense) and op = " == "
|
guard.comparesEq(left, k, true, value) and op = " == "
|
||||||
or
|
or
|
||||||
guard.comparesEq(left, right, k, false, sense) and op = " != "
|
guard.comparesEq(left, k, false, value) and op = " != "
|
||||||
) and
|
|
|
||||||
msg = left + op + right + "+" + k + " when " + guard + " is " + which
|
msg = left + op + k + " when " + guard + " is " + value
|
||||||
|
)
|
||||||
select guard.getLocation().getStartLine(), msg
|
select guard.getLocation().getStartLine(), msg
|
||||||
|
|
|
@ -86,3 +86,7 @@
|
||||||
| test.cpp:31:7:31:13 | ... == ... | true | 31 | 32 |
|
| test.cpp:31:7:31:13 | ... == ... | true | 31 | 32 |
|
||||||
| test.cpp:42:13:42:20 | call to getABool | false | 53 | 53 |
|
| test.cpp:42:13:42:20 | call to getABool | false | 53 | 53 |
|
||||||
| test.cpp:42:13:42:20 | call to getABool | true | 43 | 45 |
|
| test.cpp:42:13:42:20 | call to getABool | true | 43 | 45 |
|
||||||
|
| test.cpp:61:10:61:10 | i | Case[0] | 62 | 64 |
|
||||||
|
| test.cpp:61:10:61:10 | i | Case[1] | 65 | 66 |
|
||||||
|
| test.cpp:74:10:74:10 | i | Case[0..10] | 75 | 77 |
|
||||||
|
| test.cpp:74:10:74:10 | i | Case[11..20] | 78 | 79 |
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
import cpp
|
import cpp
|
||||||
import semmle.code.cpp.controlflow.Guards
|
import semmle.code.cpp.controlflow.Guards
|
||||||
|
|
||||||
from GuardCondition guard, boolean sense, int start, int end
|
from GuardCondition guard, AbstractValue value, int start, int end
|
||||||
where
|
where
|
||||||
exists(BasicBlock block |
|
exists(BasicBlock block |
|
||||||
guard.controls(block, sense) and
|
guard.valueControls(block, value) and
|
||||||
block.hasLocationInfo(_, start, _, end, _)
|
block.hasLocationInfo(_, start, _, end, _)
|
||||||
)
|
)
|
||||||
select guard, sense, start, end
|
select guard, value, start, end
|
||||||
|
|
|
@ -52,3 +52,37 @@ bool testWithCatch0(int v)
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void use1(int);
|
||||||
|
void use2(int);
|
||||||
|
void use3(int);
|
||||||
|
|
||||||
|
void test_switches_simple(int i) {
|
||||||
|
switch(i) {
|
||||||
|
case 0:
|
||||||
|
use1(i);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
use2(i);
|
||||||
|
/* NOTE: fallthrough */
|
||||||
|
case 2:
|
||||||
|
use3(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_switches_range(int i) {
|
||||||
|
switch(i) {
|
||||||
|
case 0 ... 10:
|
||||||
|
use1(i);
|
||||||
|
break;
|
||||||
|
case 11 ... 20:
|
||||||
|
use2(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_switches_default(int i) {
|
||||||
|
switch(i) {
|
||||||
|
default:
|
||||||
|
use1(i);
|
||||||
|
}
|
||||||
|
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче