mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge from main to resolve conflicts
This commit is contained in:
@@ -88,123 +88,46 @@
|
||||
"IR Instruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Instruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Instruction.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll"
|
||||
],
|
||||
"IR IRBlock": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRBlock.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRBlock.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll"
|
||||
],
|
||||
"IR IRVariable": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRVariable.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll"
|
||||
],
|
||||
"IR IRFunction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRFunction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRFunction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRFunction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRFunction.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRFunction.qll"
|
||||
],
|
||||
"IR Operand": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Operand.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Operand.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/Operand.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/Operand.qll"
|
||||
],
|
||||
"IR IRType": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/IRType.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/IRType.qll"
|
||||
],
|
||||
"IR IRConfiguration": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/IRConfiguration.qll"
|
||||
],
|
||||
"IR UseSoundEscapeAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/UseSoundEscapeAnalysis.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/UseSoundEscapeAnalysis.qll"
|
||||
],
|
||||
"IR IRFunctionBase": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/IRFunctionBase.qll"
|
||||
],
|
||||
"IR Operand Tag": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/OperandTag.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/OperandTag.qll"
|
||||
],
|
||||
"IR TInstruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TInstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll"
|
||||
],
|
||||
"IR TIRVariable": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/internal/TIRVariable.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Operand.qll"
|
||||
],
|
||||
"IR IR": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IR.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IR.qll"
|
||||
],
|
||||
"IR IRConsistency": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/IRConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/IRConsistency.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll"
|
||||
],
|
||||
"IR PrintIR": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/PrintIR.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/PrintIR.qll"
|
||||
],
|
||||
"IR IntegerConstant": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerConstant.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerConstant.qll"
|
||||
],
|
||||
"IR IntegerInteval": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerInterval.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerInterval.qll"
|
||||
],
|
||||
"IR IntegerPartial": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/IntegerPartial.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/IntegerPartial.qll"
|
||||
],
|
||||
"IR Overlap": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/internal/Overlap.qll",
|
||||
"csharp/ql/src/experimental/ir/internal/Overlap.qll"
|
||||
],
|
||||
"IR EdgeKind": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/EdgeKind.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/EdgeKind.qll"
|
||||
],
|
||||
"IR MemoryAccessKind": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/MemoryAccessKind.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/MemoryAccessKind.qll"
|
||||
],
|
||||
"IR TempVariableTag": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/TempVariableTag.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/TempVariableTag.qll"
|
||||
],
|
||||
"IR Opcode": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/Opcode.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/Opcode.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll"
|
||||
],
|
||||
"IR SSAConsistency": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll"
|
||||
],
|
||||
"C++ IR InstructionImports": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
@@ -252,8 +175,7 @@
|
||||
],
|
||||
"SSA AliasAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasAnalysis.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasAnalysis.qll"
|
||||
],
|
||||
"SSA PrintAliasAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintAliasAnalysis.qll",
|
||||
@@ -268,44 +190,28 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"IR SSA SimpleSSA": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SimpleSSA.qll"
|
||||
],
|
||||
"IR AliasConfiguration (unaliased_ssa)": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/AliasConfiguration.qll"
|
||||
],
|
||||
"IR SSA SSAConstruction": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll"
|
||||
],
|
||||
"IR SSA PrintSSA": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/PrintSSA.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintSSA.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/PrintSSA.qll"
|
||||
],
|
||||
"IR ValueNumberInternal": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingInternal.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/internal/ValueNumberingInternal.qll"
|
||||
],
|
||||
"C++ IR ValueNumber": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/ValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/ValueNumbering.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/ValueNumbering.qll"
|
||||
],
|
||||
"C++ IR PrintValueNumbering": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/PrintValueNumbering.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/PrintValueNumbering.qll"
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/gvn/PrintValueNumbering.qll"
|
||||
],
|
||||
"C++ IR ConstantAnalysis": [
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/constant/ConstantAnalysis.qll",
|
||||
@@ -333,38 +239,6 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/reachability/PrintDominance.qll",
|
||||
"cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/reachability/PrintDominance.qll"
|
||||
],
|
||||
"C# IR InstructionImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/InstructionImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/InstructionImports.qll"
|
||||
],
|
||||
"C# IR IRImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRImports.qll"
|
||||
],
|
||||
"C# IR IRBlockImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRBlockImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRBlockImports.qll"
|
||||
],
|
||||
"C# IR IRFunctionImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRFunctionImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRFunctionImports.qll"
|
||||
],
|
||||
"C# IR IRVariableImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/IRVariableImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/IRVariableImports.qll"
|
||||
],
|
||||
"C# IR OperandImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/OperandImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/OperandImports.qll"
|
||||
],
|
||||
"C# IR PrintIRImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/internal/PrintIRImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/PrintIRImports.qll"
|
||||
],
|
||||
"C# IR ValueNumberingImports": [
|
||||
"csharp/ql/src/experimental/ir/implementation/raw/gvn/internal/ValueNumberingImports.qll",
|
||||
"csharp/ql/src/experimental/ir/implementation/unaliased_ssa/gvn/internal/ValueNumberingImports.qll"
|
||||
],
|
||||
"C# ControlFlowReachability": [
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll"
|
||||
@@ -381,7 +255,6 @@
|
||||
"cpp/ql/lib/semmle/code/cpp/XML.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/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"
|
||||
],
|
||||
"DuplicationProblems.inc.qhelp": [
|
||||
|
||||
@@ -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
|
||||
2250
cpp/downgrades/aa7ff0ab32cd4674f6ab731d32fea64116997b05/old.dbscheme
Normal file
2250
cpp/downgrades/aa7ff0ab32cd4674f6ab731d32fea64116997b05/old.dbscheme
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
description: Introduce re-use expressions
|
||||
compatibility: partial
|
||||
expr_reuse.rel: delete
|
||||
exprs.rel: run exprs.qlo
|
||||
@@ -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.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.
|
||||
@@ -20,6 +20,44 @@ private predicate isUnreachedBlock(IRBlock block) {
|
||||
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
|
||||
* operands of logical operators but not switch statements.
|
||||
@@ -34,6 +72,15 @@ class GuardCondition extends Expr {
|
||||
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
|
||||
* entered if the value of this condition is `testIsTrue`.
|
||||
@@ -61,7 +108,9 @@ class GuardCondition extends Expr {
|
||||
* true (for `&&`) or false (for `||`) branch.
|
||||
*/
|
||||
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`. */
|
||||
cached
|
||||
@@ -88,6 +137,17 @@ class GuardCondition extends Expr {
|
||||
*/
|
||||
cached
|
||||
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 `testIsTrue`. */
|
||||
cached
|
||||
predicate comparesEq(Expr e, int k, boolean areEqual, boolean testIsTrue) { 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 +158,13 @@ private class GuardConditionFromBinaryLogicalOperator extends 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 |
|
||||
this = binop and
|
||||
lhs = binop.getLeftOperand() and
|
||||
rhs = binop.getRightOperand() and
|
||||
lhs.controls(controlled, testIsTrue) and
|
||||
rhs.controls(controlled, testIsTrue)
|
||||
lhs.valueControls(controlled, v) and
|
||||
rhs.valueControls(controlled, v)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -135,6 +195,20 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||
this.comparesEq(left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate comparesEq(Expr e, int k, boolean areEqual, boolean testIsTrue) {
|
||||
exists(boolean partIsTrue, GuardCondition part |
|
||||
this.(BinaryLogicalOperation).impliesValue(part, partIsTrue, testIsTrue)
|
||||
|
|
||||
part.comparesEq(e, k, areEqual, partIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) {
|
||||
exists(boolean testIsTrue |
|
||||
this.comparesEq(e, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,10 +220,10 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||
|
||||
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
|
||||
// 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`. */
|
||||
@@ -196,15 +270,30 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate comparesEq(Expr e, int k, boolean areEqual, boolean testIsTrue) {
|
||||
exists(Instruction i |
|
||||
i.getUnconvertedResultExpression() = e and
|
||||
ir.comparesEq(i.getAUse(), k, areEqual, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate ensuresEq(Expr e, int k, BasicBlock block, boolean areEqual) {
|
||||
exists(Instruction i, boolean testIsTrue |
|
||||
i.getUnconvertedResultExpression() = e and
|
||||
ir.comparesEq(i.getAUse(), k, areEqual, testIsTrue) and
|
||||
this.controls(block, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* `&&` 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 |
|
||||
ir.controls(irb, testIsTrue) and
|
||||
ir.valueControls(irb, v) and
|
||||
nonExcludedIRAndBasicBlock(irb, controlled) and
|
||||
not isUnreachedBlock(irb)
|
||||
)
|
||||
@@ -249,10 +338,28 @@ private predicate nonExcludedIRAndBasicBlock(IRBlock irb, BasicBlock controlled)
|
||||
*/
|
||||
cached
|
||||
class IRGuardCondition extends Instruction {
|
||||
ConditionalBranchInstruction branch;
|
||||
Instruction branch;
|
||||
|
||||
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
|
||||
@@ -282,13 +389,25 @@ class IRGuardCondition extends Instruction {
|
||||
*/
|
||||
cached
|
||||
predicate controls(IRBlock controlled, boolean testIsTrue) {
|
||||
// This condition must determine the flow of control; that is, this
|
||||
// node must be a top-level condition.
|
||||
this.controlsBlock(controlled, testIsTrue)
|
||||
this.valueControls(controlled, any(BooleanValue bv | bv.getValue() = 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
|
||||
exists(IRGuardCondition ne |
|
||||
this = ne.(LogicalNotInstruction).getUnary() and
|
||||
ne.controls(controlled, testIsTrue.booleanNot())
|
||||
succ = this.getBranchSuccessor(v) and
|
||||
(
|
||||
branch.(ConditionalBranchInstruction).getCondition() = this and
|
||||
branch.getBlock() = pred
|
||||
or
|
||||
branch.(SwitchInstruction).getExpression() = this and
|
||||
branch.getBlock() = pred
|
||||
)
|
||||
}
|
||||
|
||||
@@ -297,17 +416,12 @@ class IRGuardCondition extends Instruction {
|
||||
* the value of this condition is `testIsTrue`.
|
||||
*/
|
||||
cached
|
||||
predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) {
|
||||
pred.getASuccessor() = succ and
|
||||
this.controls(pred, testIsTrue)
|
||||
or
|
||||
succ = this.getBranchSuccessor(testIsTrue) and
|
||||
branch.getCondition() = this and
|
||||
branch.getBlock() = pred
|
||||
final predicate controlsEdge(IRBlock pred, IRBlock succ, boolean testIsTrue) {
|
||||
this.valueControlsEdge(pred, succ, any(BooleanValue bv | bv.getValue() = testIsTrue))
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* based on an edge between a block with multiple successors and a block with multiple
|
||||
@@ -321,14 +435,20 @@ class IRGuardCondition extends Instruction {
|
||||
* return x;
|
||||
* ```
|
||||
*/
|
||||
private IRBlock getBranchSuccessor(boolean testIsTrue) {
|
||||
branch.getCondition() = this and
|
||||
(
|
||||
testIsTrue = true and
|
||||
result.getFirstInstruction() = branch.getTrueSuccessor()
|
||||
private IRBlock getBranchSuccessor(AbstractValue v) {
|
||||
branch.(ConditionalBranchInstruction).getCondition() = this and
|
||||
exists(BooleanValue bv | bv = v |
|
||||
bv.getValue() = true and
|
||||
result.getFirstInstruction() = branch.(ConditionalBranchInstruction).getTrueSuccessor()
|
||||
or
|
||||
testIsTrue = false and
|
||||
result.getFirstInstruction() = branch.getFalseSuccessor()
|
||||
bv.getValue() = false and
|
||||
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()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -366,7 +486,25 @@ class IRGuardCondition extends Instruction {
|
||||
/** Holds if (determined by this guard) `left == right + k` evaluates to `areEqual` if this expression evaluates to `testIsTrue`. */
|
||||
cached
|
||||
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 `testIsTrue`. */
|
||||
cached
|
||||
predicate comparesEq(Operand op, int k, boolean areEqual, boolean testIsTrue) {
|
||||
exists(MatchValue mv |
|
||||
compares_eq(this, op, k, areEqual, mv) and
|
||||
// A match value cannot be dualized, so `testIsTrue` is always true
|
||||
testIsTrue = true
|
||||
)
|
||||
or
|
||||
exists(BooleanValue bv |
|
||||
compares_eq(this, op, k, areEqual, bv) and
|
||||
bv.getValue() = testIsTrue
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,8 +513,19 @@ class IRGuardCondition extends Instruction {
|
||||
*/
|
||||
cached
|
||||
predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) {
|
||||
exists(boolean testIsTrue |
|
||||
compares_eq(this, left, right, k, areEqual, testIsTrue) and this.controls(block, testIsTrue)
|
||||
exists(AbstractValue value |
|
||||
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 +537,31 @@ class IRGuardCondition extends Instruction {
|
||||
predicate ensuresEqEdge(
|
||||
Operand left, Operand right, int k, IRBlock pred, IRBlock succ, boolean areEqual
|
||||
) {
|
||||
exists(boolean testIsTrue |
|
||||
compares_eq(this, left, right, k, areEqual, testIsTrue) and
|
||||
this.controlsEdge(pred, succ, testIsTrue)
|
||||
exists(AbstractValue value |
|
||||
compares_eq(this, left, right, k, areEqual, value) and
|
||||
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
|
||||
* 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
|
||||
* `&&` 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
|
||||
//
|
||||
// For this block to control the block `controlled` with `testIsTrue` the
|
||||
@@ -441,7 +602,7 @@ class IRGuardCondition extends Instruction {
|
||||
// that `this` strictly dominates `controlled` so that isn't necessary to check
|
||||
// directly.
|
||||
exists(IRBlock succ |
|
||||
succ = this.getBranchSuccessor(testIsTrue) and
|
||||
succ = this.getBranchSuccessor(v) and
|
||||
this.hasDominatingEdgeTo(succ) and
|
||||
succ.dominates(controlled)
|
||||
)
|
||||
@@ -476,12 +637,14 @@ class IRGuardCondition extends Instruction {
|
||||
private IRBlock getBranchBlock() { result = branch.getBlock() }
|
||||
}
|
||||
|
||||
private ConditionalBranchInstruction get_branch_for_condition(Instruction guard) {
|
||||
result.getCondition() = guard
|
||||
private Instruction getBranchForCondition(Instruction guard) {
|
||||
result.(ConditionalBranchInstruction).getCondition() = guard
|
||||
or
|
||||
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 +653,98 @@ private ConditionalBranchInstruction get_branch_for_condition(Instruction guard)
|
||||
* Beware making mistaken logical implications here relating `areEqual` and `testIsTrue`.
|
||||
*/
|
||||
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. */
|
||||
exists(boolean eq | simple_comparison_eq(test, left, right, k, eq) |
|
||||
areEqual = true and testIsTrue = eq
|
||||
exists(AbstractValue v | simple_comparison_eq(test, left, right, k, v) |
|
||||
areEqual = true and value = v
|
||||
or
|
||||
areEqual = false and testIsTrue = eq.booleanNot()
|
||||
areEqual = false and value = v.getDualValue()
|
||||
)
|
||||
or
|
||||
// I think this is handled by forwarding in controlsBlock.
|
||||
//or
|
||||
//logical_comparison_eq(test, left, right, k, areEqual, testIsTrue)
|
||||
/* 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
|
||||
complex_eq(test, left, right, k, areEqual, testIsTrue)
|
||||
complex_eq(test, left, right, k, areEqual, value)
|
||||
or
|
||||
/* (x is true => (left == right + k)) => (!x is false => (left == right + k)) */
|
||||
exists(boolean isFalse | testIsTrue = isFalse.booleanNot() |
|
||||
compares_eq(test.(LogicalNotInstruction).getUnary(), left, right, k, areEqual, isFalse)
|
||||
exists(AbstractValue dual | value = dual.getDualValue() |
|
||||
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. */
|
||||
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
|
||||
cmp instanceof CompareEQInstruction and
|
||||
right = cmp.getRightOperand() and
|
||||
k = 0 and
|
||||
areEqual = true
|
||||
value.(BooleanValue).getValue() = true
|
||||
or
|
||||
left = cmp.getLeftOperand() and
|
||||
cmp instanceof CompareNEInstruction and
|
||||
right = cmp.getRightOperand() 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(
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -686,44 +895,61 @@ private predicate add_lt(
|
||||
// left - x == right + c => left == right + (c+x)
|
||||
// left == (right - x) + c => left == right + (c-x)
|
||||
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 |
|
||||
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()) and
|
||||
k = c + x
|
||||
)
|
||||
or
|
||||
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
|
||||
x = int_value(rhs.getRight()) and
|
||||
k = c - x
|
||||
)
|
||||
or
|
||||
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
|
||||
x = int_value(lhs.getRight()) and
|
||||
k = c + x
|
||||
)
|
||||
or
|
||||
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
|
||||
x = int_value(rhs.getRight()) and
|
||||
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 == (right + x) + c => left == right + (c+x)
|
||||
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 |
|
||||
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())
|
||||
or
|
||||
@@ -733,7 +959,7 @@ private predicate add_eq(
|
||||
)
|
||||
or
|
||||
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())
|
||||
or
|
||||
@@ -743,7 +969,7 @@ private predicate add_eq(
|
||||
)
|
||||
or
|
||||
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())
|
||||
or
|
||||
@@ -753,7 +979,7 @@ private predicate add_eq(
|
||||
)
|
||||
or
|
||||
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())
|
||||
or
|
||||
@@ -763,5 +989,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. */
|
||||
private int int_value(Instruction i) { result = i.(IntegerConstantInstruction).getValue().toInt() }
|
||||
|
||||
@@ -28,6 +28,6 @@ import cpp
|
||||
deprecated module DataFlow {
|
||||
private import semmle.code.cpp.dataflow.internal.DataFlowImplSpecific
|
||||
private import codeql.dataflow.DataFlow
|
||||
import DataFlowMake<CppOldDataFlow>
|
||||
import DataFlowMake<Location, CppOldDataFlow>
|
||||
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.TaintTrackingImplSpecific
|
||||
private import codeql.dataflow.TaintTracking
|
||||
import TaintFlowMake<CppOldDataFlow, CppOldTaintTracking>
|
||||
import TaintFlowMake<Location, CppOldDataFlow, CppOldTaintTracking>
|
||||
import semmle.code.cpp.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.Location
|
||||
private import DataFlowImplSpecific
|
||||
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 accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
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 accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
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 accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
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 accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* DEPRECATED: Use `semmle.code.cpp.dataflow.new.DataFlow` instead.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.Location
|
||||
private import DataFlowImplSpecific
|
||||
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 codeql.dataflow.internal.DataFlowImplConsistency
|
||||
|
||||
private module Input implements InputSig<CppOldDataFlow> {
|
||||
private module Input implements InputSig<Location, CppOldDataFlow> {
|
||||
predicate argHasPostUpdateExclude(Private::ArgumentNode n) {
|
||||
// Is the null pointer (or something that's not really a pointer)
|
||||
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 accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Provides C++-specific definitions for use in the data flow library.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.Location
|
||||
private import codeql.dataflow.DataFlow
|
||||
|
||||
module Private {
|
||||
@@ -15,7 +16,7 @@ module Public {
|
||||
import DataFlowUtil
|
||||
}
|
||||
|
||||
module CppOldDataFlow implements InputSig {
|
||||
module CppOldDataFlow implements InputSig<Location> {
|
||||
import Private
|
||||
import Public
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ class Node extends TNode {
|
||||
* For more information, see
|
||||
* [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
|
||||
) {
|
||||
this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
|
||||
@@ -4,9 +4,10 @@
|
||||
* Provides C++-specific definitions for use in the taint tracking library.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.Location
|
||||
private import codeql.dataflow.TaintTracking
|
||||
private import DataFlowImplSpecific
|
||||
|
||||
module CppOldTaintTracking implements InputSig<CppOldDataFlow> {
|
||||
module CppOldTaintTracking implements InputSig<Location, CppOldDataFlow> {
|
||||
import TaintTrackingUtil
|
||||
}
|
||||
|
||||
@@ -28,6 +28,6 @@ import cpp
|
||||
module DataFlow {
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||
private import codeql.dataflow.DataFlow
|
||||
import DataFlowMake<CppDataFlow>
|
||||
import DataFlowMake<Location, CppDataFlow>
|
||||
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.TaintTrackingImplSpecific
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1322,3 +1322,23 @@ class CoYieldExpr extends UnaryOperation, @co_yield {
|
||||
|
||||
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 {
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific
|
||||
private import codeql.dataflow.DataFlow
|
||||
import DataFlowMake<CppDataFlow>
|
||||
import DataFlowMake<Location, CppDataFlow>
|
||||
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.TaintTrackingImplSpecific
|
||||
private import codeql.dataflow.TaintTracking
|
||||
import TaintFlowMake<CppDataFlow, CppTaintTracking>
|
||||
import TaintFlowMake<Location, CppDataFlow, CppTaintTracking>
|
||||
import semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
private import semmle.code.cpp.Location
|
||||
private import DataFlowImplSpecific
|
||||
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 accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
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 accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
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 accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
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 accessPathLimit() { result = 5 }
|
||||
|
||||
FlowFeature getAFeature() { result = any(Configuration config).getAFeature() }
|
||||
|
||||
predicate sourceGrouping(Node source, string sourceGroup) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
private import semmle.code.cpp.Location
|
||||
private import DataFlowImplSpecific
|
||||
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 codeql.dataflow.internal.DataFlowImplConsistency
|
||||
|
||||
private module Input implements InputSig<CppDataFlow> {
|
||||
private module Input implements InputSig<Location, CppDataFlow> {
|
||||
predicate argHasPostUpdateExclude(Private::ArgumentNode n) {
|
||||
// The rules for whether an IR argument gets a post-update node are too
|
||||
// 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 semmle.code.cpp.Location
|
||||
|
||||
module Private {
|
||||
import DataFlowPrivate
|
||||
@@ -13,7 +14,7 @@ module Public {
|
||||
import DataFlowUtil
|
||||
}
|
||||
|
||||
module CppDataFlow implements InputSig {
|
||||
module CppDataFlow implements InputSig<Location> {
|
||||
import Private
|
||||
import Public
|
||||
|
||||
|
||||
@@ -448,7 +448,7 @@ class Node extends TIRDataFlowNode {
|
||||
* For more information, see
|
||||
* [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
|
||||
) {
|
||||
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`. */
|
||||
predicate exprNodeShouldBeOperand(OperandNode node, Expr e, int n) {
|
||||
not exprNodeShouldBeIndirectOperand(_, e, n) and
|
||||
exists(Instruction def |
|
||||
unique( | | getAUse(def)) = node.getOperand() and
|
||||
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) {
|
||||
exists(CallInstruction call |
|
||||
call.getStaticCallTarget() instanceof Constructor and
|
||||
@@ -1359,6 +1376,7 @@ private predicate exprNodeShouldBeIndirectOutNode(IndirectArgumentOutNode node,
|
||||
predicate exprNodeShouldBeInstruction(Node node, Expr e, int n) {
|
||||
not exprNodeShouldBeOperand(_, e, n) and
|
||||
not exprNodeShouldBeIndirectOutNode(_, e, n) and
|
||||
not exprNodeShouldBeIndirectOperand(_, e, n) and
|
||||
e = getConvertedResultExpression(node.asInstruction(), n)
|
||||
}
|
||||
|
||||
@@ -1391,7 +1409,8 @@ abstract private class ExprNodeBase extends Node {
|
||||
private predicate exprNodeShouldBe(Expr e, int n) {
|
||||
exprNodeShouldBeInstruction(_, e, n) or
|
||||
exprNodeShouldBeOperand(_, e, n) or
|
||||
exprNodeShouldBeIndirectOutNode(_, e, n)
|
||||
exprNodeShouldBeIndirectOutNode(_, e, n) or
|
||||
exprNodeShouldBeIndirectOperand(_, e, n)
|
||||
}
|
||||
|
||||
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) }
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -10,13 +10,12 @@ private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs as FIO
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||
private import DataFlowPrivate
|
||||
private import ssa0.SsaInternals as SsaInternals0
|
||||
import SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
cached
|
||||
private newtype TSourceVariable =
|
||||
TMkSourceVariable(SsaInternals0::SourceVariable base, int ind) {
|
||||
TMkSourceVariable(BaseSourceVariable base, int ind) {
|
||||
ind = [0 .. countIndirectionsForCppType(base.getLanguageType()) + 1]
|
||||
}
|
||||
|
||||
@@ -30,7 +29,7 @@ private module SourceVariables {
|
||||
}
|
||||
|
||||
class SourceVariable extends TSourceVariable {
|
||||
SsaInternals0::SourceVariable base;
|
||||
BaseSourceVariable base;
|
||||
int ind;
|
||||
|
||||
SourceVariable() { this = TMkSourceVariable(base, ind) }
|
||||
@@ -42,7 +41,7 @@ private module SourceVariables {
|
||||
* Gets the base source variable (i.e., the variable without any
|
||||
* indirections) of this source variable.
|
||||
*/
|
||||
SsaInternals0::SourceVariable getBaseVariable() { result = base }
|
||||
BaseSourceVariable getBaseVariable() { result = base }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = repeatStars(this.getIndirection()) + base.toString() }
|
||||
@@ -104,17 +103,9 @@ predicate hasRawIndirectInstruction(Instruction instr, int indirectionIndex) {
|
||||
|
||||
cached
|
||||
private newtype TDefOrUseImpl =
|
||||
TDefAddressImpl(BaseIRVariable v) or
|
||||
TDefImpl(BaseSourceVariableInstruction base, Operand address, int indirectionIndex) {
|
||||
isDef(_, _, address, base, _, indirectionIndex) and
|
||||
(
|
||||
// We only include the definition if the SSA pruning stage
|
||||
// concluded that the definition is live after the write.
|
||||
any(SsaInternals0::Def def).getAddressOperand() = address
|
||||
or
|
||||
// Since the pruning stage doesn't know about global variables we can't use the above check to
|
||||
// rule out dead assignments to globals.
|
||||
base.(VariableAddressInstruction).getAstVariable() instanceof GlobalLikeVariable
|
||||
)
|
||||
isDef(_, _, address, base, _, indirectionIndex)
|
||||
} or
|
||||
TUseImpl(BaseSourceVariableInstruction base, Operand operand, int indirectionIndex) {
|
||||
isUse(_, operand, base, _, indirectionIndex) and
|
||||
@@ -133,8 +124,7 @@ private newtype TDefOrUseImpl =
|
||||
TIteratorDef(
|
||||
Operand iteratorDerefAddress, BaseSourceVariableInstruction container, int indirectionIndex
|
||||
) {
|
||||
isIteratorDef(container, iteratorDerefAddress, _, _, indirectionIndex) and
|
||||
any(SsaInternals0::Def def | def.isIteratorDef()).getAddressOperand() = iteratorDerefAddress
|
||||
isIteratorDef(container, iteratorDerefAddress, _, _, indirectionIndex)
|
||||
} or
|
||||
TIteratorUse(
|
||||
Operand iteratorAddress, BaseSourceVariableInstruction container, int indirectionIndex
|
||||
@@ -267,24 +257,64 @@ private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVari
|
||||
}
|
||||
|
||||
abstract class DefImpl extends DefOrUseImpl {
|
||||
Operand address;
|
||||
int ind;
|
||||
|
||||
bindingset[ind]
|
||||
DefImpl() { any() }
|
||||
|
||||
abstract int getIndirection();
|
||||
|
||||
abstract Node0Impl getValue();
|
||||
|
||||
abstract predicate isCertain();
|
||||
|
||||
Operand getAddressOperand() { result = address }
|
||||
|
||||
override int getIndirectionIndex() { result = ind }
|
||||
|
||||
override string toString() { result = "Def of " + this.getSourceVariable() }
|
||||
|
||||
abstract int getIndirection();
|
||||
|
||||
abstract predicate isCertain();
|
||||
|
||||
abstract Node0Impl getValue();
|
||||
}
|
||||
|
||||
/** An initial definition of an `IRVariable`'s address. */
|
||||
private class DefAddressImpl extends DefImpl, TDefAddressImpl {
|
||||
BaseIRVariable v;
|
||||
|
||||
DefAddressImpl() {
|
||||
this = TDefAddressImpl(v) and
|
||||
ind = 0
|
||||
}
|
||||
|
||||
final override int getIndirection() { result = 0 }
|
||||
|
||||
final override predicate isCertain() { any() }
|
||||
|
||||
final override Node0Impl getValue() { none() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
block = v.getIRVariable().getEnclosingIRFunction().getEntryBlock() and
|
||||
index = 0
|
||||
}
|
||||
|
||||
override Cpp::Location getLocation() { result = v.getIRVariable().getLocation() }
|
||||
|
||||
final override SourceVariable getSourceVariable() {
|
||||
result.getBaseVariable() = v and
|
||||
result.getIndirection() = 0
|
||||
}
|
||||
|
||||
final override BaseSourceVariableInstruction getBase() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An SSA definition that has an associated `Operand` representing the address
|
||||
* that is being written to.
|
||||
*/
|
||||
abstract private class OperandBasedDef extends DefImpl {
|
||||
Operand address;
|
||||
|
||||
bindingset[ind]
|
||||
OperandBasedDef() { any() }
|
||||
|
||||
Operand getAddressOperand() { result = address }
|
||||
|
||||
override Cpp::Location getLocation() { result = this.getAddressOperand().getUse().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
@@ -292,7 +322,7 @@ abstract class DefImpl extends DefOrUseImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private class DirectDef extends DefImpl, TDefImpl {
|
||||
private class DirectDef extends OperandBasedDef, TDefImpl {
|
||||
BaseSourceVariableInstruction base;
|
||||
|
||||
DirectDef() { this = TDefImpl(base, address, ind) }
|
||||
@@ -306,7 +336,7 @@ private class DirectDef extends DefImpl, TDefImpl {
|
||||
override predicate isCertain() { isDef(true, _, address, base, _, ind) }
|
||||
}
|
||||
|
||||
private class IteratorDef extends DefImpl, TIteratorDef {
|
||||
private class IteratorDef extends OperandBasedDef, TIteratorDef {
|
||||
BaseSourceVariableInstruction container;
|
||||
|
||||
IteratorDef() { this = TIteratorDef(address, container, ind) }
|
||||
@@ -984,17 +1014,6 @@ predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a write at index `i` in basic block `bb` to variable `v` that's
|
||||
* subsequently read (as determined by the SSA pruning stage).
|
||||
*/
|
||||
private predicate variableWriteCand(IRBlock bb, int i, SourceVariable v) {
|
||||
exists(SsaInternals0::Def def, SsaInternals0::SourceVariable v0 |
|
||||
def.asDefOrUse().hasIndexInBlock(bb, i, v0) and
|
||||
v0 = v.getBaseVariable()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sourceVariableIsGlobal(
|
||||
SourceVariable sv, GlobalLikeVariable global, IRFunction func, int indirectionIndex
|
||||
) {
|
||||
@@ -1018,16 +1037,14 @@ private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
(
|
||||
variableWriteCand(bb, i, v) or
|
||||
sourceVariableIsGlobal(v, _, _, _)
|
||||
) and
|
||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
||||
if def.isCertain() then certain = true else certain = false
|
||||
)
|
||||
or
|
||||
exists(GlobalDefImpl global |
|
||||
global.hasIndexInBlock(bb, i, v) and
|
||||
certain = true
|
||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
||||
if def.isCertain() then certain = true else certain = false
|
||||
)
|
||||
or
|
||||
exists(GlobalDefImpl global |
|
||||
global.hasIndexInBlock(bb, i, v) and
|
||||
certain = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1202,7 +1219,7 @@ class UseOrPhi extends SsaDefOrUse {
|
||||
class Def extends DefOrUse {
|
||||
override DefImpl defOrUse;
|
||||
|
||||
Operand getAddressOperand() { result = defOrUse.getAddressOperand() }
|
||||
Operand getAddressOperand() { result = defOrUse.(OperandBasedDef).getAddressOperand() }
|
||||
|
||||
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
private import codeql.dataflow.TaintTracking
|
||||
private import DataFlowImplSpecific
|
||||
private import semmle.code.cpp.Location
|
||||
|
||||
module CppTaintTracking implements InputSig<CppDataFlow> {
|
||||
module CppTaintTracking implements InputSig<Location, CppDataFlow> {
|
||||
import TaintTrackingUtil
|
||||
}
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
/**
|
||||
* This module defines an initial SSA pruning stage that doesn't take
|
||||
* indirections into account.
|
||||
*/
|
||||
|
||||
private import codeql.ssa.Ssa as SsaImplCommon
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
|
||||
private import semmle.code.cpp.models.interfaces.Allocation as Alloc
|
||||
private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects
|
||||
private import semmle.code.cpp.ir.internal.IRCppLanguage
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.SsaInternalsCommon
|
||||
|
||||
private module SourceVariables {
|
||||
class SourceVariable extends BaseSourceVariable {
|
||||
/**
|
||||
* Gets the base source variable of this `SourceVariable`.
|
||||
*/
|
||||
BaseSourceVariable getBaseVariable() { result = this }
|
||||
}
|
||||
}
|
||||
|
||||
import SourceVariables
|
||||
|
||||
private newtype TDefOrUseImpl =
|
||||
TDefImpl(Operand address) { isDef(_, _, address, _, _, _) } or
|
||||
TUseImpl(Operand operand) {
|
||||
isUse(_, operand, _, _, _) and
|
||||
not isDef(true, _, operand, _, _, _)
|
||||
} or
|
||||
TIteratorDef(BaseSourceVariableInstruction container, Operand iteratorAddress) {
|
||||
isIteratorDef(container, iteratorAddress, _, _, _)
|
||||
} or
|
||||
TIteratorUse(BaseSourceVariableInstruction container, Operand iteratorAddress) {
|
||||
isIteratorUse(container, iteratorAddress, _, _)
|
||||
} or
|
||||
TFinalParameterUse(Parameter p) {
|
||||
any(Indirection indirection).getType() = p.getUnspecifiedType()
|
||||
}
|
||||
|
||||
abstract private class DefOrUseImpl extends TDefOrUseImpl {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the block of this definition or use. */
|
||||
final IRBlock getBlock() { this.hasIndexInBlock(result, _) }
|
||||
|
||||
/** Holds if this definition or use has index `index` in block `block`. */
|
||||
abstract predicate hasIndexInBlock(IRBlock block, int index);
|
||||
|
||||
final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) {
|
||||
this.hasIndexInBlock(block, index) and
|
||||
sv = this.getSourceVariable()
|
||||
}
|
||||
|
||||
/** Gets the location of this element. */
|
||||
abstract Cpp::Location getLocation();
|
||||
|
||||
abstract BaseSourceVariableInstruction getBase();
|
||||
|
||||
final BaseSourceVariable getBaseSourceVariable() {
|
||||
result = this.getBase().getBaseSourceVariable()
|
||||
}
|
||||
|
||||
/** Gets the variable that is defined or used. */
|
||||
final SourceVariable getSourceVariable() {
|
||||
result.getBaseVariable() = this.getBaseSourceVariable()
|
||||
}
|
||||
|
||||
abstract predicate isCertain();
|
||||
}
|
||||
|
||||
abstract class DefImpl extends DefOrUseImpl {
|
||||
Operand address;
|
||||
|
||||
Operand getAddressOperand() { result = address }
|
||||
|
||||
abstract Node0Impl getValue();
|
||||
|
||||
override string toString() { result = address.toString() }
|
||||
|
||||
override Cpp::Location getLocation() { result = this.getAddressOperand().getLocation() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
this.getAddressOperand().getUse() = block.getInstruction(index)
|
||||
}
|
||||
}
|
||||
|
||||
private class DirectDef extends DefImpl, TDefImpl {
|
||||
DirectDef() { this = TDefImpl(address) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { isDef(_, _, address, result, _, _) }
|
||||
|
||||
override Node0Impl getValue() { isDef(_, result, address, _, _, _) }
|
||||
|
||||
override predicate isCertain() { isDef(true, _, address, _, _, _) }
|
||||
}
|
||||
|
||||
private class IteratorDef extends DefImpl, TIteratorDef {
|
||||
BaseSourceVariableInstruction container;
|
||||
|
||||
IteratorDef() { this = TIteratorDef(container, address) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { result = container }
|
||||
|
||||
override Node0Impl getValue() { isIteratorDef(_, address, result, _, _) }
|
||||
|
||||
override predicate isCertain() { none() }
|
||||
}
|
||||
|
||||
abstract class UseImpl extends DefOrUseImpl { }
|
||||
|
||||
abstract private class OperandBasedUse extends UseImpl {
|
||||
Operand operand;
|
||||
|
||||
override string toString() { result = operand.toString() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
// Ideally, this would just be implemented as:
|
||||
// ```
|
||||
// operand.getUse() = block.getInstruction(index)
|
||||
// ```
|
||||
// but because the IR generated for a snippet such as
|
||||
// ```
|
||||
// int x = *p++;
|
||||
// ```
|
||||
// looks like
|
||||
// ```
|
||||
// r1(glval<int>) = VariableAddress[x] :
|
||||
// r2(glval<int *>) = VariableAddress[p] :
|
||||
// r3(int *) = Load[p] : &:r2, m1
|
||||
// r4(int) = Constant[1] :
|
||||
// r5(int *) = PointerAdd[4] : r3, r4
|
||||
// m3(int *) = Store[p] : &:r2, r5
|
||||
// r6(int *) = CopyValue : r3
|
||||
// r7(int) = Load[?] : &:r6, ~m2
|
||||
// m2(int) = Store[x] : &:r1, r7
|
||||
// ```
|
||||
// we need to ensure that the `r3` operand of the `CopyValue` instruction isn't seen as a fresh use
|
||||
// of `p` that happens after the increment. So if the base instruction of this use comes from a
|
||||
// post-fix crement operation we set the index of the SSA use that wraps the `r3` operand at the
|
||||
// `CopyValue` instruction to be the same index as the `r3` operand at the `PointerAdd` instruction.
|
||||
// This ensures that the SSA library doesn't create flow from the `PointerAdd` to `r6`.
|
||||
exists(BaseSourceVariableInstruction base | base = this.getBase() |
|
||||
if base.getAst() = any(Cpp::PostfixCrementOperation c).getOperand()
|
||||
then
|
||||
exists(Operand op |
|
||||
op =
|
||||
min(Operand cand, int i |
|
||||
isUse(_, cand, base, _, _) and
|
||||
block.getInstruction(i) = cand.getUse()
|
||||
|
|
||||
cand order by i
|
||||
) and
|
||||
block.getInstruction(index) = op.getUse()
|
||||
)
|
||||
else operand.getUse() = block.getInstruction(index)
|
||||
)
|
||||
}
|
||||
|
||||
final override Cpp::Location getLocation() { result = operand.getLocation() }
|
||||
}
|
||||
|
||||
private class DirectUse extends OperandBasedUse, TUseImpl {
|
||||
DirectUse() { this = TUseImpl(operand) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { isUse(_, operand, result, _, _) }
|
||||
|
||||
override predicate isCertain() { isUse(true, operand, _, _, _) }
|
||||
}
|
||||
|
||||
private class IteratorUse extends OperandBasedUse, TIteratorUse {
|
||||
BaseSourceVariableInstruction container;
|
||||
|
||||
IteratorUse() { this = TIteratorUse(container, operand) }
|
||||
|
||||
override BaseSourceVariableInstruction getBase() { result = container }
|
||||
|
||||
override predicate isCertain() { none() }
|
||||
}
|
||||
|
||||
private class FinalParameterUse extends UseImpl, TFinalParameterUse {
|
||||
Parameter p;
|
||||
|
||||
FinalParameterUse() { this = TFinalParameterUse(p) }
|
||||
|
||||
override string toString() { result = p.toString() }
|
||||
|
||||
final override predicate hasIndexInBlock(IRBlock block, int index) {
|
||||
// Ideally, this should always be a `ReturnInstruction`, but if
|
||||
// someone forgets to write a `return` statement in a function
|
||||
// with a non-void return type we generate an `UnreachedInstruction`.
|
||||
// In this case we still want to generate flow out of such functions
|
||||
// if they write to a parameter. So we pick the index of the
|
||||
// `UnreachedInstruction` as the index of this use.
|
||||
// Note that a function may have both a `ReturnInstruction` and an
|
||||
// `UnreachedInstruction`. If that's the case this predicate will
|
||||
// return multiple results. I don't think this is detrimental to
|
||||
// performance, however.
|
||||
exists(Instruction return |
|
||||
return instanceof ReturnInstruction or
|
||||
return instanceof UnreachedInstruction
|
||||
|
|
||||
block.getInstruction(index) = return and
|
||||
return.getEnclosingFunction() = p.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
final override Cpp::Location getLocation() {
|
||||
// Parameters can have multiple locations. When there's a unique location we use
|
||||
// that one, but if multiple locations exist we default to an unknown location.
|
||||
result = unique( | | p.getLocation())
|
||||
or
|
||||
not exists(unique( | | p.getLocation())) and
|
||||
result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override BaseSourceVariableInstruction getBase() {
|
||||
exists(InitializeParameterInstruction init |
|
||||
init.getParameter() = p and
|
||||
// This is always a `VariableAddressInstruction`
|
||||
result = init.getAnOperand().getDef()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isCertain() { any() }
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
import InputSigCommon
|
||||
import SourceVariables
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th write in block `bb` writes to the variable `v`.
|
||||
* `certain` is `true` if the write is guaranteed to overwrite the entire variable.
|
||||
*/
|
||||
predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
DataFlowImplCommon::forceCachingInSameStage() and
|
||||
exists(DefImpl def | def.hasIndexInBlock(bb, i, v) |
|
||||
if def.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the `i`'th read in block `bb` reads to the variable `v`.
|
||||
* `certain` is `true` if the read is guaranteed.
|
||||
*/
|
||||
predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) {
|
||||
exists(UseImpl use | use.hasIndexInBlock(bb, i, v) |
|
||||
if use.isCertain() then certain = true else certain = false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TSsaDefOrUse =
|
||||
TDefOrUse(DefOrUseImpl defOrUse) {
|
||||
defOrUse instanceof UseImpl
|
||||
or
|
||||
// If `defOrUse` is a definition we only include it if the
|
||||
// SSA library concludes that it's live after the write.
|
||||
exists(DefinitionExt def, SourceVariable sv, IRBlock bb, int i |
|
||||
def.definesAt(sv, bb, i, _) and
|
||||
defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv)
|
||||
)
|
||||
} or
|
||||
TPhi(PhiNode phi)
|
||||
|
||||
abstract private class SsaDefOrUse extends TSsaDefOrUse {
|
||||
string toString() { result = "SsaDefOrUse" }
|
||||
|
||||
DefOrUseImpl asDefOrUse() { none() }
|
||||
|
||||
PhiNode asPhi() { none() }
|
||||
|
||||
abstract Location getLocation();
|
||||
}
|
||||
|
||||
class DefOrUse extends TDefOrUse, SsaDefOrUse {
|
||||
DefOrUseImpl defOrUse;
|
||||
|
||||
DefOrUse() { this = TDefOrUse(defOrUse) }
|
||||
|
||||
final override DefOrUseImpl asDefOrUse() { result = defOrUse }
|
||||
|
||||
final override Location getLocation() { result = defOrUse.getLocation() }
|
||||
|
||||
final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() }
|
||||
}
|
||||
|
||||
class Phi extends TPhi, SsaDefOrUse {
|
||||
PhiNode phi;
|
||||
|
||||
Phi() { this = TPhi(phi) }
|
||||
|
||||
final override PhiNode asPhi() { result = phi }
|
||||
|
||||
final override Location getLocation() { result = phi.getBasicBlock().getLocation() }
|
||||
}
|
||||
|
||||
class UseOrPhi extends SsaDefOrUse {
|
||||
UseOrPhi() {
|
||||
this.asDefOrUse() instanceof UseImpl
|
||||
or
|
||||
this instanceof Phi
|
||||
}
|
||||
|
||||
final override Location getLocation() {
|
||||
result = this.asDefOrUse().getLocation() or result = this.(Phi).getLocation()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
result = this.asDefOrUse().toString()
|
||||
or
|
||||
this instanceof Phi and
|
||||
result = "Phi"
|
||||
}
|
||||
}
|
||||
|
||||
class Def extends DefOrUse {
|
||||
override DefImpl defOrUse;
|
||||
|
||||
Operand getAddressOperand() { result = defOrUse.getAddressOperand() }
|
||||
|
||||
Instruction getAddress() { result = this.getAddressOperand().getDef() }
|
||||
|
||||
Node0Impl getValue() { result = defOrUse.getValue() }
|
||||
|
||||
override string toString() { result = this.asDefOrUse().toString() }
|
||||
|
||||
BaseSourceVariableInstruction getBase() { result = defOrUse.getBase() }
|
||||
|
||||
predicate isIteratorDef() { defOrUse instanceof IteratorDef }
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<Location, SsaInput>;
|
||||
|
||||
class PhiNode extends SsaImpl::DefinitionExt {
|
||||
PhiNode() {
|
||||
this instanceof SsaImpl::PhiNode or
|
||||
this instanceof SsaImpl::PhiReadNode
|
||||
}
|
||||
}
|
||||
|
||||
class DefinitionExt = SsaImpl::DefinitionExt;
|
||||
@@ -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.
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -96,6 +96,14 @@ private predicate ignoreExprAndDescendants(Expr expr) {
|
||||
exists(BuiltInVarArgsStart vaStartExpr |
|
||||
vaStartExpr.getLastNamedParameter().getFullyConverted() = expr
|
||||
)
|
||||
or
|
||||
// suppress destructors of temporary variables until proper support is added for them.
|
||||
exists(Expr parent | parent.getAnImplicitDestructorCall() = expr)
|
||||
or
|
||||
exists(Stmt parent |
|
||||
parent.getAnImplicitDestructorCall() = expr and
|
||||
expr.(DestructorCall).getQualifier() instanceof ReuseExpr
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -744,9 +752,13 @@ newtype TTranslatedElement =
|
||||
// The declaration/initialization part of a `ConditionDeclExpr`
|
||||
TTranslatedConditionDecl(ConditionDeclExpr expr) { not ignoreExpr(expr) } or
|
||||
// The side effects of a `Call`
|
||||
TTranslatedCallSideEffects(CallOrAllocationExpr expr) { not ignoreSideEffects(expr) } or
|
||||
TTranslatedCallSideEffects(CallOrAllocationExpr expr) {
|
||||
not ignoreExpr(expr) and
|
||||
not ignoreSideEffects(expr)
|
||||
} or
|
||||
// The non-argument-specific side effect of a `Call`
|
||||
TTranslatedCallSideEffect(Expr expr, SideEffectOpcode opcode) {
|
||||
not ignoreExpr(expr) and
|
||||
not ignoreSideEffects(expr) and
|
||||
opcode = getCallSideEffectOpcode(expr)
|
||||
} or
|
||||
@@ -764,6 +776,7 @@ newtype TTranslatedElement =
|
||||
// Constructor calls lack a qualifier (`this`) expression, so we need to handle the side effects
|
||||
// on `*this` without an `Expr`.
|
||||
TTranslatedStructorQualifierSideEffect(Call call, SideEffectOpcode opcode) {
|
||||
not ignoreExpr(call) and
|
||||
not ignoreSideEffects(call) and
|
||||
call instanceof ConstructorCall and
|
||||
opcode = getASideEffectOpcode(call, -1)
|
||||
|
||||
@@ -97,19 +97,6 @@ abstract class TranslatedExpr extends TranslatedElement {
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate hasAnImplicitDestructorCall() {
|
||||
exists(expr.getAnImplicitDestructorCall())
|
||||
}
|
||||
|
||||
final override int getFirstDestructorCallIndex() {
|
||||
not this.handlesDestructorsExplicitly() and
|
||||
(
|
||||
result = max(int childId | exists(this.getChildInternal(childId))) + 1
|
||||
or
|
||||
not exists(this.getChildInternal(_)) and result = 0
|
||||
)
|
||||
}
|
||||
|
||||
final override Locatable getAst() { result = expr }
|
||||
|
||||
final override Declaration getFunction() { result = getEnclosingDeclaration(expr) }
|
||||
|
||||
@@ -248,9 +248,19 @@ abstract class TranslatedStmt extends TranslatedElement, TTranslatedStmt {
|
||||
final override TranslatedElement getChild(int id) {
|
||||
result = this.getChildInternal(id)
|
||||
or
|
||||
exists(int destructorIndex |
|
||||
exists(int destructorIndex, int tempDestructorCount |
|
||||
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() {
|
||||
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() }
|
||||
|
||||
@@ -36,7 +36,9 @@ private class MallocAllocationFunction extends AllocationFunction {
|
||||
"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_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
|
||||
sizeArg = 0
|
||||
or
|
||||
@@ -139,7 +141,9 @@ private class ReallocAllocationFunction extends AllocationFunction, TaintFunctio
|
||||
// --- Windows COM allocation
|
||||
"CoTaskMemRealloc", // CoTaskMemRealloc(ptr, size)
|
||||
// --- 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
|
||||
sizeArg = 1 and
|
||||
reallocArg = 0
|
||||
|
||||
@@ -20,8 +20,10 @@ private class StandardDeallocationFunction extends DeallocationFunction {
|
||||
freedArg = 0
|
||||
or
|
||||
this.hasGlobalName([
|
||||
// --- OpenSSL memory allocation
|
||||
"CRYPTO_free", "CRYPTO_secure_free"
|
||||
// --- OpenSSL memory deallocation
|
||||
"CRYPTO_free", "CRYPTO_secure_free",
|
||||
// --- glib memory deallocation
|
||||
"g_free"
|
||||
]) and
|
||||
freedArg = 0
|
||||
or
|
||||
|
||||
@@ -9,6 +9,8 @@ import cpp
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
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.
|
||||
@@ -438,7 +440,7 @@ private class IteratorAssignmentMemberOperatorModel extends IteratorAssignmentMe
|
||||
* A `begin` or `end` member function, or a related member function, that
|
||||
* returns an iterator.
|
||||
*/
|
||||
private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetIteratorFunction {
|
||||
class BeginOrEndFunction extends MemberFunction {
|
||||
BeginOrEndFunction() {
|
||||
this.hasName([
|
||||
"begin", "cbegin", "rbegin", "crbegin", "end", "cend", "rend", "crend", "before_begin",
|
||||
@@ -446,7 +448,11 @@ private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetItera
|
||||
]) and
|
||||
this.getType().getUnspecifiedType() instanceof Iterator
|
||||
}
|
||||
}
|
||||
|
||||
private class BeginOrEndFunctionModels extends BeginOrEndFunction, TaintFunction,
|
||||
GetIteratorFunction, AliasFunction, SideEffectFunction
|
||||
{
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isQualifierObject() and
|
||||
output.isReturnValue()
|
||||
@@ -456,6 +462,22 @@ private class BeginOrEndFunction extends MemberFunction, TaintFunction, GetItera
|
||||
input.isQualifierObject() and
|
||||
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[]`.
|
||||
*/
|
||||
private class StdSequenceContainerAt extends TaintFunction {
|
||||
class StdSequenceContainerAt extends MemberFunction {
|
||||
StdSequenceContainerAt() {
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Array or
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Deque or
|
||||
this.getClassAndName(["at", "operator[]"]) instanceof Vector
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceContainerAtModel extends StdSequenceContainerAt, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to referenced return value
|
||||
input.isQualifierObject() and
|
||||
|
||||
@@ -129,9 +129,11 @@ private class StdMapMerge extends TaintFunction {
|
||||
/**
|
||||
* The standard map functions `at` and `operator[]`.
|
||||
*/
|
||||
private class StdMapAt extends TaintFunction {
|
||||
class StdMapAt extends MemberFunction {
|
||||
StdMapAt() { this.getClassAndName(["at", "operator[]"]) instanceof MapOrUnorderedMap }
|
||||
}
|
||||
|
||||
private class StdMapAtModels extends StdMapAt, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from qualifier to referenced return value
|
||||
input.isQualifierObject() and
|
||||
|
||||
@@ -1513,6 +1513,11 @@ exprs(
|
||||
int location: @location_expr ref
|
||||
);
|
||||
|
||||
expr_reuse(
|
||||
int reuse: @expr ref,
|
||||
int original: @expr ref
|
||||
)
|
||||
|
||||
/*
|
||||
case @value.category of
|
||||
1 = prval
|
||||
@@ -1741,6 +1746,7 @@ case @expr.kind of
|
||||
| 360 = @isunsigned
|
||||
| 361 = @isvoid
|
||||
| 362 = @isvolatile
|
||||
| 363 = @reuseexpr
|
||||
;
|
||||
|
||||
@var_args_expr = @vastartexpr
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Introduce re-use expressions
|
||||
compatibility: backwards
|
||||
@@ -11,7 +11,7 @@ private predicate exprInBooleanContext(Expr e) {
|
||||
exists(IRGuardCondition gc |
|
||||
exists(Instruction i |
|
||||
i.getUnconvertedResultExpression() = e and
|
||||
gc.comparesEq(valueNumber(i).getAUse(), zero(), 0, _, _)
|
||||
gc.comparesEq(valueNumber(i).getAUse(), 0, _, _)
|
||||
)
|
||||
or
|
||||
gc.getUnconvertedResultExpression() = e
|
||||
@@ -36,10 +36,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`.
|
||||
*/
|
||||
@@ -47,7 +43,7 @@ private predicate checkedForEof(ScanfFunctionCall call) {
|
||||
exists(IRGuardCondition gc |
|
||||
exists(Instruction i | i.getUnconvertedResultExpression() = call |
|
||||
// call == EOF
|
||||
gc.comparesEq(valueNumber(i).getAUse(), eof(), 0, _, _)
|
||||
gc.comparesEq(valueNumber(i).getAUse(), getEofValue().toInt(), _, _)
|
||||
or
|
||||
// call < 0 (EOF is guaranteed to be negative)
|
||||
gc.comparesLt(valueNumber(i).getAUse(), zero(), 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.
|
||||
* This is defined as either:
|
||||
@@ -69,7 +100,9 @@ predicate isNonConst(DataFlow::Node node) {
|
||||
// Parameters of uncalled functions that aren't const
|
||||
exists(UncalledFunction f, Parameter p |
|
||||
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`
|
||||
// not ignoring it will result in path redundancies
|
||||
(f.getName() = "main" implies p != f.getParameter(1))
|
||||
@@ -82,30 +115,27 @@ predicate isNonConst(DataFlow::Node node) {
|
||||
// are considered as possible non-const sources
|
||||
// The function's output must also not be const to be considered a non-const source
|
||||
exists(Function func, CallInstruction call |
|
||||
// NOTE: could use `Call` getAnArgument() instead of `CallInstruction` but requires two
|
||||
// variables representing the same call in ordoer to use `callOutput` below.
|
||||
exists(Expr arg |
|
||||
call.getPositionalArgumentOperand(_).getDef().getUnconvertedResultExpression() = arg and
|
||||
arg = node.asDefiningArgument()
|
||||
)
|
||||
or
|
||||
call.getUnconvertedResultExpression() = node.asIndirectExpr()
|
||||
not func.hasDefinition() and
|
||||
func = call.getStaticCallTarget()
|
||||
|
|
||||
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 |
|
||||
// NOTE: we must include dataflow and taintflow. e.g., including only dataflow we will find sprintf
|
||||
// variant function's output are now possible non-const sources
|
||||
pragma[only_bind_out](func).(DataFlowFunction).hasDataFlow(_, output) or
|
||||
pragma[only_bind_out](func).(TaintFunction).hasTaintFlow(_, output)
|
||||
|
|
||||
dataFlowOrTaintFlowFunction(func, output) and
|
||||
output.isReturnValueDeref(_) and
|
||||
node = callOutput(call, output)
|
||||
)
|
||||
) and
|
||||
not exists(Call c |
|
||||
c.getTarget().hasDefinition() and
|
||||
if node instanceof DataFlow::DefinitionByReferenceNode
|
||||
then c.getAnArgument() = node.asDefiningArgument()
|
||||
else c = [node.asExpr(), node.asIndirectExpr()]
|
||||
or
|
||||
// Case 2: It's a known dataflow or taintflow function with flow to an output parameter
|
||||
exists(int i |
|
||||
call.getPositionalArgumentOperand(i).getDef().getUnconvertedResultExpression() =
|
||||
node.asDefiningArgument() and
|
||||
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`.
|
||||
*/
|
||||
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()))
|
||||
}
|
||||
|
||||
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 isBarrier(DataFlow::Node node) {
|
||||
// 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
|
||||
* @description Reading from a local variable that has not been assigned to
|
||||
* will typically yield garbage.
|
||||
* @kind problem
|
||||
* @kind path-problem
|
||||
* @id cpp/uninitialized-local
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.8
|
||||
@@ -15,6 +15,7 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.MustFlow
|
||||
import PathGraph
|
||||
|
||||
/**
|
||||
* Auxiliary predicate: Types that don't require initialization
|
||||
@@ -89,4 +90,4 @@ where
|
||||
conf.hasFlowPath(source, sink) and
|
||||
isSinkImpl(sink.getInstruction(), va) and
|
||||
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()
|
||||
|
||||
50
cpp/ql/src/Security/CWE/CWE-843/TypeConfusion.qhelp
Normal file
50
cpp/ql/src/Security/CWE/CWE-843/TypeConfusion.qhelp
Normal file
@@ -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>
|
||||
263
cpp/ql/src/Security/CWE/CWE-843/TypeConfusion.ql
Normal file
263
cpp/ql/src/Security/CWE/CWE-843/TypeConfusion.ql
Normal file
@@ -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()
|
||||
7
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionBad.cpp
Normal file
7
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionBad.cpp
Normal file
@@ -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();
|
||||
}
|
||||
25
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionCommon.cpp
Normal file
25
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionCommon.cpp
Normal file
@@ -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();
|
||||
};
|
||||
11
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionGood.cpp
Normal file
11
cpp/ql/src/Security/CWE/CWE-843/TypeConfusionGood.cpp
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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,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.
|
||||
}
|
||||
}
|
||||
@@ -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 |
|
||||
| 31 | - ... != x+0 when ... == ... is false |
|
||||
| 31 | - ... == x+0 when ... == ... is true |
|
||||
| 31 | x != -1 when ... == ... is false |
|
||||
| 31 | x != - ...+0 when ... == ... is false |
|
||||
| 31 | x == -1 when ... == ... is true |
|
||||
| 31 | x == - ...+0 when ... == ... is true |
|
||||
| 34 | 10 < j+1 when ... < ... is false |
|
||||
| 34 | 10 >= j+1 when ... < ... is true |
|
||||
@@ -86,15 +88,20 @@ astGuardsCompare
|
||||
| 58 | 0 < y+1 when ... \|\| ... is false |
|
||||
| 58 | 0 == x+0 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 when ... == ... is true |
|
||||
| 58 | x == 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 |
|
||||
| 75 | 0 != x+0 when ... == ... is false |
|
||||
| 75 | 0 == x+0 when ... == ... is true |
|
||||
| 75 | x != 0 when ... == ... is false |
|
||||
| 75 | x != 0+0 when ... == ... is false |
|
||||
| 75 | x == 0 when ... == ... is true |
|
||||
| 75 | x == 0+0 when ... == ... is true |
|
||||
| 85 | 0 != x+0 when ... == ... is false |
|
||||
| 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 == y+0 when ... != ... is false |
|
||||
| 85 | x != 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 | 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 when ... != ... is false |
|
||||
| 85 | y == 0+0 when ... != ... is false |
|
||||
| 94 | 0 != x+0 when ... != ... is true |
|
||||
| 94 | 0 == x+0 when ... != ... is false |
|
||||
| 94 | x != 0 when ... != ... is true |
|
||||
| 94 | x != 0+0 when ... != ... is true |
|
||||
| 94 | x == 0 when ... != ... is false |
|
||||
| 94 | x == 0+0 when ... != ... is false |
|
||||
| 102 | 10 < j+1 when ... < ... is false |
|
||||
| 102 | 10 >= j+1 when ... < ... is true |
|
||||
@@ -122,8 +137,11 @@ astGuardsCompare
|
||||
| 109 | 0 < y+1 when ... \|\| ... is false |
|
||||
| 109 | 0 == x+0 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 when ... == ... is true |
|
||||
| 109 | x == 0+0 when ... == ... is true |
|
||||
| 109 | y < 0+0 when ... < ... is true |
|
||||
| 109 | y >= 0+0 when ... < ... is false |
|
||||
@@ -162,7 +180,9 @@ astGuardsCompare
|
||||
| 165 | y >= x+43 when ... < ... is true |
|
||||
| 175 | 0 != call to foo+0 when ... == ... is false |
|
||||
| 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 when ... == ... is true |
|
||||
| 175 | call to foo == 0+0 when ... == ... is true |
|
||||
astGuardsControl
|
||||
| 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 | 30 | 30 |
|
||||
| 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
|
||||
| test.c:7:9:7:13 | CompareGT: ... > ... |
|
||||
| test.c:17:8:17:12 | CompareLT: ... < ... |
|
||||
@@ -497,7 +545,9 @@ irGuardsCompare
|
||||
| 26 | x >= 0+1 when CompareGT: ... > ... is true |
|
||||
| 31 | - ... != x+0 when CompareEQ: ... == ... is false |
|
||||
| 31 | - ... == x+0 when CompareEQ: ... == ... is true |
|
||||
| 31 | x != -1 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 |
|
||||
| 34 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||
| 34 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||
@@ -519,25 +569,35 @@ irGuardsCompare
|
||||
| 58 | 0 < y+1 when CompareLT: ... < ... is false |
|
||||
| 58 | 0 == x+0 when CompareEQ: ... == ... 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 when CompareEQ: ... == ... is true |
|
||||
| 58 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||
| 58 | y < 0+0 when CompareLT: ... < ... is true |
|
||||
| 58 | y >= 0+0 when CompareLT: ... < ... is false |
|
||||
| 75 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||
| 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 when CompareEQ: ... == ... is true |
|
||||
| 75 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||
| 85 | 0 != x+0 when CompareEQ: ... == ... is false |
|
||||
| 85 | 0 != y+0 when CompareNE: ... != ... is true |
|
||||
| 85 | 0 == x+0 when CompareEQ: ... == ... is true |
|
||||
| 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 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 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 false |
|
||||
| 94 | x != 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 |
|
||||
| 102 | 10 < j+1 when CompareLT: ... < ... is false |
|
||||
| 102 | 10 >= j+1 when CompareLT: ... < ... is true |
|
||||
@@ -547,7 +607,9 @@ irGuardsCompare
|
||||
| 109 | 0 < y+1 when CompareLT: ... < ... is false |
|
||||
| 109 | 0 == x+0 when CompareEQ: ... == ... 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 when CompareEQ: ... == ... is true |
|
||||
| 109 | x == 0+0 when CompareEQ: ... == ... is true |
|
||||
| 109 | y < 0+0 when CompareLT: ... < ... is true |
|
||||
| 109 | y >= 0+0 when CompareLT: ... < ... is false |
|
||||
@@ -585,7 +647,9 @@ irGuardsCompare
|
||||
| 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 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 when CompareEQ: ... == ... is true |
|
||||
| 175 | call to foo == 0+0 when CompareEQ: ... == ... is true |
|
||||
irGuardsControl
|
||||
| test.c:7:9:7:13 | CompareGT: ... > ... | false | 11 | 11 |
|
||||
@@ -841,3 +905,27 @@ 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 | 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 |
|
||||
irGuardsEnsure_const
|
||||
| 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: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: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: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,32 @@ import semmle.code.cpp.controlflow.IRGuards
|
||||
query predicate astGuards(GuardCondition guard) { any() }
|
||||
|
||||
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 which, string op |
|
||||
exists(boolean sense |
|
||||
sense = true and which = "true"
|
||||
or
|
||||
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
|
||||
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 = " != "
|
||||
(
|
||||
guard.comparesEq(left, k, true, sense) and op = " == "
|
||||
or
|
||||
guard.comparesEq(left, k, false, sense) and op = " != "
|
||||
) and
|
||||
msg = left + op + k + " when " + guard + " is " + which
|
||||
) and
|
||||
startLine = guard.getLocation().getStartLine() and
|
||||
msg = left + op + right + "+" + k + " when " + guard + " is " + which
|
||||
startLine = guard.getLocation().getStartLine()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -46,28 +56,52 @@ 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 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 which, string op |
|
||||
exists(boolean sense |
|
||||
sense = true and which = "true"
|
||||
or
|
||||
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
|
||||
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 = " != "
|
||||
(
|
||||
guard.comparesEq(left, k, true, sense) and op = " == "
|
||||
or
|
||||
guard.comparesEq(left, k, false, sense) and op = " != "
|
||||
) and
|
||||
msg =
|
||||
left.getAnyDef().getUnconvertedResultExpression() + op + k + " when " + guard + " is " +
|
||||
which
|
||||
) and
|
||||
startLine = guard.getLocation().getStartLine() and
|
||||
msg =
|
||||
left.getAnyDef().getUnconvertedResultExpression() + op +
|
||||
right.getAnyDef().getUnconvertedResultExpression() + "+" + k + " when " + guard + " is " +
|
||||
which
|
||||
startLine = guard.getLocation().getStartLine()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -95,3 +129,16 @@ query predicate irGuardsEnsure(
|
||||
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.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:31:7:31:13 | ... == ... |
|
||||
| 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 |
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
| 26 | x >= 0+1 when ... > ... is true |
|
||||
| 31 | - ... != x+0 when ... == ... is false |
|
||||
| 31 | - ... == x+0 when ... == ... is true |
|
||||
| 31 | x != -1 when ... == ... is false |
|
||||
| 31 | x != - ...+0 when ... == ... is false |
|
||||
| 31 | x == -1 when ... == ... is true |
|
||||
| 31 | x == - ...+0 when ... == ... is true |
|
||||
| 34 | 10 < j+1 when ... < ... is false |
|
||||
| 34 | 10 >= j+1 when ... < ... is true |
|
||||
@@ -44,15 +46,23 @@
|
||||
| 58 | 0 < y+1 when ... \|\| ... is false |
|
||||
| 58 | 0 == x+0 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 when ... == ... is true |
|
||||
| 58 | x == 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 |
|
||||
| 61 | i == 0 when i is true |
|
||||
| 61 | i == 1 when i is true |
|
||||
| 61 | i == 2 when i is true |
|
||||
| 75 | 0 != x+0 when ... == ... is false |
|
||||
| 75 | 0 == x+0 when ... == ... is true |
|
||||
| 75 | x != 0 when ... == ... is false |
|
||||
| 75 | x != 0+0 when ... == ... is false |
|
||||
| 75 | x == 0 when ... == ... is true |
|
||||
| 75 | x == 0+0 when ... == ... is true |
|
||||
| 85 | 0 != x+0 when ... == ... is false |
|
||||
| 85 | 0 != y+0 when ... != ... is true |
|
||||
@@ -60,15 +70,23 @@
|
||||
| 85 | 0 == x+0 when ... && ... is true |
|
||||
| 85 | 0 == x+0 when ... == ... is true |
|
||||
| 85 | 0 == y+0 when ... != ... is false |
|
||||
| 85 | x != 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 | 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 when ... != ... is false |
|
||||
| 85 | y == 0+0 when ... != ... is false |
|
||||
| 94 | 0 != x+0 when ... != ... is true |
|
||||
| 94 | 0 == x+0 when ... != ... is false |
|
||||
| 94 | x != 0 when ... != ... is true |
|
||||
| 94 | x != 0+0 when ... != ... is true |
|
||||
| 94 | x == 0 when ... != ... is false |
|
||||
| 94 | x == 0+0 when ... != ... is false |
|
||||
| 102 | 10 < j+1 when ... < ... is false |
|
||||
| 102 | 10 >= j+1 when ... < ... is true |
|
||||
@@ -80,8 +98,11 @@
|
||||
| 109 | 0 < y+1 when ... \|\| ... is false |
|
||||
| 109 | 0 == x+0 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 when ... == ... is true |
|
||||
| 109 | x == 0+0 when ... == ... is true |
|
||||
| 109 | y < 0+0 when ... < ... is true |
|
||||
| 109 | y >= 0+0 when ... < ... is false |
|
||||
|
||||
@@ -7,20 +7,30 @@
|
||||
import cpp
|
||||
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 which, string op, string msg
|
||||
where
|
||||
exists(boolean sense |
|
||||
sense = true and which = "true"
|
||||
or
|
||||
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
|
||||
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 = " != "
|
||||
) and
|
||||
msg = left + op + right + "+" + k + " when " + guard + " is " + which
|
||||
(
|
||||
guard.comparesEq(left, k, true, sense) and op = " == "
|
||||
or
|
||||
guard.comparesEq(left, k, false, sense) and op = " != "
|
||||
) and
|
||||
msg = left + op + k + " when " + guard + " is " + which
|
||||
)
|
||||
select guard.getLocation().getStartLine(), msg
|
||||
|
||||
@@ -86,3 +86,7 @@
|
||||
| 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 | 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 semmle.code.cpp.controlflow.Guards
|
||||
|
||||
from GuardCondition guard, boolean sense, int start, int end
|
||||
from GuardCondition guard, AbstractValue value, int start, int end
|
||||
where
|
||||
exists(BasicBlock block |
|
||||
guard.controls(block, sense) and
|
||||
guard.valueControls(block, value) and
|
||||
block.hasLocationInfo(_, start, _, end, _)
|
||||
)
|
||||
select guard, sense, start, end
|
||||
select guard, value, start, end
|
||||
|
||||
@@ -52,3 +52,37 @@ bool testWithCatch0(int v)
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -166,6 +166,8 @@ postWithInFlow
|
||||
| test.cpp:932:5:932:19 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:932:6:932:19 | global_pointer [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:1045:9:1045:11 | ref arg buf | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:1051:5:1051:11 | content [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| test.cpp:1052:9:1052:9 | a [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
viableImplInCallContextTooLarge
|
||||
uniqueParameterNodeAtPosition
|
||||
uniqueParameterNodePosition
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
| example.c:15:37:15:37 | **b | example.c:15:37:15:37 | **b |
|
||||
| example.c:15:37:15:37 | **b | example.c:15:37:15:37 | **b |
|
||||
| example.c:15:37:15:37 | **b | example.c:15:37:15:37 | *b |
|
||||
| example.c:15:37:15:37 | **b | example.c:19:6:19:6 | *b |
|
||||
| example.c:15:37:15:37 | *b | example.c:15:37:15:37 | **b |
|
||||
| example.c:15:37:15:37 | *b | example.c:15:37:15:37 | *b |
|
||||
| example.c:15:37:15:37 | *b | example.c:15:37:15:37 | *b |
|
||||
| example.c:15:37:15:37 | *b | example.c:15:37:15:37 | b |
|
||||
| example.c:15:37:15:37 | *b | example.c:19:6:19:6 | b |
|
||||
| example.c:15:37:15:37 | b | example.c:15:37:15:37 | *b |
|
||||
| example.c:15:37:15:37 | b | example.c:15:37:15:37 | b |
|
||||
| example.c:15:37:15:37 | b | example.c:15:37:15:37 | b |
|
||||
| example.c:15:37:15:37 | b | example.c:19:6:19:6 | b |
|
||||
| example.c:15:44:15:46 | pos | example.c:24:24:24:26 | pos |
|
||||
| example.c:17:11:17:16 | *definition of coords | example.c:17:11:17:16 | *definition of coords |
|
||||
| example.c:17:11:17:16 | *definition of coords | example.c:17:11:17:16 | *definition of coords |
|
||||
| example.c:17:11:17:16 | *definition of coords | example.c:17:11:17:16 | *definition of coords |
|
||||
| example.c:17:11:17:16 | *definition of coords | example.c:17:11:17:16 | *definition of coords |
|
||||
| example.c:17:11:17:16 | *definition of coords | example.c:24:13:24:18 | *coords |
|
||||
| example.c:17:11:17:16 | *definition of coords [post update] | example.c:17:11:17:16 | *definition of coords |
|
||||
| example.c:17:11:17:16 | *definition of coords [post update] | example.c:24:13:24:18 | *coords |
|
||||
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | *definition of coords |
|
||||
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | definition of coords |
|
||||
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | definition of coords |
|
||||
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | definition of coords |
|
||||
| example.c:17:11:17:16 | definition of coords | example.c:17:11:17:16 | definition of coords |
|
||||
| example.c:17:11:17:16 | definition of coords | example.c:24:13:24:18 | coords |
|
||||
| example.c:17:11:17:16 | definition of coords [post update] | example.c:17:11:17:16 | definition of coords |
|
||||
| example.c:17:11:17:16 | definition of coords [post update] | example.c:24:13:24:18 | coords |
|
||||
| example.c:17:19:17:22 | {...} | example.c:17:19:17:22 | {...} |
|
||||
| example.c:17:21:17:21 | 0 | example.c:17:21:17:21 | 0 |
|
||||
| example.c:19:6:19:6 | *b | example.c:15:37:15:37 | *b |
|
||||
| example.c:19:6:19:6 | *b [post update] | example.c:15:37:15:37 | *b |
|
||||
| example.c:19:6:19:6 | *b [post update] | example.c:19:6:19:6 | *b |
|
||||
| example.c:19:6:19:6 | b [post update] | example.c:19:6:19:6 | b |
|
||||
| example.c:24:2:24:7 | *coords | example.c:26:18:26:24 | *& ... |
|
||||
| example.c:24:2:24:7 | *coords [post update] | example.c:26:18:26:24 | *& ... |
|
||||
| example.c:24:2:24:7 | coords | example.c:26:18:26:24 | & ... |
|
||||
| example.c:24:2:24:7 | coords [post update] | example.c:26:18:26:24 | & ... |
|
||||
| example.c:24:13:24:18 | *coords | example.c:24:2:24:7 | *coords |
|
||||
| example.c:24:13:24:18 | *coords [post update] | example.c:24:2:24:7 | *coords |
|
||||
| example.c:24:13:24:18 | coords | example.c:24:2:24:7 | coords |
|
||||
| example.c:24:13:24:18 | coords [post update] | example.c:24:2:24:7 | coords |
|
||||
| example.c:24:13:24:30 | ... = ... | example.c:24:2:24:30 | ... = ... |
|
||||
| example.c:24:20:24:20 | *y | example.c:24:20:24:20 | *y |
|
||||
| example.c:24:20:24:20 | y | example.c:24:20:24:20 | y |
|
||||
| example.c:24:20:24:20 | y | example.c:24:20:24:20 | y |
|
||||
| example.c:24:24:24:26 | pos | example.c:28:14:28:25 | & ... |
|
||||
| example.c:24:24:24:26 | pos | example.c:28:14:28:25 | *& ... |
|
||||
| example.c:24:24:24:30 | ... + ... | example.c:24:13:24:30 | ... = ... |
|
||||
| example.c:26:13:26:16 | call to getX | example.c:26:2:26:25 | ... = ... |
|
||||
| example.c:26:18:26:24 | & ... | example.c:26:2:26:7 | coords |
|
||||
| example.c:26:18:26:24 | *& ... | example.c:26:2:26:7 | *coords |
|
||||
| example.c:26:18:26:24 | getX output argument | example.c:26:2:26:7 | *coords |
|
||||
| example.c:26:18:26:24 | pointer to getX output argument | example.c:26:2:26:7 | coords |
|
||||
| example.c:26:19:26:24 | *coords | example.c:26:18:26:24 | *& ... |
|
||||
| example.c:26:19:26:24 | coords | example.c:26:18:26:24 | & ... |
|
||||
| example.c:28:22:28:25 | & ... | example.c:28:14:28:25 | & ... |
|
||||
| example.c:28:22:28:25 | *& ... | example.c:28:14:28:25 | *& ... |
|
||||
| example.c:28:23:28:25 | *pos | example.c:28:22:28:25 | *& ... |
|
||||
| example.c:28:23:28:25 | pos | example.c:28:22:28:25 | & ... |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:6:12:6:17 | call to source |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:7:8:7:9 | t1 |
|
||||
| test.cpp:7:8:7:9 | t1 | test.cpp:8:8:8:9 | t1 |
|
||||
| test.cpp:7:8:7:9 | t1 | test.cpp:8:8:8:9 | t1 |
|
||||
| test.cpp:8:3:8:9 | ... = ... | test.cpp:10:8:10:9 | t2 |
|
||||
| test.cpp:8:8:8:9 | t1 | test.cpp:8:3:8:9 | ... = ... |
|
||||
| test.cpp:8:8:8:9 | t1 | test.cpp:9:8:9:9 | t1 |
|
||||
| test.cpp:8:8:8:9 | t1 | test.cpp:9:8:9:9 | t1 |
|
||||
| test.cpp:9:8:9:9 | t1 | test.cpp:11:7:11:8 | t1 |
|
||||
| test.cpp:9:8:9:9 | t1 | test.cpp:11:7:11:8 | t1 |
|
||||
| test.cpp:10:8:10:9 | t2 | test.cpp:13:10:13:11 | t2 |
|
||||
| test.cpp:10:8:10:9 | t2 | test.cpp:15:3:15:6 | Phi |
|
||||
| test.cpp:10:8:10:9 | t2 | test.cpp:15:3:15:6 | Phi |
|
||||
| test.cpp:11:7:11:8 | t1 | test.cpp:21:8:21:9 | t1 |
|
||||
| test.cpp:12:5:12:10 | ... = ... | test.cpp:13:10:13:11 | t2 |
|
||||
| test.cpp:12:10:12:10 | 0 | test.cpp:12:5:12:10 | ... = ... |
|
||||
| test.cpp:13:10:13:11 | t2 | test.cpp:15:3:15:6 | Phi |
|
||||
| test.cpp:13:10:13:11 | t2 | test.cpp:15:3:15:6 | Phi |
|
||||
| test.cpp:15:3:15:6 | Phi | test.cpp:15:8:15:9 | t2 |
|
||||
| test.cpp:15:3:15:6 | Phi | test.cpp:15:8:15:9 | t2 |
|
||||
| test.cpp:15:8:15:9 | t2 | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:15:8:15:9 | t2 | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:17:3:17:8 | ... = ... | test.cpp:21:8:21:9 | t1 |
|
||||
| test.cpp:17:8:17:8 | 0 | test.cpp:17:3:17:8 | ... = ... |
|
||||
| test.cpp:21:8:21:9 | t1 | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:21:8:21:9 | t1 | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:23:15:23:16 | 0 | test.cpp:23:15:23:16 | 0 |
|
||||
| test.cpp:23:15:23:16 | 0 | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:23:19:23:19 | Phi | test.cpp:23:19:23:19 | i |
|
||||
| test.cpp:23:19:23:19 | Phi | test.cpp:23:19:23:19 | i |
|
||||
| test.cpp:23:19:23:19 | Phi | test.cpp:23:23:23:24 | t1 |
|
||||
| test.cpp:23:19:23:19 | Phi | test.cpp:23:23:23:24 | t1 |
|
||||
| test.cpp:23:19:23:19 | Phi | test.cpp:24:10:24:11 | t2 |
|
||||
| test.cpp:23:19:23:19 | Phi | test.cpp:24:10:24:11 | t2 |
|
||||
| test.cpp:23:19:23:19 | i | test.cpp:23:27:23:27 | i |
|
||||
| test.cpp:23:19:23:19 | i | test.cpp:23:27:23:27 | i |
|
||||
| test.cpp:23:23:23:24 | t1 | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:23:23:23:24 | t1 | test.cpp:26:8:26:9 | t1 |
|
||||
| test.cpp:23:23:23:24 | t1 | test.cpp:26:8:26:9 | t1 |
|
||||
| test.cpp:23:27:23:27 | *i | test.cpp:23:27:23:27 | *i |
|
||||
| test.cpp:23:27:23:27 | *i | test.cpp:23:27:23:27 | i |
|
||||
| test.cpp:23:27:23:27 | i | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:23:27:23:27 | i | test.cpp:23:27:23:27 | i |
|
||||
| test.cpp:23:27:23:27 | i | test.cpp:23:27:23:27 | i |
|
||||
| test.cpp:23:27:23:29 | ... ++ | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:23:27:23:29 | ... ++ | test.cpp:23:27:23:29 | ... ++ |
|
||||
| test.cpp:24:5:24:11 | ... = ... | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:24:10:24:11 | t2 | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:24:10:24:11 | t2 | test.cpp:23:19:23:19 | Phi |
|
||||
| test.cpp:24:10:24:11 | t2 | test.cpp:24:5:24:11 | ... = ... |
|
||||
| test.cpp:382:48:382:54 | source1 | test.cpp:384:16:384:23 | *& ... |
|
||||
| test.cpp:383:12:383:13 | 0 | test.cpp:383:12:383:13 | 0 |
|
||||
| test.cpp:383:12:383:13 | 0 | test.cpp:384:10:384:13 | *& ... |
|
||||
| test.cpp:384:10:384:13 | & ... | test.cpp:384:3:384:8 | call to memcpy |
|
||||
| test.cpp:384:10:384:13 | & ... | test.cpp:384:10:384:13 | & ... |
|
||||
| test.cpp:384:10:384:13 | & ... | test.cpp:385:8:385:10 | tmp |
|
||||
| test.cpp:384:10:384:13 | *& ... | test.cpp:384:10:384:13 | *& ... |
|
||||
| test.cpp:384:10:384:13 | memcpy output argument | test.cpp:385:8:385:10 | tmp |
|
||||
| test.cpp:384:10:384:13 | pointer to memcpy output argument | test.cpp:385:8:385:10 | tmp |
|
||||
| test.cpp:384:11:384:13 | *tmp | test.cpp:384:10:384:13 | *& ... |
|
||||
| test.cpp:384:11:384:13 | tmp | test.cpp:384:10:384:13 | & ... |
|
||||
| test.cpp:384:16:384:23 | & ... | test.cpp:384:16:384:23 | & ... |
|
||||
| test.cpp:384:16:384:23 | *& ... | test.cpp:384:3:384:8 | **call to memcpy |
|
||||
| test.cpp:384:16:384:23 | *& ... | test.cpp:384:3:384:8 | *call to memcpy |
|
||||
| test.cpp:384:16:384:23 | *& ... | test.cpp:384:10:384:13 | memcpy output argument |
|
||||
| test.cpp:384:16:384:23 | *& ... | test.cpp:384:16:384:23 | *& ... |
|
||||
| test.cpp:384:16:384:23 | **(const void *)... | test.cpp:384:3:384:8 | **call to memcpy |
|
||||
| test.cpp:384:16:384:23 | **(const void *)... | test.cpp:384:10:384:13 | memcpy output argument |
|
||||
| test.cpp:384:17:384:23 | *source1 | test.cpp:384:16:384:23 | *& ... |
|
||||
| test.cpp:384:17:384:23 | source1 | test.cpp:384:16:384:23 | & ... |
|
||||
| test.cpp:388:53:388:59 | source1 | test.cpp:391:16:391:23 | *& ... |
|
||||
| test.cpp:388:66:388:66 | b | test.cpp:393:7:393:7 | b |
|
||||
| test.cpp:389:12:389:13 | 0 | test.cpp:389:12:389:13 | 0 |
|
||||
| test.cpp:389:12:389:13 | 0 | test.cpp:390:18:390:21 | *& ... |
|
||||
| test.cpp:390:18:390:21 | & ... | test.cpp:390:18:390:21 | & ... |
|
||||
| test.cpp:390:18:390:21 | & ... | test.cpp:391:10:391:13 | & ... |
|
||||
| test.cpp:390:18:390:21 | *& ... | test.cpp:390:18:390:21 | *& ... |
|
||||
| test.cpp:390:18:390:21 | *& ... | test.cpp:391:10:391:13 | *& ... |
|
||||
| test.cpp:390:19:390:21 | *tmp | test.cpp:390:18:390:21 | *& ... |
|
||||
| test.cpp:390:19:390:21 | tmp | test.cpp:390:18:390:21 | & ... |
|
||||
| test.cpp:391:10:391:13 | & ... | test.cpp:391:3:391:8 | call to memcpy |
|
||||
| test.cpp:391:10:391:13 | & ... | test.cpp:391:10:391:13 | & ... |
|
||||
| test.cpp:391:10:391:13 | & ... | test.cpp:392:8:392:10 | tmp |
|
||||
| test.cpp:391:10:391:13 | *& ... | test.cpp:391:10:391:13 | *& ... |
|
||||
| test.cpp:391:10:391:13 | memcpy output argument | test.cpp:392:8:392:10 | tmp |
|
||||
| test.cpp:391:10:391:13 | pointer to memcpy output argument | test.cpp:392:8:392:10 | tmp |
|
||||
| test.cpp:391:11:391:13 | *tmp | test.cpp:391:10:391:13 | *& ... |
|
||||
| test.cpp:391:11:391:13 | tmp | test.cpp:391:10:391:13 | & ... |
|
||||
| test.cpp:391:16:391:23 | & ... | test.cpp:391:16:391:23 | & ... |
|
||||
| test.cpp:391:16:391:23 | *& ... | test.cpp:391:3:391:8 | **call to memcpy |
|
||||
| test.cpp:391:16:391:23 | *& ... | test.cpp:391:3:391:8 | *call to memcpy |
|
||||
| test.cpp:391:16:391:23 | *& ... | test.cpp:391:10:391:13 | memcpy output argument |
|
||||
| test.cpp:391:16:391:23 | *& ... | test.cpp:391:16:391:23 | *& ... |
|
||||
| test.cpp:391:16:391:23 | **(const void *)... | test.cpp:391:3:391:8 | **call to memcpy |
|
||||
| test.cpp:391:16:391:23 | **(const void *)... | test.cpp:391:10:391:13 | memcpy output argument |
|
||||
| test.cpp:391:17:391:23 | *source1 | test.cpp:391:16:391:23 | *& ... |
|
||||
| test.cpp:391:17:391:23 | source1 | test.cpp:391:16:391:23 | & ... |
|
||||
| test.cpp:392:8:392:10 | tmp | test.cpp:394:10:394:12 | tmp |
|
||||
| test.cpp:392:8:392:10 | tmp | test.cpp:394:10:394:12 | tmp |
|
||||
| test.cpp:487:67:487:67 | **s | test.cpp:487:67:487:67 | **s |
|
||||
| test.cpp:487:67:487:67 | **s | test.cpp:487:67:487:67 | **s |
|
||||
| test.cpp:487:67:487:67 | **s | test.cpp:487:67:487:67 | *s |
|
||||
| test.cpp:487:67:487:67 | **s | test.cpp:488:21:488:21 | *s |
|
||||
| test.cpp:487:67:487:67 | *s | test.cpp:487:67:487:67 | **s |
|
||||
| test.cpp:487:67:487:67 | *s | test.cpp:487:67:487:67 | *s |
|
||||
| test.cpp:487:67:487:67 | *s | test.cpp:487:67:487:67 | *s |
|
||||
| test.cpp:487:67:487:67 | *s | test.cpp:487:67:487:67 | s |
|
||||
| test.cpp:487:67:487:67 | *s | test.cpp:488:21:488:21 | s |
|
||||
| test.cpp:487:67:487:67 | s | test.cpp:487:67:487:67 | *s |
|
||||
| test.cpp:487:67:487:67 | s | test.cpp:487:67:487:67 | s |
|
||||
| test.cpp:487:67:487:67 | s | test.cpp:487:67:487:67 | s |
|
||||
| test.cpp:487:67:487:67 | s | test.cpp:488:21:488:21 | s |
|
||||
| test.cpp:488:21:488:21 | *s | test.cpp:489:20:489:20 | *s |
|
||||
| test.cpp:488:21:488:21 | *s [post update] | test.cpp:489:20:489:20 | *s |
|
||||
| test.cpp:488:21:488:21 | s | test.cpp:489:20:489:20 | s |
|
||||
| test.cpp:488:21:488:21 | s | test.cpp:489:20:489:20 | s |
|
||||
| test.cpp:488:21:488:21 | s [post update] | test.cpp:489:20:489:20 | s |
|
||||
| test.cpp:488:24:488:30 | *content | test.cpp:488:21:488:30 | *content |
|
||||
| test.cpp:488:24:488:30 | content | test.cpp:488:21:488:30 | content |
|
||||
| test.cpp:489:20:489:20 | *s | test.cpp:487:67:487:67 | *s |
|
||||
| test.cpp:489:20:489:20 | *s [post update] | test.cpp:487:67:487:67 | *s |
|
||||
| test.cpp:489:20:489:20 | *s [post update] | test.cpp:489:20:489:20 | *s |
|
||||
| test.cpp:489:20:489:20 | s [post update] | test.cpp:489:20:489:20 | s |
|
||||
| test.cpp:489:23:489:29 | *content | test.cpp:489:23:489:29 | *content |
|
||||
| test.cpp:489:23:489:29 | *content | test.cpp:490:8:490:17 | * ... |
|
||||
| test.cpp:489:23:489:29 | content | test.cpp:489:23:489:29 | content |
|
||||
| test.cpp:489:23:489:29 | content | test.cpp:490:9:490:17 | p_content |
|
||||
| test.cpp:1050:12:1050:12 | definition of a | test.cpp:1051:3:1051:3 | *a |
|
||||
| test.cpp:1051:3:1051:3 | *a | test.cpp:1052:8:1052:9 | *& ... |
|
||||
| test.cpp:1051:3:1051:3 | *a [post update] | test.cpp:1052:8:1052:9 | *& ... |
|
||||
| test.cpp:1051:3:1051:3 | a | test.cpp:1052:8:1052:9 | & ... |
|
||||
| test.cpp:1051:3:1051:3 | a [post update] | test.cpp:1052:8:1052:9 | & ... |
|
||||
| test.cpp:1051:15:1051:21 | 0 | test.cpp:1051:3:1051:21 | ... = ... |
|
||||
| test.cpp:1051:15:1051:21 | *0 | test.cpp:1051:3:1051:21 | *... = ... |
|
||||
| test.cpp:1052:9:1052:9 | *a | test.cpp:1052:8:1052:9 | *& ... |
|
||||
| test.cpp:1052:9:1052:9 | a | test.cpp:1052:8:1052:9 | & ... |
|
||||
@@ -0,0 +1,8 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
|
||||
from DataFlow::Node nodeFrom, DataFlow::Node nodeTo
|
||||
where
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo) and
|
||||
nodeFrom.getFunction().getName().matches("%\\_with\\_local\\_flow")
|
||||
select nodeFrom, nodeTo
|
||||
@@ -81,3 +81,10 @@ WARNING: Module DataFlow has been deprecated and may be removed in future (local
|
||||
| test.cpp:488:21:488:21 | s [post update] | test.cpp:489:20:489:20 | s |
|
||||
| test.cpp:488:24:488:30 | ref arg content | test.cpp:489:23:489:29 | content |
|
||||
| test.cpp:489:23:489:29 | content | test.cpp:490:9:490:17 | p_content |
|
||||
| test.cpp:1050:12:1050:12 | a | test.cpp:1051:3:1051:3 | a |
|
||||
| test.cpp:1050:12:1050:12 | a | test.cpp:1052:9:1052:9 | a |
|
||||
| test.cpp:1051:3:1051:3 | a [post update] | test.cpp:1052:9:1052:9 | a |
|
||||
| test.cpp:1051:3:1051:21 | ... = ... | test.cpp:1051:5:1051:11 | content [post update] |
|
||||
| test.cpp:1051:15:1051:21 | 0 | test.cpp:1051:3:1051:21 | ... = ... |
|
||||
| test.cpp:1052:8:1052:9 | ref arg & ... | test.cpp:1052:9:1052:9 | a [inner post update] |
|
||||
| test.cpp:1052:9:1052:9 | a | test.cpp:1052:8:1052:9 | & ... |
|
||||
|
||||
@@ -123,6 +123,7 @@ astFlow
|
||||
| test.cpp:842:11:842:16 | call to source | test.cpp:844:8:844:8 | y |
|
||||
| test.cpp:846:13:846:27 | call to indirect_source | test.cpp:848:23:848:25 | rpx |
|
||||
| test.cpp:860:54:860:59 | call to source | test.cpp:861:10:861:37 | static_local_pointer_dynamic |
|
||||
| test.cpp:1050:12:1050:12 | a | test.cpp:1052:8:1052:9 | & ... |
|
||||
| true_upon_entry.cpp:17:11:17:16 | call to source | true_upon_entry.cpp:21:8:21:8 | x |
|
||||
| true_upon_entry.cpp:27:9:27:14 | call to source | true_upon_entry.cpp:29:8:29:8 | x |
|
||||
| true_upon_entry.cpp:33:11:33:16 | call to source | true_upon_entry.cpp:39:8:39:8 | x |
|
||||
|
||||
@@ -1044,4 +1044,10 @@ void* memset(void*, int, size_t);
|
||||
void memset_test(char* buf) { // $ ast-def=buf ir-def=*buf
|
||||
memset(buf, source(), 10);
|
||||
sink(*buf); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void flow_out_of_address_with_local_flow() {
|
||||
MyStruct a;
|
||||
a.content = nullptr;
|
||||
sink(&a); // $ SPURIOUS: ast
|
||||
}
|
||||
@@ -54,3 +54,5 @@
|
||||
| test.cpp:796:12:796:12 | a | test.cpp:797:20:797:20 | a |
|
||||
| test.cpp:796:12:796:12 | a | test.cpp:797:31:797:31 | a |
|
||||
| test.cpp:796:12:796:12 | a | test.cpp:798:17:798:17 | a |
|
||||
| test.cpp:1050:12:1050:12 | a | test.cpp:1051:3:1051:3 | a |
|
||||
| test.cpp:1050:12:1050:12 | a | test.cpp:1052:9:1052:9 | a |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
56
cpp/ql/test/library-tests/ir/ir/destructors_for_temps.cpp
Normal file
56
cpp/ql/test/library-tests/ir/ir/destructors_for_temps.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
class ClassWithDestructor2 {
|
||||
public:
|
||||
ClassWithDestructor2();
|
||||
~ClassWithDestructor2();
|
||||
|
||||
char get_x();
|
||||
};
|
||||
|
||||
class ClassWithConstructor {
|
||||
public:
|
||||
ClassWithConstructor(char x, char y);
|
||||
};
|
||||
|
||||
char temp_test() {
|
||||
char x = ClassWithDestructor2().get_x();
|
||||
ClassWithConstructor y('a', ClassWithDestructor2().get_x());
|
||||
return ClassWithDestructor2().get_x();
|
||||
}
|
||||
|
||||
|
||||
char temp_test2() {
|
||||
new ClassWithDestructor2();
|
||||
return ClassWithDestructor2().get_x() + ClassWithDestructor2().get_x();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T returnValue();
|
||||
|
||||
void temp_test3() {
|
||||
const ClassWithDestructor2& rs = returnValue<ClassWithDestructor2>();
|
||||
}
|
||||
|
||||
void temp_test4() {
|
||||
ClassWithDestructor2 c;
|
||||
const ClassWithDestructor2& rs2 = returnValue<ClassWithDestructor2>();
|
||||
}
|
||||
|
||||
void temp_test5(bool b) {
|
||||
b ? ClassWithDestructor2() : ClassWithDestructor2();
|
||||
}
|
||||
|
||||
void temp_test6(bool b) {
|
||||
ClassWithDestructor2 c;
|
||||
if (b) {
|
||||
throw ClassWithConstructor('x', ClassWithDestructor2().get_x());
|
||||
}
|
||||
}
|
||||
|
||||
void temp_test7(bool b) {
|
||||
ClassWithDestructor2 c;
|
||||
b ? throw ClassWithConstructor('x', ClassWithDestructor2().get_x()) : ClassWithDestructor2();
|
||||
}
|
||||
|
||||
void temp_test8(bool b) {
|
||||
b ? throw ClassWithConstructor('x', ClassWithDestructor2().get_x()) : ClassWithDestructor2();
|
||||
}
|
||||
@@ -1055,28 +1055,75 @@ void Lambda(int x, const String& s) {
|
||||
lambda_inits(6);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct vector {
|
||||
struct iterator {
|
||||
T* p;
|
||||
iterator& operator++();
|
||||
T& operator*() const;
|
||||
namespace std {
|
||||
template<class T>
|
||||
struct remove_const { typedef T type; };
|
||||
|
||||
bool operator!=(iterator right) const;
|
||||
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;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
vector(T);
|
||||
~vector();
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
};
|
||||
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 {};
|
||||
|
||||
template<typename T>
|
||||
bool operator==(typename vector<T>::iterator left, typename vector<T>::iterator right);
|
||||
template<typename T>
|
||||
bool operator!=(typename vector<T>::iterator left, typename vector<T>::iterator right);
|
||||
struct output_iterator_tag {};
|
||||
|
||||
void RangeBasedFor(const vector<int>& v) {
|
||||
template<typename T>
|
||||
struct vector {
|
||||
vector(T);
|
||||
~vector();
|
||||
|
||||
using iterator = std::iterator<random_access_iterator_tag, T>;
|
||||
using const_iterator = std::iterator<random_access_iterator_tag, const T>;
|
||||
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
bool operator==(typename vector<T>::iterator left, typename vector<T>::iterator right);
|
||||
template<typename T>
|
||||
bool operator!=(typename vector<T>::iterator left, typename vector<T>::iterator right);
|
||||
|
||||
}
|
||||
|
||||
void RangeBasedFor(const std::vector<int>& v) {
|
||||
for (int e : v) {
|
||||
if (e > 0) {
|
||||
continue;
|
||||
@@ -2151,21 +2198,21 @@ void initialization_with_destructor(bool b, char c) {
|
||||
}
|
||||
|
||||
ClassWithDestructor x;
|
||||
for(vector<ClassWithDestructor> ys(x); ClassWithDestructor y : ys)
|
||||
for(std::vector<ClassWithDestructor> ys(x); ClassWithDestructor y : ys)
|
||||
y.set_x('a');
|
||||
|
||||
for(vector<ClassWithDestructor> ys(x); ClassWithDestructor y : ys) {
|
||||
for(std::vector<ClassWithDestructor> ys(x); ClassWithDestructor y : ys) {
|
||||
y.set_x('a');
|
||||
if (y.get_x() == 'b')
|
||||
return;
|
||||
}
|
||||
|
||||
for(vector<int> ys(1); int y : ys) {
|
||||
for(std::vector<int> ys(1); int y : ys) {
|
||||
if (y == 1)
|
||||
return;
|
||||
}
|
||||
|
||||
for(vector<ClassWithDestructor> ys(x); ClassWithDestructor y : ys) {
|
||||
for(std::vector<ClassWithDestructor> ys(x); ClassWithDestructor y : ys) {
|
||||
ClassWithDestructor z1;
|
||||
ClassWithDestructor z2;
|
||||
}
|
||||
@@ -2243,7 +2290,7 @@ void ForDestructors() {
|
||||
String s2;
|
||||
}
|
||||
|
||||
for(String s : vector<String>(String("hello"))) {
|
||||
for(String s : std::vector<String>(String("hello"))) {
|
||||
String s2;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,7 @@ multipleIRTypes
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
| ir.cpp:1488:8:1488:8 | Unary | Operand 'Unary' is not dominated by its definition in function '$@'. | ir.cpp:1488:8:1488:8 | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() |
|
||||
| ir.cpp:1535:8:1535:8 | Unary | Operand 'Unary' is not dominated by its definition in function '$@'. | ir.cpp:1535:8:1535:8 | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() |
|
||||
| try_except.c:13:13:13:13 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:6:6:6:6 | void f() | void f() |
|
||||
| try_except.c:13:13:13:13 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:6:6:6:6 | void f() | void f() |
|
||||
| try_except.c:39:15:39:15 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:32:6:32:6 | void h(int) | void h(int) |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@ sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ms_try_mix.cpp:35:13:35:19 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:29:6:29:19 | void ms_finally_mix(int) | void ms_finally_mix(int) |
|
||||
| ms_try_mix.cpp:53:5:53:11 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:49:6:49:28 | void ms_empty_finally_at_end() | void ms_empty_finally_at_end() |
|
||||
| statements.cpp:25:5:25:9 | ReThrow: re-throw exception | Instruction 'ReThrow: re-throw exception ' has no successors in function '$@'. | statements.cpp:21:6:21:16 | void early_throw(int) | void early_throw(int) |
|
||||
| stmt_expr.cpp:27:5:27:15 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | stmt_expr.cpp:21:13:21:13 | void stmtexpr::g(int) | void stmtexpr::g(int) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
|
||||
@@ -10,9 +10,6 @@ sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ms_try_mix.cpp:35:13:35:19 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:29:6:29:19 | void ms_finally_mix(int) | void ms_finally_mix(int) |
|
||||
| ms_try_mix.cpp:53:5:53:11 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:49:6:49:28 | void ms_empty_finally_at_end() | void ms_empty_finally_at_end() |
|
||||
| statements.cpp:25:5:25:9 | ReThrow: re-throw exception | Instruction 'ReThrow: re-throw exception ' has no successors in function '$@'. | statements.cpp:21:6:21:16 | void early_throw(int) | void early_throw(int) |
|
||||
| statements.cpp:26:3:26:3 | IndirectMayWriteSideEffect: inner | Instruction 'IndirectMayWriteSideEffect: inner' has no successors in function '$@'. | statements.cpp:21:6:21:16 | void early_throw(int) | void early_throw(int) |
|
||||
| statements.cpp:28:1:28:1 | IndirectMayWriteSideEffect: before | Instruction 'IndirectMayWriteSideEffect: before' has no successors in function '$@'. | statements.cpp:21:6:21:16 | void early_throw(int) | void early_throw(int) |
|
||||
| stmt_expr.cpp:27:5:27:15 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | stmt_expr.cpp:21:13:21:13 | void stmtexpr::g(int) | void stmtexpr::g(int) |
|
||||
| stmt_expr.cpp:29:11:32:11 | CopyValue: (statement expression) | Instruction 'CopyValue: (statement expression)' has no successors in function '$@'. | stmt_expr.cpp:21:13:21:13 | void stmtexpr::g(int) | void stmtexpr::g(int) |
|
||||
| stmt_in_type.cpp:5:53:5:53 | Constant: 1 | Instruction 'Constant: 1' has no successors in function '$@'. | stmt_in_type.cpp:2:6:2:12 | void cpp_fun() | void cpp_fun() |
|
||||
|
||||
@@ -9,7 +9,6 @@ sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ms_try_mix.cpp:35:13:35:19 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:29:6:29:19 | void ms_finally_mix(int) | void ms_finally_mix(int) |
|
||||
| ms_try_mix.cpp:53:5:53:11 | ThrowValue: throw ... | Instruction 'ThrowValue: throw ...' has no successors in function '$@'. | ms_try_mix.cpp:49:6:49:28 | void ms_empty_finally_at_end() | void ms_empty_finally_at_end() |
|
||||
| statements.cpp:25:5:25:9 | ReThrow: re-throw exception | Instruction 'ReThrow: re-throw exception ' has no successors in function '$@'. | statements.cpp:21:6:21:16 | void early_throw(int) | void early_throw(int) |
|
||||
| stmt_expr.cpp:27:5:27:15 | Store: ... = ... | Instruction 'Store: ... = ...' has no successors in function '$@'. | stmt_expr.cpp:21:13:21:13 | void stmtexpr::g(int) | void stmtexpr::g(int) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
|
||||
@@ -11,6 +11,7 @@ edges
|
||||
| test_free.cpp:128:10:128:11 | pointer to free output argument | test_free.cpp:129:10:129:11 | * ... | provenance | |
|
||||
| test_free.cpp:152:27:152:27 | pointer to free output argument | test_free.cpp:154:10:154:10 | a | provenance | |
|
||||
| test_free.cpp:207:10:207:10 | pointer to free output argument | test_free.cpp:209:10:209:10 | a | provenance | |
|
||||
| test_free.cpp:301:12:301:14 | pointer to g_free output argument | test_free.cpp:302:12:302:14 | buf | provenance | |
|
||||
nodes
|
||||
| test_free.cpp:11:10:11:10 | pointer to free output argument | semmle.label | pointer to free output argument |
|
||||
| test_free.cpp:14:10:14:10 | a | semmle.label | a |
|
||||
@@ -36,6 +37,8 @@ nodes
|
||||
| test_free.cpp:154:10:154:10 | a | semmle.label | a |
|
||||
| test_free.cpp:207:10:207:10 | pointer to free output argument | semmle.label | pointer to free output argument |
|
||||
| test_free.cpp:209:10:209:10 | a | semmle.label | a |
|
||||
| test_free.cpp:301:12:301:14 | pointer to g_free output argument | semmle.label | pointer to g_free output argument |
|
||||
| test_free.cpp:302:12:302:14 | buf | semmle.label | buf |
|
||||
subpaths
|
||||
#select
|
||||
| test_free.cpp:14:10:14:10 | a | test_free.cpp:11:10:11:10 | pointer to free output argument | test_free.cpp:14:10:14:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:11:5:11:8 | call to free | call to free |
|
||||
@@ -50,3 +53,4 @@ subpaths
|
||||
| test_free.cpp:129:10:129:11 | * ... | test_free.cpp:128:10:128:11 | pointer to free output argument | test_free.cpp:129:10:129:11 | * ... | Memory pointed to by '* ...' may already have been freed by $@. | test_free.cpp:128:5:128:8 | call to free | call to free |
|
||||
| test_free.cpp:154:10:154:10 | a | test_free.cpp:152:27:152:27 | pointer to free output argument | test_free.cpp:154:10:154:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:152:22:152:25 | call to free | call to free |
|
||||
| test_free.cpp:209:10:209:10 | a | test_free.cpp:207:10:207:10 | pointer to free output argument | test_free.cpp:209:10:209:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:207:5:207:8 | call to free | call to free |
|
||||
| test_free.cpp:302:12:302:14 | buf | test_free.cpp:301:12:301:14 | pointer to g_free output argument | test_free.cpp:302:12:302:14 | buf | Memory pointed to by 'buf' may already have been freed by $@. | test_free.cpp:301:5:301:10 | call to g_free | call to g_free |
|
||||
|
||||
@@ -102,6 +102,8 @@
|
||||
| test_free.cpp:282:10:282:12 | buf |
|
||||
| test_free.cpp:288:8:288:10 | buf |
|
||||
| test_free.cpp:293:8:293:10 | buf |
|
||||
| test_free.cpp:301:12:301:14 | buf |
|
||||
| test_free.cpp:302:12:302:14 | buf |
|
||||
| virtual.cpp:18:10:18:10 | a |
|
||||
| virtual.cpp:19:10:19:10 | c |
|
||||
| virtual.cpp:38:10:38:10 | b |
|
||||
|
||||
@@ -293,4 +293,11 @@ void test_free_struct4(char* buf, MyStruct s) {
|
||||
free(buf);
|
||||
s.buf = buf;
|
||||
char c = s.buf[0]; // BAD
|
||||
}
|
||||
|
||||
void g_free (void*);
|
||||
|
||||
void test_g_free(char* buf) {
|
||||
g_free(buf);
|
||||
g_free(buf); // BAD
|
||||
}
|
||||
@@ -8,15 +8,10 @@ edges
|
||||
| nested.cpp:35:19:35:21 | *fmt | nested.cpp:27:32:27:34 | *fmt | provenance | |
|
||||
| nested.cpp:42:24:42:34 | *call to ext_fmt_str | nested.cpp:34:37:34:39 | *fmt | provenance | |
|
||||
| nested.cpp:86:19:86:46 | *call to __builtin_alloca | nested.cpp:87:18:87:20 | *fmt | provenance | |
|
||||
| test.cpp:27:39:27:39 | n | test.cpp:27:13:27:24 | **make_message | provenance | |
|
||||
| test.cpp:46:14:46:17 | argc | test.cpp:51:23:51:30 | ... - ... | provenance | |
|
||||
| test.cpp:46:27:46:30 | **argv | test.cpp:130:20:130:26 | *access to array | provenance | |
|
||||
| test.cpp:51:23:51:30 | ... - ... | test.cpp:27:39:27:39 | n | provenance | |
|
||||
| test.cpp:51:23:51:30 | ... - ... | test.cpp:51:10:51:21 | *call to make_message | provenance | |
|
||||
| test.cpp:155:27:155:30 | data | test.cpp:157:12:157:15 | data | provenance | |
|
||||
| test.cpp:167:31:167:34 | data | test.cpp:170:12:170:14 | *res | provenance | |
|
||||
| test.cpp:193:32:193:34 | str | test.cpp:195:31:195:33 | str | provenance | |
|
||||
| test.cpp:193:32:193:34 | str | test.cpp:197:11:197:14 | *wstr | provenance | |
|
||||
| test.cpp:167:31:167:34 | *data | test.cpp:170:12:170:14 | *res | provenance | |
|
||||
| test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | provenance | |
|
||||
| test.cpp:193:32:193:34 | *str | test.cpp:197:11:197:14 | *wstr | provenance | |
|
||||
| test.cpp:204:25:204:36 | *call to get_string | test.cpp:205:12:205:20 | *... + ... | provenance | |
|
||||
| test.cpp:204:25:204:36 | *call to get_string | test.cpp:206:12:206:16 | *hello | provenance | |
|
||||
| test.cpp:209:25:209:36 | *call to get_string | test.cpp:211:12:211:16 | *hello | provenance | |
|
||||
@@ -42,19 +37,12 @@ nodes
|
||||
| nested.cpp:79:32:79:38 | *call to get_fmt | semmle.label | *call to get_fmt |
|
||||
| nested.cpp:86:19:86:46 | *call to __builtin_alloca | semmle.label | *call to __builtin_alloca |
|
||||
| nested.cpp:87:18:87:20 | *fmt | semmle.label | *fmt |
|
||||
| test.cpp:27:13:27:24 | **make_message | semmle.label | **make_message |
|
||||
| test.cpp:27:39:27:39 | n | semmle.label | n |
|
||||
| test.cpp:46:14:46:17 | argc | semmle.label | argc |
|
||||
| test.cpp:46:27:46:30 | **argv | semmle.label | **argv |
|
||||
| test.cpp:51:10:51:21 | *call to make_message | semmle.label | *call to make_message |
|
||||
| test.cpp:51:23:51:30 | ... - ... | semmle.label | ... - ... |
|
||||
| test.cpp:130:20:130:26 | *access to array | semmle.label | *access to array |
|
||||
| test.cpp:155:27:155:30 | data | semmle.label | data |
|
||||
| test.cpp:157:12:157:15 | data | semmle.label | data |
|
||||
| test.cpp:167:31:167:34 | data | semmle.label | data |
|
||||
| test.cpp:167:31:167:34 | *data | semmle.label | *data |
|
||||
| test.cpp:170:12:170:14 | *res | semmle.label | *res |
|
||||
| test.cpp:193:32:193:34 | str | semmle.label | str |
|
||||
| test.cpp:195:31:195:33 | str | semmle.label | str |
|
||||
| test.cpp:193:32:193:34 | *str | semmle.label | *str |
|
||||
| test.cpp:195:31:195:33 | *str | semmle.label | *str |
|
||||
| test.cpp:197:11:197:14 | *wstr | semmle.label | *wstr |
|
||||
| test.cpp:204:25:204:36 | *call to get_string | semmle.label | *call to get_string |
|
||||
| test.cpp:205:12:205:20 | *... + ... | semmle.label | *... + ... |
|
||||
@@ -74,7 +62,6 @@ nodes
|
||||
| test.cpp:245:25:245:36 | *call to get_string | semmle.label | *call to get_string |
|
||||
| test.cpp:247:12:247:16 | *hello | semmle.label | *hello |
|
||||
subpaths
|
||||
| test.cpp:51:23:51:30 | ... - ... | test.cpp:27:39:27:39 | n | test.cpp:27:13:27:24 | **make_message | test.cpp:51:10:51:21 | *call to make_message |
|
||||
#select
|
||||
| NonConstantFormat.c:30:10:30:16 | *access to array | NonConstantFormat.c:28:27:28:30 | **argv | NonConstantFormat.c:30:10:30:16 | *access to array | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:30:3:30:8 | call to printf | printf |
|
||||
| NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:41:2:41:7 | call to printf | printf |
|
||||
@@ -82,12 +69,10 @@ subpaths
|
||||
| nested.cpp:21:23:21:26 | *fmt0 | nested.cpp:42:24:42:34 | *call to ext_fmt_str | nested.cpp:21:23:21:26 | *fmt0 | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | nested.cpp:21:5:21:12 | call to snprintf | snprintf |
|
||||
| nested.cpp:79:32:79:38 | *call to get_fmt | nested.cpp:79:32:79:38 | *call to get_fmt | nested.cpp:79:32:79:38 | *call to get_fmt | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | nested.cpp:79:5:79:14 | call to diagnostic | diagnostic |
|
||||
| nested.cpp:87:18:87:20 | *fmt | nested.cpp:86:19:86:46 | *call to __builtin_alloca | nested.cpp:87:18:87:20 | *fmt | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | nested.cpp:87:7:87:16 | call to diagnostic | diagnostic |
|
||||
| test.cpp:51:10:51:21 | *call to make_message | test.cpp:46:14:46:17 | argc | test.cpp:51:10:51:21 | *call to make_message | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:51:3:51:8 | call to printf | printf |
|
||||
| test.cpp:130:20:130:26 | *access to array | test.cpp:46:27:46:30 | **argv | test.cpp:130:20:130:26 | *access to array | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:130:2:130:10 | call to sprintf | sprintf |
|
||||
| test.cpp:157:12:157:15 | data | test.cpp:155:27:155:30 | data | test.cpp:157:12:157:15 | data | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:157:5:157:10 | call to printf | printf |
|
||||
| test.cpp:170:12:170:14 | *res | test.cpp:167:31:167:34 | data | test.cpp:170:12:170:14 | *res | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:170:5:170:10 | call to printf | printf |
|
||||
| test.cpp:195:31:195:33 | str | test.cpp:193:32:193:34 | str | test.cpp:195:31:195:33 | str | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:195:3:195:18 | call to StringCchPrintfW | StringCchPrintfW |
|
||||
| test.cpp:197:11:197:14 | *wstr | test.cpp:193:32:193:34 | str | test.cpp:197:11:197:14 | *wstr | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:197:3:197:9 | call to wprintf | wprintf |
|
||||
| test.cpp:170:12:170:14 | *res | test.cpp:167:31:167:34 | *data | test.cpp:170:12:170:14 | *res | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:170:5:170:10 | call to printf | printf |
|
||||
| test.cpp:195:31:195:33 | *str | test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:195:3:195:18 | call to StringCchPrintfW | StringCchPrintfW |
|
||||
| test.cpp:197:11:197:14 | *wstr | test.cpp:193:32:193:34 | *str | test.cpp:197:11:197:14 | *wstr | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:197:3:197:9 | call to wprintf | wprintf |
|
||||
| test.cpp:205:12:205:20 | *... + ... | test.cpp:204:25:204:36 | *call to get_string | test.cpp:205:12:205:20 | *... + ... | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:205:5:205:10 | call to printf | printf |
|
||||
| test.cpp:206:12:206:16 | *hello | test.cpp:204:25:204:36 | *call to get_string | test.cpp:206:12:206:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:206:5:206:10 | call to printf | printf |
|
||||
| test.cpp:211:12:211:16 | *hello | test.cpp:209:25:209:36 | *call to get_string | test.cpp:211:12:211:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:211:5:211:10 | call to printf | printf |
|
||||
|
||||
@@ -48,7 +48,7 @@ int main(int argc, char **argv) {
|
||||
printf(choose_message(argc - 1), argc - 1); // GOOD
|
||||
printf(messages[1]); // GOOD
|
||||
printf(message); // GOOD
|
||||
printf(make_message(argc - 1)); // BAD
|
||||
printf(make_message(argc - 1)); // BAD [NOT DETECTED]
|
||||
printf("Hello, World\n"); // GOOD
|
||||
printf(_("Hello, World\n")); // GOOD
|
||||
{
|
||||
@@ -154,7 +154,7 @@ void print_ith_message() {
|
||||
|
||||
void fmt_via_strcpy(char *data) {
|
||||
strcpy(data, "some string");
|
||||
printf(data); // GOOD [FALSE POSITIVE: Due to inaccurate dataflow killers]
|
||||
printf(data); // GOOD
|
||||
}
|
||||
|
||||
void fmt_with_assignment() {
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
| test2.cpp:7:32:7:33 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:6:40:6:72 | sslv23 | sslv23 | test2.cpp:7:32:7:33 | call to context | no_sslv3 has not been set |
|
||||
| test2.cpp:7:32:7:33 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:6:40:6:72 | sslv23 | sslv23 | test2.cpp:7:32:7:33 | call to context | no_tlsv1 has not been set |
|
||||
| test2.cpp:7:32:7:33 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:6:40:6:72 | sslv23 | sslv23 | test2.cpp:7:32:7:33 | call to context | no_tlsv1_1 has not been set |
|
||||
| test2.cpp:15:32:15:33 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:14:40:14:72 | sslv23 | sslv23 | test2.cpp:15:32:15:33 | call to context | no_sslv3 has not been set |
|
||||
| test2.cpp:15:32:15:33 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:14:40:14:72 | sslv23 | sslv23 | test2.cpp:15:32:15:33 | call to context | no_tlsv1 has not been set |
|
||||
| test2.cpp:15:32:15:33 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:14:40:14:72 | sslv23 | sslv23 | test2.cpp:15:32:15:33 | call to context | no_tlsv1_1 has not been set |
|
||||
| test2.cpp:23:32:23:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:23:32:23:64 | sslv23 | sslv23 | test2.cpp:23:32:23:65 | call to context | no_sslv3 has not been set |
|
||||
| test2.cpp:23:32:23:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:23:32:23:64 | sslv23 | sslv23 | test2.cpp:23:32:23:65 | call to context | no_tlsv1 has not been set |
|
||||
| test2.cpp:23:32:23:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:23:32:23:64 | sslv23 | sslv23 | test2.cpp:23:32:23:65 | call to context | no_tlsv1_1 has not been set |
|
||||
@@ -19,22 +14,11 @@
|
||||
| test2.cpp:52:32:52:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:52:32:52:64 | sslv23 | sslv23 | test2.cpp:52:32:52:65 | call to context | no_sslv3 has not been set |
|
||||
| test2.cpp:52:32:52:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:52:32:52:64 | sslv23 | sslv23 | test2.cpp:52:32:52:65 | call to context | no_tlsv1 has not been set |
|
||||
| test2.cpp:52:32:52:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test2.cpp:52:32:52:64 | sslv23 | sslv23 | test2.cpp:52:32:52:65 | call to context | no_tlsv1_1 has not been set |
|
||||
| test3.cpp:7:32:7:62 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test3.cpp:7:32:7:61 | tls | tls | test3.cpp:7:32:7:62 | call to context | no_tlsv1 has not been set |
|
||||
| test3.cpp:7:32:7:62 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test3.cpp:7:32:7:61 | tls | tls | test3.cpp:7:32:7:62 | call to context | no_tlsv1_1 has not been set |
|
||||
| test3.cpp:15:32:15:62 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test3.cpp:15:32:15:61 | tls | tls | test3.cpp:15:32:15:62 | call to context | no_tlsv1 has not been set |
|
||||
| test3.cpp:15:32:15:62 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test3.cpp:15:32:15:61 | tls | tls | test3.cpp:15:32:15:62 | call to context | no_tlsv1_1 has not been set |
|
||||
| test.cpp:11:32:11:69 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:11:32:11:68 | tls_client | tls_client | test.cpp:11:32:11:69 | call to context | no_tlsv1 has not been set |
|
||||
| test.cpp:11:32:11:69 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:11:32:11:68 | tls_client | tls_client | test.cpp:11:32:11:69 | call to context | no_tlsv1_1 has not been set |
|
||||
| test.cpp:17:32:17:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:17:32:17:64 | sslv23 | sslv23 | test.cpp:17:32:17:65 | call to context | no_sslv3 has not been set |
|
||||
| test.cpp:17:32:17:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:17:32:17:64 | sslv23 | sslv23 | test.cpp:17:32:17:65 | call to context | no_tlsv1 has not been set |
|
||||
| test.cpp:17:32:17:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:17:32:17:64 | sslv23 | sslv23 | test.cpp:17:32:17:65 | call to context | no_tlsv1_1 has not been set |
|
||||
| test.cpp:25:32:25:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:25:32:25:64 | sslv23 | sslv23 | test.cpp:25:32:25:65 | call to context | no_sslv3 has not been set |
|
||||
| test.cpp:25:32:25:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:25:32:25:64 | sslv23 | sslv23 | test.cpp:25:32:25:65 | call to context | no_tlsv1 has not been set |
|
||||
| test.cpp:25:32:25:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:25:32:25:64 | sslv23 | sslv23 | test.cpp:25:32:25:65 | call to context | no_tlsv1_1 has not been set |
|
||||
| test.cpp:31:32:31:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:31:32:31:64 | sslv23 | sslv23 | test.cpp:31:32:31:65 | call to context | no_sslv3 has not been set |
|
||||
| test.cpp:31:32:31:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:31:32:31:64 | sslv23 | sslv23 | test.cpp:31:32:31:65 | call to context | no_tlsv1 has not been set |
|
||||
| test.cpp:31:32:31:65 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:31:32:31:64 | sslv23 | sslv23 | test.cpp:31:32:31:65 | call to context | no_tlsv1_1 has not been set |
|
||||
| test.cpp:36:32:36:62 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:36:32:36:61 | tls | tls | test.cpp:36:32:36:62 | call to context | no_tlsv1 has not been set |
|
||||
| test.cpp:36:32:36:62 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:36:32:36:61 | tls | tls | test.cpp:36:32:36:62 | call to context | no_tlsv1_1 has not been set |
|
||||
| test.cpp:41:32:41:62 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:41:32:41:61 | tls | tls | test.cpp:41:32:41:62 | call to context | no_tlsv1 has not been set |
|
||||
| test.cpp:41:32:41:62 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:41:32:41:61 | tls | tls | test.cpp:41:32:41:62 | call to context | no_tlsv1_1 has not been set |
|
||||
| test.cpp:41:32:41:62 | call to context | This usage of 'boost::asio::ssl::context::context' with protocol $@ is not configured correctly: The option $@. | test.cpp:41:32:41:61 | tls | tls | test.cpp:43:6:43:16 | call to set_options | no_tlsv1_2 was set |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user