mirror of
https://github.com/github/codeql.git
synced 2026-04-24 08:15:14 +02:00
Merge branch 'main' into rdmarsh2/cpp/ir-synthetic-destructors
Fixes conflicts in C++ IR tests and Stmt.qll
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Expr extends @expr {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Stmt extends @stmt {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
predicate isStmtWithInitializer(Stmt stmt) { exists(int kind | stmts(stmt, kind, _) | kind = 29) }
|
||||
|
||||
from Expr child, int index, int index_new, Element parent
|
||||
where
|
||||
exprparents(child, index, parent) and
|
||||
if isStmtWithInitializer(parent) then index_new = index - 1 else index_new = index
|
||||
select child, index_new, parent
|
||||
@@ -0,0 +1,9 @@
|
||||
class Stmt extends @stmt {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from Stmt f, Stmt i
|
||||
where
|
||||
for_initialization(f, i) and
|
||||
f instanceof @stmt_for
|
||||
select f, i
|
||||
2244
cpp/downgrades/298438feb146335af824002589cd6d4e96e5dbf9/old.dbscheme
Normal file
2244
cpp/downgrades/298438feb146335af824002589cd6d4e96e5dbf9/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,20 @@
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Stmt extends @stmt {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
predicate isStmtWithInitializer(Stmt stmt) { exists(int kind | stmts(stmt, kind, _) | kind = 29) }
|
||||
|
||||
from Stmt child, int index, int index_new, Element parent
|
||||
where
|
||||
stmtparents(child, index, parent) and
|
||||
(
|
||||
not isStmtWithInitializer(parent)
|
||||
or
|
||||
index > 0
|
||||
) and
|
||||
if isStmtWithInitializer(parent) then index_new = index - 1 else index_new = index
|
||||
select child, index_new, parent
|
||||
@@ -0,0 +1,5 @@
|
||||
description: Support C++20 range-based for initializers
|
||||
compatibility: partial
|
||||
exprparents.rel: run exprparents.qlo
|
||||
stmtparents.rel: run stmtparents.qlo
|
||||
for_initialization.rel: run for_initialization.qlo
|
||||
@@ -1,3 +1,9 @@
|
||||
## 0.12.6
|
||||
|
||||
### New Features
|
||||
|
||||
* A `getInitialization` predicate was added to the `RangeBasedForStmt` class that yields the C++20-style initializer of the range-based `for` statement when it exists.
|
||||
|
||||
## 0.12.5
|
||||
|
||||
### New Features
|
||||
|
||||
5
cpp/ql/lib/change-notes/released/0.12.6.md
Normal file
5
cpp/ql/lib/change-notes/released/0.12.6.md
Normal file
@@ -0,0 +1,5 @@
|
||||
## 0.12.6
|
||||
|
||||
### New Features
|
||||
|
||||
* A `getInitialization` predicate was added to the `RangeBasedForStmt` class that yields the C++20-style initializer of the range-based `for` statement when it exists.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.12.5
|
||||
lastReleaseVersion: 0.12.6
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.12.6-dev
|
||||
version: 0.12.7-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -735,7 +735,9 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred)
|
||||
or
|
||||
s.(ForStmt).getStmt() = e and pred = "getStmt()"
|
||||
or
|
||||
s.(RangeBasedForStmt).getChild(0) = e and pred = "getChild(0)"
|
||||
s.(RangeBasedForStmt).getInitialization() = e and pred = "getInitialization()"
|
||||
or
|
||||
s.(RangeBasedForStmt).getChild(1) = e and pred = "getChild(1)"
|
||||
or
|
||||
s.(RangeBasedForStmt).getBeginEndDeclaration() = e and pred = "getBeginEndDeclaration()"
|
||||
or
|
||||
@@ -743,7 +745,7 @@ private predicate namedStmtChildPredicates(Locatable s, Element e, string pred)
|
||||
or
|
||||
s.(RangeBasedForStmt).getUpdate() = e and pred = "getUpdate()"
|
||||
or
|
||||
s.(RangeBasedForStmt).getChild(4) = e and pred = "getChild(4)"
|
||||
s.(RangeBasedForStmt).getChild(5) = e and pred = "getChild(5)"
|
||||
or
|
||||
s.(RangeBasedForStmt).getStmt() = e and pred = "getStmt()"
|
||||
or
|
||||
@@ -835,7 +837,11 @@ private predicate namedExprChildPredicates(Expr expr, Element ele, string pred)
|
||||
or
|
||||
expr.(OverloadedArrayExpr).getArrayOffset() = ele and pred = "getArrayOffset()"
|
||||
or
|
||||
expr.(OverloadedPointerDereferenceExpr).getExpr() = ele and pred = "getExpr()"
|
||||
// OverloadedPointerDereferenceExpr::getExpr/0 also considers qualifiers, which are already handled above for all Call classes.
|
||||
not expr.(OverloadedPointerDereferenceExpr).getQualifier() =
|
||||
expr.(OverloadedPointerDereferenceExpr).getExpr() and
|
||||
expr.(OverloadedPointerDereferenceExpr).getExpr() = ele and
|
||||
pred = "getExpr()"
|
||||
or
|
||||
expr.(CommaExpr).getLeftOperand() = ele and pred = "getLeftOperand()"
|
||||
or
|
||||
|
||||
@@ -234,7 +234,16 @@ class VariableDeclarationEntry extends DeclarationEntry, @var_decl {
|
||||
* int f(int y) { return y; }
|
||||
* ```
|
||||
*/
|
||||
override string getName() { var_decls(underlyingElement(this), _, _, result, _) and result != "" }
|
||||
override string getName() {
|
||||
exists(string name |
|
||||
var_decls(underlyingElement(this), _, _, name, _) and
|
||||
(
|
||||
name != "" and result = name
|
||||
or
|
||||
name = "" and result = this.getVariable().(LocalVariable).getName()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the variable which is being declared or defined.
|
||||
|
||||
@@ -203,30 +203,42 @@ private class GuardConditionFromIR extends GuardCondition {
|
||||
* `&&` and `||`. See the detailed explanation on predicate `controls`.
|
||||
*/
|
||||
private predicate controlsBlock(BasicBlock controlled, boolean testIsTrue) {
|
||||
exists(IRBlock irb, Instruction instr |
|
||||
exists(IRBlock irb |
|
||||
ir.controls(irb, testIsTrue) and
|
||||
instr = irb.getAnInstruction() and
|
||||
instr.getAst().(ControlFlowNode).getBasicBlock() = controlled and
|
||||
not isUnreachedBlock(irb) and
|
||||
not this.excludeAsControlledInstruction(instr)
|
||||
nonExcludedIRAndBasicBlock(irb, controlled) and
|
||||
not isUnreachedBlock(irb)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private predicate excludeAsControlledInstruction(Instruction instr) {
|
||||
// Exclude the temporaries generated by a ternary expression.
|
||||
exists(TranslatedConditionalExpr tce |
|
||||
instr = tce.getInstruction(ConditionValueFalseStoreTag())
|
||||
or
|
||||
instr = tce.getInstruction(ConditionValueTrueStoreTag())
|
||||
or
|
||||
instr = tce.getInstruction(ConditionValueTrueTempAddressTag())
|
||||
or
|
||||
instr = tce.getInstruction(ConditionValueFalseTempAddressTag())
|
||||
)
|
||||
private predicate excludeAsControlledInstruction(Instruction instr) {
|
||||
// Exclude the temporaries generated by a ternary expression.
|
||||
exists(TranslatedConditionalExpr tce |
|
||||
instr = tce.getInstruction(ConditionValueFalseStoreTag())
|
||||
or
|
||||
// Exclude unreached instructions, as their AST is the whole function and not a block.
|
||||
instr instanceof UnreachedInstruction
|
||||
}
|
||||
instr = tce.getInstruction(ConditionValueTrueStoreTag())
|
||||
or
|
||||
instr = tce.getInstruction(ConditionValueTrueTempAddressTag())
|
||||
or
|
||||
instr = tce.getInstruction(ConditionValueFalseTempAddressTag())
|
||||
)
|
||||
or
|
||||
// Exclude unreached instructions, as their AST is the whole function and not a block.
|
||||
instr instanceof UnreachedInstruction
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `irb` is the `IRBlock` corresponding to the AST basic block
|
||||
* `controlled`, and `irb` does not contain any instruction(s) that should make
|
||||
* the `irb` be ignored.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate nonExcludedIRAndBasicBlock(IRBlock irb, BasicBlock controlled) {
|
||||
exists(Instruction instr |
|
||||
instr = irb.getAnInstruction() and
|
||||
instr.getAst().(ControlFlowNode).getBasicBlock() = controlled and
|
||||
not excludeAsControlledInstruction(instr)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -637,8 +637,10 @@ private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) {
|
||||
any(RangeBasedForStmt for |
|
||||
i = -1 and ni = for and spec.isAt()
|
||||
or
|
||||
i = 0 and ni = for.getInitialization() and spec.isAround()
|
||||
or
|
||||
exists(DeclStmt s | s.getADeclaration() = for.getRangeVariable() |
|
||||
i = 0 and ni = s and spec.isAround()
|
||||
i = 1 and ni = s and spec.isAround()
|
||||
)
|
||||
or
|
||||
exists(DeclStmt s |
|
||||
@@ -649,22 +651,22 @@ private predicate straightLineSparse(Node scope, int i, Node ni, Spec spec) {
|
||||
// DeclStmt in that case.
|
||||
exists(s.getADeclaration())
|
||||
|
|
||||
i = 1 and ni = s and spec.isAround()
|
||||
i = 2 and ni = s and spec.isAround()
|
||||
)
|
||||
or
|
||||
i = 2 and ni = for.getCondition() and spec.isBefore()
|
||||
i = 3 and ni = for.getCondition() and spec.isBefore()
|
||||
or
|
||||
i = 3 and /* BARRIER */ ni = for and spec.isBarrier()
|
||||
i = 4 and /* BARRIER */ ni = for and spec.isBarrier()
|
||||
or
|
||||
exists(DeclStmt declStmt | declStmt.getADeclaration() = for.getVariable() |
|
||||
i = 4 and ni = declStmt and spec.isAfter()
|
||||
i = 5 and ni = declStmt and spec.isAfter()
|
||||
)
|
||||
or
|
||||
i = 5 and ni = for.getStmt() and spec.isAround()
|
||||
i = 6 and ni = for.getStmt() and spec.isAround()
|
||||
or
|
||||
i = 6 and ni = for.getUpdate() and spec.isAround()
|
||||
i = 7 and ni = for.getUpdate() and spec.isAround()
|
||||
or
|
||||
i = 7 and ni = for.getCondition() and spec.isBefore()
|
||||
i = 8 and ni = for.getCondition() and spec.isBefore()
|
||||
)
|
||||
or
|
||||
scope =
|
||||
|
||||
@@ -142,7 +142,7 @@ private newtype TDefOrUseImpl =
|
||||
exists(SsaInternals0::Def def |
|
||||
def.getSourceVariable().getBaseVariable().(BaseIRVariable).getIRVariable().getAst() = p and
|
||||
not def.getValue().asInstruction() instanceof InitializeParameterInstruction and
|
||||
unspecifiedTypeIsModifiableAt(p.getUnspecifiedType(), indirectionIndex)
|
||||
underlyingTypeIsModifiableAt(p.getUnderlyingType(), indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -172,11 +172,13 @@ private predicate isGlobalDefImpl(
|
||||
)
|
||||
}
|
||||
|
||||
private predicate unspecifiedTypeIsModifiableAt(Type unspecified, int indirectionIndex) {
|
||||
indirectionIndex = [1 .. getIndirectionForUnspecifiedType(unspecified).getNumberOfIndirections()] and
|
||||
private predicate underlyingTypeIsModifiableAt(Type underlying, int indirectionIndex) {
|
||||
indirectionIndex =
|
||||
[1 .. getIndirectionForUnspecifiedType(underlying.getUnspecifiedType())
|
||||
.getNumberOfIndirections()] and
|
||||
exists(CppType cppType |
|
||||
cppType.hasUnspecifiedType(unspecified, _) and
|
||||
isModifiableAt(cppType, indirectionIndex + 1)
|
||||
cppType.hasUnderlyingType(underlying, false) and
|
||||
isModifiableAt(cppType, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -452,7 +452,7 @@ private module IsModifiableAtImpl {
|
||||
private predicate impl(CppType cppType, int indirectionIndex) {
|
||||
exists(Type pointerType, Type base |
|
||||
isUnderlyingIndirectionType(pointerType) and
|
||||
cppType.hasUnderlyingType(pointerType, _) and
|
||||
cppType.hasUnderlyingType(pointerType, false) and
|
||||
base = getTypeImpl(pointerType, indirectionIndex)
|
||||
|
|
||||
// The value cannot be modified if it has a const specifier,
|
||||
|
||||
@@ -212,6 +212,8 @@ private predicate usedAsCondition(Expr expr) {
|
||||
or
|
||||
exists(IfStmt ifStmt | ifStmt.getCondition().getFullyConverted() = expr)
|
||||
or
|
||||
exists(ConstexprIfStmt ifStmt | ifStmt.getCondition().getFullyConverted() = expr)
|
||||
or
|
||||
exists(ConditionalExpr condExpr |
|
||||
// The two-operand form of `ConditionalExpr` treats its condition as a value, since it needs to
|
||||
// be reused as a value if the condition is true.
|
||||
@@ -477,7 +479,6 @@ private module IRDeclarationEntries {
|
||||
* This class exists to work around the fact that `DeclStmt`s in some cases
|
||||
* do not have `DeclarationEntry`s. Currently, this is the case for:
|
||||
* - `DeclStmt`s in template instantiations.
|
||||
* - `DeclStmt`s that are generated by the desugaring of range-based for-loops.
|
||||
*
|
||||
* So instead, the IR works with `IRDeclarationEntry`s that synthesize missing
|
||||
* `DeclarationEntry`s when there is no result for `DeclStmt::getDeclarationEntry`.
|
||||
|
||||
@@ -921,6 +921,78 @@ class TranslatedIfStmt extends TranslatedStmt, ConditionContext {
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedConstExprIfStmt extends TranslatedStmt, ConditionContext {
|
||||
override ConstexprIfStmt stmt;
|
||||
|
||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
if this.hasInitialization()
|
||||
then result = this.getInitialization().getFirstInstruction(kind)
|
||||
else result = this.getFirstConditionInstruction(kind)
|
||||
}
|
||||
|
||||
override TranslatedElement getChildInternal(int id) {
|
||||
id = 0 and result = this.getInitialization()
|
||||
or
|
||||
id = 1 and result = this.getCondition()
|
||||
or
|
||||
id = 2 and result = this.getThen()
|
||||
or
|
||||
id = 3 and result = this.getElse()
|
||||
}
|
||||
|
||||
private predicate hasInitialization() { exists(stmt.getInitialization()) }
|
||||
|
||||
private TranslatedStmt getInitialization() {
|
||||
result = getTranslatedStmt(stmt.getInitialization())
|
||||
}
|
||||
|
||||
private TranslatedCondition getCondition() {
|
||||
result = getTranslatedCondition(stmt.getCondition().getFullyConverted())
|
||||
}
|
||||
|
||||
private Instruction getFirstConditionInstruction(EdgeKind kind) {
|
||||
result = this.getCondition().getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
private TranslatedStmt getThen() { result = getTranslatedStmt(stmt.getThen()) }
|
||||
|
||||
private TranslatedStmt getElse() { result = getTranslatedStmt(stmt.getElse()) }
|
||||
|
||||
private predicate hasElse() { exists(stmt.getElse()) }
|
||||
|
||||
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) { none() }
|
||||
|
||||
override Instruction getChildTrueSuccessor(TranslatedCondition child, EdgeKind kind) {
|
||||
child = this.getCondition() and
|
||||
result = this.getThen().getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(TranslatedCondition child, EdgeKind kind) {
|
||||
child = this.getCondition() and
|
||||
if this.hasElse()
|
||||
then result = this.getElse().getFirstInstruction(kind)
|
||||
else result = this.getParent().getChildSuccessor(this, kind)
|
||||
}
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
child = this.getInitialization() and
|
||||
result = this.getFirstConditionInstruction(kind)
|
||||
or
|
||||
(child = this.getThen() or child = this.getElse()) and
|
||||
result = this.getParent().getChildSuccessor(this, kind)
|
||||
}
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
none()
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
result = this.getThen().getALastInstruction()
|
||||
or
|
||||
result = this.getElse().getALastInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TranslatedLoop extends TranslatedStmt, ConditionContext {
|
||||
override Loop stmt;
|
||||
|
||||
@@ -1099,22 +1171,32 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
||||
override RangeBasedForStmt stmt;
|
||||
|
||||
override TranslatedElement getChildInternal(int id) {
|
||||
id = 0 and result = this.getRangeVariableDeclStmt()
|
||||
id = 0 and result = this.getInitialization()
|
||||
or
|
||||
id = 1 and result = this.getRangeVariableDeclStmt()
|
||||
or
|
||||
// Note: `__begin` and `__end` are declared by the same `DeclStmt`
|
||||
id = 1 and result = this.getBeginEndVariableDeclStmt()
|
||||
id = 2 and result = this.getBeginEndVariableDeclStmt()
|
||||
or
|
||||
id = 2 and result = this.getCondition()
|
||||
id = 3 and result = this.getCondition()
|
||||
or
|
||||
id = 3 and result = this.getUpdate()
|
||||
id = 4 and result = this.getUpdate()
|
||||
or
|
||||
id = 4 and result = this.getVariableDeclStmt()
|
||||
id = 5 and result = this.getVariableDeclStmt()
|
||||
or
|
||||
id = 5 and result = this.getBody()
|
||||
id = 6 and result = this.getBody()
|
||||
}
|
||||
|
||||
private predicate hasInitialization() { exists(stmt.getInitialization()) }
|
||||
|
||||
private TranslatedStmt getInitialization() {
|
||||
result = getTranslatedStmt(stmt.getInitialization())
|
||||
}
|
||||
|
||||
override Instruction getFirstInstruction(EdgeKind kind) {
|
||||
result = this.getRangeVariableDeclStmt().getFirstInstruction(kind)
|
||||
if this.hasInitialization()
|
||||
then result = this.getInitialization().getFirstInstruction(kind)
|
||||
else result = this.getFirstRangeVariableDeclStmtInstruction(kind)
|
||||
}
|
||||
|
||||
override Instruction getALastInstructionInternal() {
|
||||
@@ -1124,6 +1206,9 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
||||
override TranslatedElement getLastChild() { result = this.getCondition() }
|
||||
|
||||
override Instruction getChildSuccessorInternal(TranslatedElement child, EdgeKind kind) {
|
||||
child = this.getInitialization() and
|
||||
result = this.getFirstRangeVariableDeclStmtInstruction(kind)
|
||||
or
|
||||
child = this.getRangeVariableDeclStmt() and
|
||||
result = this.getBeginEndVariableDeclStmt().getFirstInstruction(kind)
|
||||
or
|
||||
@@ -1163,6 +1248,10 @@ class TranslatedRangeBasedForStmt extends TranslatedStmt, ConditionContext {
|
||||
)
|
||||
}
|
||||
|
||||
private Instruction getFirstRangeVariableDeclStmtInstruction(EdgeKind kind) {
|
||||
result = this.getRangeVariableDeclStmt().getFirstInstruction(kind)
|
||||
}
|
||||
|
||||
private TranslatedDeclStmt getBeginEndVariableDeclStmt() {
|
||||
exists(IRVariableDeclarationEntry entry |
|
||||
entry.getStmt() = stmt.getBeginEndDeclaration() and
|
||||
|
||||
@@ -892,6 +892,26 @@ class DoStmt extends Loop, @stmt_end_test_while {
|
||||
class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
override string getAPrimaryQlClass() { result = "RangeBasedForStmt" }
|
||||
|
||||
/**
|
||||
* Gets the initialization statement of this 'for' statement, if any.
|
||||
*
|
||||
* For example, for
|
||||
* ```
|
||||
* for (int x = y; auto z : ... ) { }
|
||||
* ```
|
||||
* the result is `int x = y;`.
|
||||
*
|
||||
* Does not hold if the initialization statement is missing or an empty statement, as in
|
||||
* ```
|
||||
* for (auto z : ...) { }
|
||||
* ```
|
||||
* or
|
||||
* ```
|
||||
* for (; auto z : ) { }
|
||||
* ```
|
||||
*/
|
||||
Stmt getInitialization() { for_initialization(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
/**
|
||||
* Gets the 'body' statement of this range-based 'for' statement.
|
||||
*
|
||||
@@ -901,7 +921,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
* ```
|
||||
* the result is the `BlockStmt` `{ y += x; }`.
|
||||
*/
|
||||
override Stmt getStmt() { result = this.getChild(5) }
|
||||
override Stmt getStmt() { result = this.getChild(6) }
|
||||
|
||||
override string toString() { result = "for(...:...) ..." }
|
||||
|
||||
@@ -914,7 +934,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
* ```
|
||||
* the result is `int x`.
|
||||
*/
|
||||
LocalVariable getVariable() { result = this.getChild(4).(DeclStmt).getADeclaration() }
|
||||
LocalVariable getVariable() { result = this.getChild(5).(DeclStmt).getADeclaration() }
|
||||
|
||||
/**
|
||||
* Gets the expression giving the range to iterate over.
|
||||
@@ -928,7 +948,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
Expr getRange() { result = this.getRangeVariable().getInitializer().getExpr() }
|
||||
|
||||
/** Gets the compiler-generated `__range` variable after desugaring. */
|
||||
LocalVariable getRangeVariable() { result = this.getChild(0).(DeclStmt).getADeclaration() }
|
||||
LocalVariable getRangeVariable() { result = this.getChild(1).(DeclStmt).getADeclaration() }
|
||||
|
||||
/**
|
||||
* Gets the compiler-generated `__begin != __end` which is the
|
||||
@@ -936,7 +956,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
* It will be either an `NEExpr` or a call to a user-defined
|
||||
* `operator!=`.
|
||||
*/
|
||||
override Expr getCondition() { result = this.getChild(2) }
|
||||
override Expr getCondition() { result = this.getChild(3) }
|
||||
|
||||
override Expr getControllingExpr() { result = this.getCondition() }
|
||||
|
||||
@@ -945,7 +965,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
* `__end`, initializing them to the values they have before entering the
|
||||
* desugared loop.
|
||||
*/
|
||||
DeclStmt getBeginEndDeclaration() { result = this.getChild(1) }
|
||||
DeclStmt getBeginEndDeclaration() { result = this.getChild(2) }
|
||||
|
||||
/** Gets the compiler-generated `__begin` variable after desugaring. */
|
||||
LocalVariable getBeginVariable() { result = this.getBeginEndDeclaration().getDeclaration(0) }
|
||||
@@ -959,7 +979,7 @@ class RangeBasedForStmt extends Loop, @stmt_range_based_for {
|
||||
* be either a `PrefixIncrExpr` or a call to a user-defined
|
||||
* `operator++`.
|
||||
*/
|
||||
Expr getUpdate() { result = this.getChild(3) }
|
||||
Expr getUpdate() { result = this.getChild(4) }
|
||||
|
||||
/** Gets the compiler-generated `__begin` variable after desugaring. */
|
||||
LocalVariable getAnIterationVariable() { result = this.getBeginVariable() }
|
||||
|
||||
@@ -2050,8 +2050,11 @@ switch_body(
|
||||
int body_id: @stmt ref
|
||||
);
|
||||
|
||||
@stmt_for_or_range_based_for = @stmt_for
|
||||
| @stmt_range_based_for;
|
||||
|
||||
for_initialization(
|
||||
unique int for_stmt: @stmt_for ref,
|
||||
unique int for_stmt: @stmt_for_or_range_based_for ref,
|
||||
int init_id: @stmt ref
|
||||
);
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Expr extends @expr {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Stmt extends @stmt {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
predicate isStmtWithInitializer(Stmt stmt) { exists(int kind | stmts(stmt, kind, _) | kind = 29) }
|
||||
|
||||
from Expr child, int index, int index_new, Element parent
|
||||
where
|
||||
exprparents(child, index, parent) and
|
||||
if isStmtWithInitializer(parent) then index_new = index + 1 else index_new = index
|
||||
select child, index_new, parent
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
class Element extends @element {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class Stmt extends @stmt {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
predicate isStmtWithInitializer(Stmt stmt) { exists(int kind | stmts(stmt, kind, _) | kind = 29) }
|
||||
|
||||
from Stmt child, int index, int index_new, Element parent
|
||||
where
|
||||
stmtparents(child, index, parent) and
|
||||
if isStmtWithInitializer(parent) then index_new = index + 1 else index_new = index
|
||||
select child, index_new, parent
|
||||
@@ -0,0 +1,4 @@
|
||||
description: Support C++20 range-based for initializers
|
||||
compatibility: partial
|
||||
exprparents.rel: run exprparents.qlo
|
||||
stmtparents.rel: run stmtparents.qlo
|
||||
@@ -1,3 +1,10 @@
|
||||
## 0.9.5
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "non-constant format string" query (`cpp/non-constant-format`) has been updated to produce fewer false positives.
|
||||
* Added dataflow models for the `gettext` function variants.
|
||||
|
||||
## 0.9.4
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
/**
|
||||
* @name Non-constant format string
|
||||
* @description Passing a non-constant 'format' string to a printf-like function can lead
|
||||
* @description Passing a value that is not a string literal 'format' string to a printf-like function can lead
|
||||
* to a mismatch between the number of arguments defined by the 'format' and the number
|
||||
* of arguments actually passed to the function. If the format string ultimately stems
|
||||
* from an untrusted source, this can be used for exploits.
|
||||
* This query finds format strings coming from non-literal sources. Note that format strings of
|
||||
* type `const char*` it is still considered non-constant if the value is not coming from a string
|
||||
* literal. For example, for a parameter with type `const char*` of an exported function that is
|
||||
* used as a format string, there is no way to ensure the originating value was a string literal.
|
||||
* @kind problem
|
||||
* @problem.severity recommendation
|
||||
* @security-severity 9.3
|
||||
@@ -16,135 +20,118 @@
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.models.implementations.GetText
|
||||
import semmle.code.cpp.commons.Printf
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import semmle.code.cpp.ir.dataflow.internal.ModelUtil
|
||||
import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
import semmle.code.cpp.ir.IR
|
||||
|
||||
// For the following `...gettext` functions, we assume that
|
||||
// all translations preserve the type and order of `%` specifiers
|
||||
// (and hence are safe to use as format strings). This
|
||||
// assumption is hard-coded into the query.
|
||||
predicate whitelistFunction(Function f, int arg) {
|
||||
// basic variations of gettext
|
||||
f.getName() = "_" and arg = 0
|
||||
or
|
||||
exists(FunctionInput input |
|
||||
f.(GetTextFunction).hasDataFlow(input, _) and
|
||||
input.isParameterDeref(arg)
|
||||
)
|
||||
}
|
||||
|
||||
// we assume that ALL uses of the `_` macro (and calls to `gettext`)
|
||||
// return constant string literals
|
||||
predicate underscoreMacro(Expr e) {
|
||||
exists(MacroInvocation mi |
|
||||
mi.getMacroName() = "_" and
|
||||
mi.getExpr() = e
|
||||
)
|
||||
or
|
||||
e = any(GetTextFunction gettext).getACallToThisFunction()
|
||||
class UncalledFunction extends Function {
|
||||
UncalledFunction() {
|
||||
not exists(Call c | c.getTarget() = this) and
|
||||
// Ignore functions that appear to be function pointers
|
||||
// function pointers may be seen as uncalled statically
|
||||
not exists(FunctionAccess fa | fa.getTarget() = this)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `t` cannot hold a character array, directly or indirectly.
|
||||
* Holds if `node` is a non-constant source of data flow for non-const format string detection.
|
||||
* This is defined as either:
|
||||
* 1) a `FlowSource`
|
||||
* 2) a parameter of an 'uncalled' function
|
||||
* 3) an argument to a function with no definition that is not known to define the output through its input
|
||||
* 4) an out arg of a function with no definition that is not known to define the output through its input
|
||||
*
|
||||
* The latter two cases address identifying standard string manipulation libraries as input sources
|
||||
* e.g., strcpy. More simply, functions without definitions that are known to manipulate the
|
||||
* input to produce an output are not sources. Instead the ultimate source of input to these functions
|
||||
* should be considered as the source.
|
||||
*
|
||||
* False Negative Implication: This approach has false negatives (fails to identify non-const sources)
|
||||
* when the source is a field of a struct or object and the initialization is not observed statically.
|
||||
* There are 3 general cases where this can occur:
|
||||
* 1) Parameters of uncalled functions that are structs/objects and a field is accessed for a format string.
|
||||
* 2) A local variable that is a struct/object and initialization of the field occurs in code that is unseen statically.
|
||||
* e.g., an object constructor isn't known statically, or a function sets fields
|
||||
* of a struct, but the function is not known statically.
|
||||
* 3) A function meeting cases (3) and (4) above returns (through an out argument or return value)
|
||||
* a struct or object where a field containing a format string has been initialized.
|
||||
*
|
||||
* Note, uninitialized variables used as format strings are never detected by design.
|
||||
* Uninitialized variables are a separate vulnerability concern and should be addressed by a separate query.
|
||||
*/
|
||||
predicate cannotContainString(Type t, boolean isIndirect) {
|
||||
isIndirect = false and
|
||||
exists(Type unspecified |
|
||||
unspecified = t.getUnspecifiedType() and
|
||||
not unspecified instanceof UnknownType
|
||||
predicate isNonConst(DataFlow::Node node) {
|
||||
node instanceof FlowSource
|
||||
or
|
||||
// Parameters of uncalled functions that aren't const
|
||||
exists(UncalledFunction f, Parameter p |
|
||||
f.getAParameter() = p and
|
||||
p = node.asParameter()
|
||||
)
|
||||
or
|
||||
// Consider as an input any out arg of a function or a function's return where the function is not:
|
||||
// 1. a function with a known dataflow or taintflow from input to output and the `node` is the output
|
||||
// 2. a function where there is a known definition
|
||||
// i.e., functions that with unknown bodies and are not known to define the output through its input
|
||||
// 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()
|
||||
|
|
||||
unspecified instanceof BuiltInType or
|
||||
unspecified instanceof IntegralOrEnumType
|
||||
func = call.getStaticCallTarget() 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)
|
||||
|
|
||||
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()]
|
||||
)
|
||||
}
|
||||
|
||||
predicate isNonConst(DataFlow::Node node, boolean isIndirect) {
|
||||
exists(Expr e |
|
||||
e = node.asExpr() and isIndirect = false
|
||||
or
|
||||
e = node.asIndirectExpr() and isIndirect = true
|
||||
|
|
||||
exists(FunctionCall fc | fc = e |
|
||||
not (
|
||||
whitelistFunction(fc.getTarget(), _) or
|
||||
fc.getTarget().hasDefinition()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Parameter p | p = e.(VariableAccess).getTarget() |
|
||||
p.getFunction().getName() = "main" and p.getType() instanceof PointerType
|
||||
)
|
||||
or
|
||||
e instanceof CrementOperation
|
||||
or
|
||||
e instanceof AddressOfExpr
|
||||
or
|
||||
e instanceof ReferenceToExpr
|
||||
or
|
||||
e instanceof AssignPointerAddExpr
|
||||
or
|
||||
e instanceof AssignPointerSubExpr
|
||||
or
|
||||
e instanceof PointerArithmeticOperation
|
||||
or
|
||||
e instanceof FieldAccess
|
||||
or
|
||||
e instanceof PointerDereferenceExpr
|
||||
or
|
||||
e instanceof AddressOfExpr
|
||||
or
|
||||
e instanceof ExprCall
|
||||
or
|
||||
e instanceof NewArrayExpr
|
||||
or
|
||||
exists(Variable v | v = e.(VariableAccess).getTarget() |
|
||||
v.getType().(ArrayType).getBaseType() instanceof CharType and
|
||||
exists(AssignExpr ae |
|
||||
ae.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = v
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
node instanceof DataFlow::DefinitionByReferenceNode and
|
||||
isIndirect = true
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate isBarrierNode(DataFlow::Node node) {
|
||||
underscoreMacro([node.asExpr(), node.asIndirectExpr()])
|
||||
or
|
||||
exists(node.asExpr()) and
|
||||
cannotContainString(node.getType(), false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a sink is a format string of any
|
||||
* `FormattingFunctionCall`.
|
||||
*/
|
||||
predicate isSinkImpl(DataFlow::Node sink, Expr formatString) {
|
||||
[sink.asExpr(), sink.asIndirectExpr()] = formatString and
|
||||
exists(FormattingFunctionCall fc | formatString = fc.getArgument(fc.getFormatParameterIndex()))
|
||||
}
|
||||
|
||||
module NonConstFlowConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(boolean isIndirect, Type t |
|
||||
isNonConst(source, isIndirect) and
|
||||
t = source.getType() and
|
||||
not cannotContainString(t, isIndirect)
|
||||
)
|
||||
}
|
||||
predicate isSource(DataFlow::Node source) { isNonConst(source) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { isBarrierNode(node) }
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// Ignore tracing non-const through array indices
|
||||
exists(ArrayExpr a | a.getArrayOffset() = node.asExpr())
|
||||
}
|
||||
}
|
||||
|
||||
module NonConstFlow = TaintTracking::Global<NonConstFlowConfig>;
|
||||
|
||||
from FormattingFunctionCall call, Expr formatString
|
||||
from FormattingFunctionCall call, Expr formatString, DataFlow::Node sink
|
||||
where
|
||||
call.getArgument(call.getFormatParameterIndex()) = formatString and
|
||||
exists(DataFlow::Node sink |
|
||||
NonConstFlow::flowTo(sink) and
|
||||
isSinkImpl(sink, formatString)
|
||||
)
|
||||
NonConstFlow::flowTo(sink) and
|
||||
isSinkImpl(sink, formatString)
|
||||
select formatString,
|
||||
"The format string argument to " + call.getTarget().getName() +
|
||||
" should be constant to prevent security issues and other potential errors."
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added dataflow models for the `gettext` function variants.
|
||||
6
cpp/ql/src/change-notes/released/0.9.5.md
Normal file
6
cpp/ql/src/change-notes/released/0.9.5.md
Normal file
@@ -0,0 +1,6 @@
|
||||
## 0.9.5
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "non-constant format string" query (`cpp/non-constant-format`) has been updated to produce fewer false positives.
|
||||
* Added dataflow models for the `gettext` function variants.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.4
|
||||
lastReleaseVersion: 0.9.5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.9.5-dev
|
||||
version: 0.9.6-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,6 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ir.cpp:2138:21:2138:21 | Chi: x | Instruction 'Chi: x' has no successors in function '$@'. | ir.cpp:2136:6:2136:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
@@ -29,4 +28,8 @@ nonUniqueEnclosingIRFunction
|
||||
fieldAddressOnNonPointer
|
||||
thisArgumentIsNonPointer
|
||||
nonUniqueIRVariable
|
||||
| ir.cpp:2154:68:2154:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2157:68:2157:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2163:36:2163:37 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2168:68:2168:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
missingCppType
|
||||
|
||||
@@ -6,7 +6,6 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ir.cpp:2138:21:2138:21 | Chi: x | Instruction 'Chi: x' has no successors in function '$@'. | ir.cpp:2136:6:2136:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
@@ -29,4 +28,8 @@ nonUniqueEnclosingIRFunction
|
||||
fieldAddressOnNonPointer
|
||||
thisArgumentIsNonPointer
|
||||
nonUniqueIRVariable
|
||||
| ir.cpp:2154:68:2154:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2157:68:2157:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2163:36:2163:37 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2168:68:2168:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
missingCppType
|
||||
|
||||
@@ -2129,6 +2129,7 @@ public:
|
||||
~ClassWithDestructor() { delete x; }
|
||||
|
||||
void set_x(char y) { *x = y; }
|
||||
char get_x() { return *x; }
|
||||
};
|
||||
|
||||
constexpr bool initialization_with_destructor_bool = true;
|
||||
@@ -2152,8 +2153,42 @@ void initialization_with_destructor(bool b, char c) {
|
||||
ClassWithDestructor x;
|
||||
for(vector<ClassWithDestructor> ys(x); ClassWithDestructor y : ys)
|
||||
y.set_x('a');
|
||||
|
||||
for(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) {
|
||||
if (y == 1)
|
||||
return;
|
||||
}
|
||||
|
||||
for(vector<ClassWithDestructor> ys(x); ClassWithDestructor y : ys) {
|
||||
ClassWithDestructor z1;
|
||||
ClassWithDestructor z2;
|
||||
}
|
||||
}
|
||||
|
||||
void static_variable_with_destructor_1() {
|
||||
ClassWithDestructor a;
|
||||
static ClassWithDestructor b;
|
||||
}
|
||||
|
||||
void static_variable_with_destructor_2() {
|
||||
static ClassWithDestructor a;
|
||||
ClassWithDestructor b;
|
||||
}
|
||||
|
||||
void static_variable_with_destructor_3() {
|
||||
ClassWithDestructor a;
|
||||
ClassWithDestructor b;
|
||||
static ClassWithDestructor c;
|
||||
}
|
||||
|
||||
static ClassWithDestructor global_class_with_destructor;
|
||||
|
||||
void TryCatchDestructors(bool b) {
|
||||
try {
|
||||
String s;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,6 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ir.cpp:2138:21:2138:21 | IndirectMayWriteSideEffect: x | Instruction 'IndirectMayWriteSideEffect: x' has no successors in function '$@'. | ir.cpp:2136:6:2136:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2140:39:2140:39 | IndirectMayWriteSideEffect: call to ClassWithDestructor | Instruction 'IndirectMayWriteSideEffect: call to ClassWithDestructor' has no successors in function '$@'. | ir.cpp:2136:6:2136:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2140:42:2140:76 | Constant: initialization_with_destructor_bool | Instruction 'Constant: initialization_with_destructor_bool' has no successors in function '$@'. | ir.cpp:2136:6:2136:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2141:9:2141:9 | IndirectMayWriteSideEffect: x | Instruction 'IndirectMayWriteSideEffect: x' has no successors in function '$@'. | ir.cpp:2136:6:2136:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
@@ -41,5 +37,8 @@ nonUniqueEnclosingIRFunction
|
||||
fieldAddressOnNonPointer
|
||||
thisArgumentIsNonPointer
|
||||
nonUniqueIRVariable
|
||||
| ir.cpp:2153:68:2153:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2136:6:2136:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2154:68:2154:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2157:68:2157:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2163:36:2163:37 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2168:68:2168:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
missingCppType
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,6 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ir.cpp:2138:21:2138:21 | IndirectMayWriteSideEffect: x | Instruction 'IndirectMayWriteSideEffect: x' has no successors in function '$@'. | ir.cpp:2136:6:2136:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
@@ -29,4 +28,8 @@ nonUniqueEnclosingIRFunction
|
||||
fieldAddressOnNonPointer
|
||||
thisArgumentIsNonPointer
|
||||
nonUniqueIRVariable
|
||||
| ir.cpp:2154:68:2154:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2157:68:2157:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2163:36:2163:37 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2168:68:2168:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
missingCppType
|
||||
|
||||
@@ -6,7 +6,6 @@ missingOperandType
|
||||
duplicateChiOperand
|
||||
sideEffectWithoutPrimary
|
||||
instructionWithoutSuccessor
|
||||
| ir.cpp:2138:21:2138:21 | IndirectMayWriteSideEffect: x | Instruction 'IndirectMayWriteSideEffect: x' has no successors in function '$@'. | ir.cpp:2136:6:2136:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
ambiguousSuccessors
|
||||
unexplainedLoop
|
||||
unnecessaryPhiInstruction
|
||||
@@ -29,4 +28,8 @@ nonUniqueEnclosingIRFunction
|
||||
fieldAddressOnNonPointer
|
||||
thisArgumentIsNonPointer
|
||||
nonUniqueIRVariable
|
||||
| ir.cpp:2154:68:2154:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2157:68:2157:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2163:36:2163:37 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
| ir.cpp:2168:68:2168:69 | VariableAddress: ys | Variable address instruction 'VariableAddress: ys' has no associated variable, in function '$@'. | ir.cpp:2137:6:2137:35 | void initialization_with_destructor(bool, char) | void initialization_with_destructor(bool, char) |
|
||||
missingCppType
|
||||
|
||||
@@ -23,7 +23,7 @@ extern char *dcngettext (const char *__domainname, const char *__msgid1,
|
||||
extern char *any_random_function(const char *);
|
||||
|
||||
#define NULL ((void*)0)
|
||||
#define _(X) any_random_function((X))
|
||||
#define _(X) gettext(X)
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if(argc > 1)
|
||||
@@ -40,10 +40,9 @@ int main(int argc, char **argv) {
|
||||
printf(gettext("%d arguments\n"), argc-1); // GOOD
|
||||
printf(any_random_function("%d arguments\n"), argc-1); // BAD
|
||||
|
||||
// Even though `_` is mapped to `some_random_function` above,
|
||||
// the following call should not be flagged.
|
||||
printf(_(any_random_function("%d arguments\n")),
|
||||
argc-1); // GOOD
|
||||
|
||||
|
||||
printf(_(any_random_function("%d arguments\n")), argc-1); // BAD
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
| NonConstantFormat.c:30:10:30:16 | access to array | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| NonConstantFormat.c:41:9:41:27 | call to any_random_function | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| NonConstantFormat.c:45:9:45:48 | call to gettext | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| nested.cpp:21:23:21:26 | fmt0 | The format string argument to snprintf should be constant to prevent security issues and other potential errors. |
|
||||
| nested.cpp:79:32:79:38 | call to get_fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
|
||||
| nested.cpp:87:18:87:20 | fmt | The format string argument to diagnostic should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:51:10:51:21 | call to make_message | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:57:12:57:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:60:12:60:21 | call to const_wash | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:61:12:61:26 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:62:12:62:17 | + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:63:12:63:18 | * ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:64:12:64:18 | & ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:65:12:65:39 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:67:10:67:35 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:70:12:70:20 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:76:12:76:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:82:12:82:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:88:12:88:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:93:12:93:18 | ++ ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:100:12:100:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:110:12:110:24 | new[] | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:115:12:115:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:130:20:130:26 | access to array | The format string argument to sprintf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:157:12:157:15 | data | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:170:12:170:14 | res | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:195:31:195:33 | str | The format string argument to StringCchPrintfW should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:197:11:197:14 | wstr | The format string argument to wprintf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:205:12:205:20 | ... + ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:206:12:206:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:211:12:211:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:217:12:217:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:223:12:223:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:228:12:228:18 | ++ ... | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:235:12:235:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:242:12:242:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| test.cpp:247:12:247:16 | hello | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
|
||||
@@ -18,7 +18,7 @@ extern "C" int snprintf ( char * s, int n, const char * format, ... );
|
||||
struct A {
|
||||
void do_print(const char *fmt0) {
|
||||
char buf[32];
|
||||
snprintf(buf, 32, fmt0); // GOOD [FALSE POSITIVE]
|
||||
snprintf(buf, 32, fmt0); // BAD, all paths from unknown const char*, not assuming literal
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,12 +34,12 @@ struct C {
|
||||
void do_some_printing(const char *fmt) {
|
||||
b.do_printing(fmt);
|
||||
}
|
||||
const char *ext_fmt_str(void);
|
||||
const char *ext_fmt_str(void); // NOTE: not assuming result is literal
|
||||
};
|
||||
|
||||
void foo(void) {
|
||||
C c;
|
||||
c.do_some_printing(c.ext_fmt_str()); // BAD [NOT DETECTED]
|
||||
c.do_some_printing(c.ext_fmt_str());
|
||||
}
|
||||
|
||||
struct some_class {
|
||||
@@ -76,12 +76,12 @@ void diagnostic(const char *fmt, ...)
|
||||
}
|
||||
|
||||
void bar(void) {
|
||||
diagnostic (some_instance->get_fmt()); // BAD
|
||||
diagnostic (some_instance->get_fmt()); // BAD const char* but not assuming literal
|
||||
}
|
||||
|
||||
namespace ns {
|
||||
|
||||
class blab {
|
||||
class blab {
|
||||
void out1(void) {
|
||||
char *fmt = (char *)__builtin_alloca(10);
|
||||
diagnostic(fmt); // BAD
|
||||
|
||||
@@ -54,66 +54,66 @@ int main(int argc, char **argv) {
|
||||
{
|
||||
char hello[] = "hello, World\n";
|
||||
hello[0] = 'H';
|
||||
printf(hello); // BAD
|
||||
printf(hello); // GOOD
|
||||
printf(_(hello)); // GOOD
|
||||
printf(gettext(hello)); // GOOD
|
||||
printf(const_wash(hello)); // BAD
|
||||
printf((hello + 1) + 1); // BAD
|
||||
printf(+hello); // BAD
|
||||
printf(*&hello); // BAD
|
||||
printf(&*hello); // BAD
|
||||
printf((char*)(void*)+(hello+1) + 1); // BAD
|
||||
printf(const_wash(hello)); // GOOD
|
||||
printf((hello + 1) + 1); // GOOD
|
||||
printf(+hello); // GOOD
|
||||
printf(*&hello); // GOOD
|
||||
printf(&*hello); // GOOD
|
||||
printf((char*)(void*)+(hello+1) + 1); // GOOD
|
||||
}
|
||||
printf(("Hello, World\n" + 1) + 1); // BAD
|
||||
printf(("Hello, World\n" + 1) + 1); // GOOD
|
||||
{
|
||||
const char *hello = "Hello, World\n";
|
||||
printf(hello + 1); // BAD
|
||||
printf(hello + 1); // GOOD
|
||||
printf(hello); // GOOD
|
||||
}
|
||||
{
|
||||
const char *hello = "Hello, World\n";
|
||||
hello += 1;
|
||||
printf(hello); // BAD
|
||||
printf(hello); // GOOD
|
||||
}
|
||||
{
|
||||
// Same as above block but using "x = x + 1" syntax
|
||||
const char *hello = "Hello, World\n";
|
||||
hello = hello + 1;
|
||||
printf(hello); // BAD
|
||||
printf(hello); // GOOD
|
||||
}
|
||||
{
|
||||
// Same as above block but using "x++" syntax
|
||||
const char *hello = "Hello, World\n";
|
||||
hello++;
|
||||
printf(hello); // BAD
|
||||
printf(hello); // GOOD
|
||||
}
|
||||
{
|
||||
// Same as above block but using "++x" as subexpression
|
||||
const char *hello = "Hello, World\n";
|
||||
printf(++hello); // BAD
|
||||
printf(++hello); // GOOD
|
||||
}
|
||||
{
|
||||
// Same as above block but through a pointer
|
||||
const char *hello = "Hello, World\n";
|
||||
const char **p = &hello;
|
||||
(*p)++;
|
||||
printf(hello); // BAD
|
||||
printf(hello); // GOOD
|
||||
}
|
||||
{
|
||||
// Same as above block but through a C++ reference
|
||||
const char *hello = "Hello, World\n";
|
||||
const char *&p = hello;
|
||||
p++;
|
||||
printf(hello); // BAD [NOT DETECTED]
|
||||
printf(hello); // GOOD
|
||||
}
|
||||
if (gettext_debug) {
|
||||
printf(new char[100]); // BAD
|
||||
printf(new char[100]); // BAD [FALSE NEGATIVE: uninitialized value not considered a source]
|
||||
}
|
||||
{
|
||||
const char *hello = "Hello, World\n";
|
||||
const char *const *p = &hello; // harmless reference to const pointer
|
||||
printf(hello); // GOOD [FALSE POSITIVE]
|
||||
hello++; // modification comes after use and so does no harm
|
||||
const char *const *p = &hello;
|
||||
printf(hello); // GOOD
|
||||
hello++;
|
||||
}
|
||||
printf(argc > 2 ? "More than one\n" : _("Only one\n")); // GOOD
|
||||
|
||||
@@ -154,7 +154,7 @@ void print_ith_message() {
|
||||
|
||||
void fmt_via_strcpy(char *data) {
|
||||
strcpy(data, "some string");
|
||||
printf(data); // BAD
|
||||
printf(data); // GOOD [FALSE POSITIVE: Due to inaccurate dataflow killers]
|
||||
}
|
||||
|
||||
void fmt_with_assignment() {
|
||||
@@ -163,3 +163,107 @@ void fmt_with_assignment() {
|
||||
x = y = "a";
|
||||
printf(y); // GOOD
|
||||
}
|
||||
|
||||
void fmt_via_strcpy_bad(char *data) {
|
||||
char res[100];
|
||||
strcpy(res, data);
|
||||
printf(res); // BAD
|
||||
}
|
||||
|
||||
|
||||
int wprintf(const wchar_t *format,...);
|
||||
typedef wchar_t *STRSAFE_LPWSTR;
|
||||
typedef const wchar_t *STRSAFE_LPCWSTR;
|
||||
typedef unsigned int size_t;
|
||||
|
||||
void StringCchPrintfW(
|
||||
STRSAFE_LPWSTR pszDest,
|
||||
size_t cchDest,
|
||||
STRSAFE_LPCWSTR pszFormat,
|
||||
...
|
||||
);
|
||||
|
||||
void wchar_t_test_good(){
|
||||
wchar_t wstr[100];
|
||||
StringCchPrintfW(wstr, 100, L"STRING"); // GOOD
|
||||
|
||||
wprintf(wstr); // GOOD
|
||||
}
|
||||
|
||||
void wchar_t_test_bad(wchar_t* str){
|
||||
wchar_t wstr[100];
|
||||
StringCchPrintfW(wstr, 100, str); // BAD
|
||||
|
||||
wprintf(wstr); // BAD
|
||||
}
|
||||
|
||||
char* get_string();
|
||||
|
||||
void pointer_arithmetic_test_on_bad_string(){
|
||||
{
|
||||
const char *hello = get_string();
|
||||
printf(hello + 1); // BAD
|
||||
printf(hello); // BAD
|
||||
}
|
||||
{
|
||||
const char *hello = get_string();
|
||||
hello += 1;
|
||||
printf(hello); // BAD
|
||||
}
|
||||
{
|
||||
// Same as above block but using "x = x + 1" syntax
|
||||
const char *hello = get_string();
|
||||
hello = hello + 1;
|
||||
printf(hello); // BAD
|
||||
}
|
||||
{
|
||||
// Same as above block but using "x++" syntax
|
||||
const char *hello = get_string();
|
||||
hello++;
|
||||
printf(hello); // BAD
|
||||
}
|
||||
{
|
||||
// Same as above block but using "++x" as subexpression
|
||||
const char *hello = get_string();
|
||||
printf(++hello); // BAD
|
||||
}
|
||||
{
|
||||
// Same as above block but through a pointer
|
||||
const char *hello = get_string();
|
||||
const char **p = &hello;
|
||||
(*p)++;
|
||||
printf(hello); // BAD
|
||||
}
|
||||
{
|
||||
// Same as above block but through a C++ reference
|
||||
const char *hello = get_string();
|
||||
const char *&p = hello;
|
||||
p++;
|
||||
printf(hello); // BAD
|
||||
}
|
||||
{
|
||||
const char *hello = get_string();
|
||||
const char *const *p = &hello;
|
||||
printf(hello); // BAD
|
||||
}
|
||||
}
|
||||
|
||||
struct struct1 {
|
||||
char *non_const;
|
||||
const char* const_member = "TEST";
|
||||
char * const_member2 = "TEST";
|
||||
struct struct2{
|
||||
char *nested_non_const;
|
||||
const char* nested_const_member = "TEST";
|
||||
char * nested_const_member2 = "TEST";
|
||||
} nested;
|
||||
};
|
||||
|
||||
int test_uncalled_func_with_struct_param(struct struct1 *s) {
|
||||
printf(s->non_const); // BAD [FALSE NEGATIVE]
|
||||
printf(s->const_member); // GOOD
|
||||
printf(s->const_member2); // GOOD
|
||||
printf(s->nested.nested_non_const); // BAD [FALSE NEGATIVE]
|
||||
printf(s->nested.nested_const_member); // GOOD
|
||||
printf(s->nested.nested_const_member2); // GOOD
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
| consts.cpp:81:9:81:10 | c8 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| consts.cpp:86:9:86:10 | v1 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| consts.cpp:91:9:91:10 | v2 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
| consts.cpp:95:9:95:10 | v3 | The format string argument to printf should be constant to prevent security issues and other potential errors. |
|
||||
|
||||
@@ -75,7 +75,7 @@ void a() {
|
||||
// GOOD: constFuncToArray() always returns a value from gc1, which is always constant
|
||||
printf(constFuncToArray(0));
|
||||
|
||||
// BAD: format string is not constant
|
||||
// BAD: format string is not constant [NOT DETECTED]
|
||||
char c8[10];
|
||||
sprintf(c8, "%d", 1);
|
||||
printf(c8);
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
| | forstmt01.cpp:3:6:3:14 | for_loop1 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (const iterator)... | <none> |
|
||||
| | forstmt01.cpp:3:6:3:14 | for_loop1 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (const iterator)... | <none> |
|
||||
| | forstmt01.cpp:3:6:3:14 | for_loop1 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (const vector<int>)... | <none> |
|
||||
| | forstmt01.cpp:3:6:3:14 | for_loop1 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (const vector<int>)... | <none> |
|
||||
| | forstmt01.cpp:3:6:3:14 | for_loop1 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (reference dereference) | <none> |
|
||||
| | forstmt01.cpp:3:6:3:14 | for_loop1 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (reference dereference) | <none> |
|
||||
| | forstmt01.cpp:3:6:3:14 | for_loop1 | 0 | 5 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | initializer for (__range) | 4: declaration |
|
||||
| | forstmt01.cpp:3:6:3:14 | for_loop1 | 0 | 9 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | initializer for (__begin) | 5: (__range) |
|
||||
| | forstmt01.cpp:3:6:3:14 | for_loop1 | 0 | 12 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | initializer for (__end) | 5: (__end) |
|
||||
| | forstmt02.cpp:3:6:3:14 | for_loop2 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (const iterator)... | <none> |
|
||||
| | forstmt02.cpp:3:6:3:14 | for_loop2 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (const iterator)... | <none> |
|
||||
| | forstmt02.cpp:3:6:3:14 | for_loop2 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (const vector<int>)... | <none> |
|
||||
| | forstmt02.cpp:3:6:3:14 | for_loop2 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (const vector<int>)... | <none> |
|
||||
| | forstmt02.cpp:3:6:3:14 | for_loop2 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (reference dereference) | <none> |
|
||||
| | forstmt02.cpp:3:6:3:14 | for_loop2 | 0 | 1 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | (reference dereference) | <none> |
|
||||
| | forstmt02.cpp:3:6:3:14 | for_loop2 | 0 | 8 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | initializer for (__range) | 4: declaration |
|
||||
| | forstmt02.cpp:3:6:3:14 | for_loop2 | 0 | 12 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | initializer for (__begin) | 6: (__range) |
|
||||
| | forstmt02.cpp:3:6:3:14 | for_loop2 | 0 | 15 | file://:0:0:0:0 | file://:0:0:0:0 | file://:0:0:0:0 | initializer for (__end) | 6: (__end) |
|
||||
| forstmt | forstmt.h:2:8:2:8 | operator= | 2 | 1 | forstmt.h:2:8:2:8 | forstmt.h:2:8:2:8 | forstmt.h:2:8:2:8 | operator= | <none> |
|
||||
| forstmt | forstmt.h:2:8:2:8 | operator= | 2 | 1 | forstmt.h:2:8:2:8 | forstmt.h:2:8:2:8 | forstmt.h:2:8:2:8 | operator= | <none> |
|
||||
| forstmt | forstmt.h:3:12:3:12 | operator= | 3 | 1 | forstmt.h:3:12:3:12 | forstmt.h:3:12:3:12 | forstmt.h:3:12:3:12 | operator= | <none> |
|
||||
| forstmt | forstmt.h:3:12:3:12 | operator= | 3 | 1 | forstmt.h:3:12:3:12 | forstmt.h:3:12:3:12 | forstmt.h:3:12:3:12 | operator= | <none> |
|
||||
| forstmt | forstmt.h:4:19:4:28 | operator++ | 4 | 1 | forstmt.h:4:19:4:28 | forstmt.h:4:19:4:28 | forstmt.h:4:19:4:28 | operator++ | <none> |
|
||||
| forstmt | forstmt.h:4:19:4:28 | operator++ | 4 | 1 | forstmt.h:4:19:4:28 | forstmt.h:4:19:4:28 | forstmt.h:4:19:4:28 | operator++ | <none> |
|
||||
| forstmt | forstmt.h:5:12:5:20 | operator* | 5 | 1 | forstmt.h:5:12:5:20 | forstmt.h:5:12:5:20 | forstmt.h:5:12:5:20 | operator* | <none> |
|
||||
| forstmt | forstmt.h:5:12:5:20 | operator* | 5 | 1 | forstmt.h:5:12:5:20 | forstmt.h:5:12:5:20 | forstmt.h:5:12:5:20 | operator* | <none> |
|
||||
| forstmt | forstmt.h:7:14:7:23 | operator!= | 7 | 1 | forstmt.h:7:14:7:23 | forstmt.h:7:14:7:23 | forstmt.h:7:14:7:23 | operator!= | <none> |
|
||||
| forstmt | forstmt.h:7:14:7:23 | operator!= | 7 | 1 | forstmt.h:7:14:7:23 | forstmt.h:7:14:7:23 | forstmt.h:7:14:7:23 | operator!= | <none> |
|
||||
| forstmt | forstmt.h:10:14:10:18 | begin | 10 | 1 | forstmt.h:10:14:10:18 | forstmt.h:10:14:10:18 | forstmt.h:10:14:10:18 | begin | <none> |
|
||||
| forstmt | forstmt.h:10:14:10:18 | begin | 10 | 1 | forstmt.h:10:14:10:18 | forstmt.h:10:14:10:18 | forstmt.h:10:14:10:18 | begin | <none> |
|
||||
| forstmt | forstmt.h:11:14:11:16 | end | 11 | 1 | forstmt.h:11:14:11:16 | forstmt.h:11:14:11:16 | forstmt.h:11:14:11:16 | end | <none> |
|
||||
| forstmt | forstmt.h:11:14:11:16 | end | 11 | 1 | forstmt.h:11:14:11:16 | forstmt.h:11:14:11:16 | forstmt.h:11:14:11:16 | end | <none> |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 3 | 1 | forstmt01.cpp:3:39:9:1 | forstmt01.cpp:3:39:9:1 | forstmt01.cpp:3:39:9:1 | { ... } | 4: for(...:...) ... |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 4 | 2 | forstmt01.cpp:4:5:8:9 | forstmt01.cpp:4:5:8:9 | forstmt01.cpp:4:5:8:9 | for(...:...) ... | 4: declaration |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 4 | 3 | forstmt01.cpp:4:5:8:9 | forstmt01.cpp:4:5:8:9 | forstmt01.cpp:4:5:8:9 | declaration | 5: zs |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 4 | 6 | forstmt01.cpp:4:5:8:9 | forstmt01.cpp:4:5:8:9 | forstmt01.cpp:4:5:8:9 | declaration | 5: (__range) |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 4 | 22 | forstmt01.cpp:4:5:8:9 | forstmt01.cpp:4:5:8:9 | forstmt01.cpp:4:5:8:9 | declaration | 5: (__begin) |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 1 | forstmt01.cpp:5:18:5:19 | forstmt01.cpp:5:18:5:19 | forstmt01.cpp:5:18:5:19 | (reference to) | <none> |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 1 | forstmt01.cpp:5:18:6:9 | forstmt01.cpp:5:18:6:9 | forstmt01.cpp:5:18:6:9 | (reference dereference) | <none> |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 1 | forstmt01.cpp:5:18:6:9 | forstmt01.cpp:5:18:6:9 | forstmt01.cpp:5:18:6:9 | (reference dereference) | <none> |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 4 | forstmt01.cpp:5:18:5:19 | forstmt01.cpp:5:18:5:19 | forstmt01.cpp:5:18:5:19 | zs | 0: initializer for (__range) |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 7 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | (__range) | 5: call to begin |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 8 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | call to begin | 0: initializer for (__begin) |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 10 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | (__range) | 5: call to end |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 11 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | call to end | 0: initializer for (__end) |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 22 | forstmt01.cpp:5:14:5:14 | forstmt01.cpp:5:14:5:14 | forstmt01.cpp:5:14:5:14 | initializer for z | 7: { ... } |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 22 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | (__begin) | 5: call to operator!= |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 22 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | (__begin) | 5: call to operator* |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 22 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | (__begin) | 5: call to operator++ |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 22 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | (__end) | 5: (__begin) |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 22 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | call to operator!= | <false> 9: return ... |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 22 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | call to operator!= | <true> 4: declaration |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 22 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | call to operator* | 5: initializer for z |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 5 | 22 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | forstmt01.cpp:5:18:5:18 | call to operator++ | 5: (__end) |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 7 | 22 | forstmt01.cpp:7:9:8:9 | forstmt01.cpp:7:9:8:9 | forstmt01.cpp:7:9:8:9 | { ... } | 5: (__begin) |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 9 | 23 | forstmt01.cpp:9:1:9:1 | forstmt01.cpp:9:1:9:1 | forstmt01.cpp:9:1:9:1 | return ... | 9: for_loop1 |
|
||||
| forstmt01 | forstmt01.cpp:3:6:3:14 | for_loop1 | 9 | 24 | forstmt01.cpp:3:6:3:14 | forstmt01.cpp:3:6:3:14 | forstmt01.cpp:3:6:3:14 | for_loop1 | <none> |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 3 | 1 | forstmt02.cpp:3:39:10:1 | forstmt02.cpp:3:39:10:1 | forstmt02.cpp:3:39:10:1 | { ... } | 4: for(...:...) ... |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 4 | 2 | forstmt02.cpp:4:5:9:9 | forstmt02.cpp:4:5:9:9 | forstmt02.cpp:4:5:9:9 | for(...:...) ... | 5: declaration |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 4 | 6 | forstmt02.cpp:4:5:9:9 | forstmt02.cpp:4:5:9:9 | forstmt02.cpp:4:5:9:9 | declaration | 6: zs |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 4 | 9 | forstmt02.cpp:4:5:9:9 | forstmt02.cpp:4:5:9:9 | forstmt02.cpp:4:5:9:9 | declaration | 6: (__range) |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 4 | 25 | forstmt02.cpp:4:5:9:9 | forstmt02.cpp:4:5:9:9 | forstmt02.cpp:4:5:9:9 | declaration | 6: (__begin) |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 5 | 3 | forstmt02.cpp:5:9:5:18 | forstmt02.cpp:5:9:5:18 | forstmt02.cpp:5:9:5:18 | declaration | 5: initializer for y |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 5 | 4 | forstmt02.cpp:5:16:5:17 | forstmt02.cpp:5:16:5:17 | forstmt02.cpp:5:16:5:17 | initializer for y | 5: x |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 5 | 5 | forstmt02.cpp:5:17:5:17 | forstmt02.cpp:5:17:5:17 | forstmt02.cpp:5:17:5:17 | x | 4: declaration |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 1 | forstmt02.cpp:6:18:6:19 | forstmt02.cpp:6:18:6:19 | forstmt02.cpp:6:18:6:19 | (reference to) | <none> |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 1 | forstmt02.cpp:6:18:7:9 | forstmt02.cpp:6:18:7:9 | forstmt02.cpp:6:18:7:9 | (reference dereference) | <none> |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 1 | forstmt02.cpp:6:18:7:9 | forstmt02.cpp:6:18:7:9 | forstmt02.cpp:6:18:7:9 | (reference dereference) | <none> |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 7 | forstmt02.cpp:6:18:6:19 | forstmt02.cpp:6:18:6:19 | forstmt02.cpp:6:18:6:19 | zs | 0: initializer for (__range) |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 10 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | (__range) | 6: call to begin |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 11 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | call to begin | 0: initializer for (__begin) |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 13 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | (__range) | 6: call to end |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 14 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | call to end | 0: initializer for (__end) |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 25 | forstmt02.cpp:6:14:6:14 | forstmt02.cpp:6:14:6:14 | forstmt02.cpp:6:14:6:14 | initializer for z | 8: { ... } |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 25 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | (__begin) | 6: call to operator!= |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 25 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | (__begin) | 6: call to operator* |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 25 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | (__begin) | 6: call to operator++ |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 25 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | (__end) | 6: (__begin) |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 25 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | call to operator!= | <false> 10: return ... |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 25 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | call to operator!= | <true> 4: declaration |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 25 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | call to operator* | 6: initializer for z |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 6 | 25 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | forstmt02.cpp:6:18:6:18 | call to operator++ | 6: (__end) |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 8 | 25 | forstmt02.cpp:8:9:9:9 | forstmt02.cpp:8:9:9:9 | forstmt02.cpp:8:9:9:9 | { ... } | 6: (__begin) |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 10 | 26 | forstmt02.cpp:10:1:10:1 | forstmt02.cpp:10:1:10:1 | forstmt02.cpp:10:1:10:1 | return ... | 10: for_loop2 |
|
||||
| forstmt02 | forstmt02.cpp:3:6:3:14 | for_loop2 | 10 | 27 | forstmt02.cpp:3:6:3:14 | forstmt02.cpp:3:6:3:14 | forstmt02.cpp:3:6:3:14 | for_loop2 | <none> |
|
||||
28
cpp/ql/test/successor-tests/forstmt/rangebasedforstmt/cfg.ql
Normal file
28
cpp/ql/test/successor-tests/forstmt/rangebasedforstmt/cfg.ql
Normal file
@@ -0,0 +1,28 @@
|
||||
import cpp
|
||||
|
||||
int getCFLine(ControlFlowNode n) {
|
||||
if n instanceof Function
|
||||
then
|
||||
// Functions appear at the end of the control flow, so we get
|
||||
// nicer results if we take the last position in the function,
|
||||
// rather than the function's position (which is the start).
|
||||
result = max(ControlFlowNode c | c.getControlFlowScope() = n | c.getLocation().getStartLine())
|
||||
else result = n.getLocation().getStartLine()
|
||||
}
|
||||
|
||||
string getASuccessorOrNone(ControlFlowNode n) {
|
||||
if exists(n.getASuccessor())
|
||||
then
|
||||
exists(ControlFlowNode s, string trueSucc, string falseSucc |
|
||||
s = n.getASuccessor() and
|
||||
(if s = n.getATrueSuccessor() then trueSucc = "<true> " else trueSucc = "") and
|
||||
(if s = n.getAFalseSuccessor() then falseSucc = "<false> " else falseSucc = "") and
|
||||
result = trueSucc + falseSucc + getCFLine(s) + ": " + s.toString()
|
||||
)
|
||||
else result = "<none>"
|
||||
}
|
||||
|
||||
from ControlFlowNode n
|
||||
select n.getLocation().getFile().getShortName(), n.getControlFlowScope(), getCFLine(n),
|
||||
count(n.getAPredecessor*()), // This helps order things sensibly
|
||||
n.getLocation(), n, getASuccessorOrNone(n)
|
||||
@@ -0,0 +1,12 @@
|
||||
template<typename T>
|
||||
struct vector {
|
||||
struct iterator {
|
||||
iterator& operator++();
|
||||
T& operator*() const;
|
||||
|
||||
bool operator!=(iterator right) const;
|
||||
};
|
||||
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "forstmt.h"
|
||||
|
||||
void for_loop1(int x, vector<int> zs) {
|
||||
for (
|
||||
auto z : zs
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "forstmt.h"
|
||||
|
||||
void for_loop2(int x, vector<int> zs) {
|
||||
for (
|
||||
int y = x;
|
||||
auto z : zs
|
||||
)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// semmle-extractor-options: -std=c++20
|
||||
@@ -2,5 +2,3 @@
|
||||
| no_dynamic_init.cpp:5:12:5:35 | no_dynamic_init.cpp:5:12:5:35 | no_dynamic_init.cpp:5:12:5:35 | Goodbye cruel world.\n |
|
||||
| no_dynamic_init.cpp:5:12:5:35 | no_dynamic_init.cpp:5:12:5:35 | no_dynamic_init.cpp:5:12:5:35 | array to pointer conversion |
|
||||
| no_dynamic_init.cpp:11:10:11:10 | no_dynamic_init.cpp:11:10:11:10 | no_dynamic_init.cpp:11:10:11:10 | 0 |
|
||||
| no_dynamic_init.cpp:12:1:12:1 | no_dynamic_init.cpp:12:1:12:1 | no_dynamic_init.cpp:12:1:12:1 | call to ~Magic |
|
||||
| no_dynamic_init.cpp:12:1:12:1 | no_dynamic_init.cpp:12:1:12:1 | no_dynamic_init.cpp:12:1:12:1 | m |
|
||||
|
||||
Reference in New Issue
Block a user