Merge branch 'main' into python/test-MaD-keyword-argument

This commit is contained in:
yoff 2024-03-22 10:56:08 +01:00 коммит произвёл GitHub
Родитель eef60c9ad2 fb4ed39d89
Коммит c520cb6d58
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
862 изменённых файлов: 70984 добавлений и 70155 удалений

Просмотреть файл

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

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше