Merge branch 'main' into rdmarsh2/cpp/ir-synthetic-destructors

Fixes conflicts in C++ IR tests and Stmt.qll
This commit is contained in:
Robert Marsh
2024-02-21 21:34:49 +00:00
493 changed files with 21529 additions and 6387 deletions

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.12.5
lastReleaseVersion: 0.12.6

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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)
)
}
/**

View File

@@ -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 =

View File

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

View File

@@ -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,

View File

@@ -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`.

View File

@@ -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

View File

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

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,4 @@
description: Support C++20 range-based for initializers
compatibility: partial
exprparents.rel: run exprparents.qlo
stmtparents.rel: run stmtparents.qlo

View File

@@ -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

View File

@@ -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."

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added dataflow models for the `gettext` function variants.

View 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.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 0.9.4
lastReleaseVersion: 0.9.5

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

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

View File

@@ -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. |

View File

@@ -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

View File

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

View File

@@ -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. |

View File

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

View File

@@ -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> |

View 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)

View File

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

View File

@@ -0,0 +1,9 @@
#include "forstmt.h"
void for_loop1(int x, vector<int> zs) {
for (
auto z : zs
)
{
}
}

View File

@@ -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

View File

@@ -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 |