mirror of
https://github.com/github/codeql.git
synced 2026-05-26 17:11:24 +02:00
Compare commits
2 Commits
post-relea
...
criemen/cs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a1483d8b5 | ||
|
|
1223907b57 |
@@ -8,8 +8,6 @@
|
||||
/swift/ @github/codeql-swift
|
||||
/misc/codegen/ @github/codeql-swift
|
||||
/java/kotlin-extractor/ @github/codeql-kotlin
|
||||
/java/ql/test-kotlin1/ @github/codeql-kotlin
|
||||
/java/ql/test-kotlin2/ @github/codeql-kotlin
|
||||
|
||||
# ML-powered queries
|
||||
/javascript/ql/experimental/adaptivethreatmodeling/ @github/codeql-ml-powered-queries-reviewers
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
provide:
|
||||
- "*/ql/src/qlpack.yml"
|
||||
- "*/ql/lib/qlpack.yml"
|
||||
- "*/ql/test*/qlpack.yml"
|
||||
- "*/ql/test/qlpack.yml"
|
||||
- "*/ql/examples/qlpack.yml"
|
||||
- "*/ql/consistency-queries/qlpack.yml"
|
||||
- "*/ql/automodel/src/qlpack.yml"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
description: Expose whether a function was prototyped or not
|
||||
compatibility: backwards
|
||||
function_prototyped.rel: delete
|
||||
@@ -1,27 +1,3 @@
|
||||
## 0.12.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### New Features
|
||||
|
||||
* Added an `isPrototyped` predicate to `Function` that holds when the function has a prototype.
|
||||
|
||||
## 0.12.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The expressions `AssignPointerAddExpr` and `AssignPointerSubExpr` are no longer subtypes of `AssignBitwiseOperation`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Returning stack-allocated memory" (`cpp/return-stack-allocated-memory`) query now also detects returning stack-allocated memory allocated by calls to `alloca`, `strdupa`, and `strndupa`.
|
||||
* Added models for `strlcpy` and `strlcat`.
|
||||
* Added models for the `sprintf` variants from the `StrSafe.h` header.
|
||||
* Added SQL API models for `ODBC`.
|
||||
* Added taint models for `realloc` and related functions.
|
||||
|
||||
## 0.11.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
4
cpp/ql/lib/change-notes/2023-10-30-realloc-flow.md
Normal file
4
cpp/ql/lib/change-notes/2023-10-30-realloc-flow.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added taint models for `realloc` and related functions.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: breaking
|
||||
---
|
||||
* The expressions `AssignPointerAddExpr` and `AssignPointerSubExpr` are no longer subtypes of `AssignBitwiseOperation`.
|
||||
4
cpp/ql/lib/change-notes/2023-11-08-strsafe-models.md
Normal file
4
cpp/ql/lib/change-notes/2023-11-08-strsafe-models.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added models for the `sprintf` variants from the `StrSafe.h` header.
|
||||
@@ -1,13 +0,0 @@
|
||||
## 0.12.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The expressions `AssignPointerAddExpr` and `AssignPointerSubExpr` are no longer subtypes of `AssignBitwiseOperation`.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The "Returning stack-allocated memory" (`cpp/return-stack-allocated-memory`) query now also detects returning stack-allocated memory allocated by calls to `alloca`, `strdupa`, and `strndupa`.
|
||||
* Added models for `strlcpy` and `strlcat`.
|
||||
* Added models for the `sprintf` variants from the `StrSafe.h` header.
|
||||
* Added SQL API models for `ODBC`.
|
||||
* Added taint models for `realloc` and related functions.
|
||||
@@ -1,5 +0,0 @@
|
||||
## 0.12.1
|
||||
|
||||
### New Features
|
||||
|
||||
* Added an `isPrototyped` predicate to `Function` that holds when the function has a prototype.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.12.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.12.2
|
||||
lastReleaseVersion: 0.11.0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.12.3-dev
|
||||
version: 0.11.1-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -112,16 +112,6 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
*/
|
||||
predicate isDeleted() { function_deleted(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this function has a prototyped interface.
|
||||
*
|
||||
* Functions generally have a prototyped interface, unless they are
|
||||
* K&R-style functions either without any forward function declaration,
|
||||
* or with all the forward declarations omitting the parameters of the
|
||||
* function.
|
||||
*/
|
||||
predicate isPrototyped() { function_prototyped(underlyingElement(this)) }
|
||||
|
||||
/**
|
||||
* Holds if this function is explicitly defaulted with the `= default`
|
||||
* specifier.
|
||||
|
||||
@@ -30,6 +30,11 @@ class GuardCondition extends Expr {
|
||||
or
|
||||
// no binary operators in the IR
|
||||
this.(BinaryLogicalOperation).getAnOperand() instanceof GuardCondition
|
||||
or
|
||||
// the IR short-circuits if(!x)
|
||||
// don't produce a guard condition for `y = !x` and other non-short-circuited cases
|
||||
not exists(Instruction inst | this.getFullyConverted() = inst.getAst()) and
|
||||
exists(IRGuardCondition ir | this.(NotExpr).getOperand() = ir.getAst())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,6 +140,39 @@ private class GuardConditionFromBinaryLogicalOperator extends GuardCondition {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `!` operator in the AST that guards one or more basic blocks, and does not have a corresponding
|
||||
* IR instruction.
|
||||
*/
|
||||
private class GuardConditionFromShortCircuitNot extends GuardCondition, NotExpr {
|
||||
GuardConditionFromShortCircuitNot() {
|
||||
not exists(Instruction inst | this.getFullyConverted() = inst.getAst()) and
|
||||
exists(IRGuardCondition ir | this.getOperand() = ir.getAst())
|
||||
}
|
||||
|
||||
override predicate controls(BasicBlock controlled, boolean testIsTrue) {
|
||||
this.getOperand().(GuardCondition).controls(controlled, testIsTrue.booleanNot())
|
||||
}
|
||||
|
||||
override predicate comparesLt(Expr left, Expr right, int k, boolean isLessThan, boolean testIsTrue) {
|
||||
this.getOperand()
|
||||
.(GuardCondition)
|
||||
.comparesLt(left, right, k, isLessThan, testIsTrue.booleanNot())
|
||||
}
|
||||
|
||||
override predicate ensuresLt(Expr left, Expr right, int k, BasicBlock block, boolean isLessThan) {
|
||||
this.getOperand().(GuardCondition).ensuresLt(left, right, k, block, isLessThan.booleanNot())
|
||||
}
|
||||
|
||||
override predicate comparesEq(Expr left, Expr right, int k, boolean areEqual, boolean testIsTrue) {
|
||||
this.getOperand().(GuardCondition).comparesEq(left, right, k, areEqual, testIsTrue.booleanNot())
|
||||
}
|
||||
|
||||
override predicate ensuresEq(Expr left, Expr right, int k, BasicBlock block, boolean areEqual) {
|
||||
this.getOperand().(GuardCondition).ensuresEq(left, right, k, block, areEqual.booleanNot())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Boolean condition in the AST that guards one or more basic blocks and has a corresponding IR
|
||||
* instruction.
|
||||
|
||||
@@ -81,14 +81,6 @@ class Node0Impl extends TIRDataFlowNode0 {
|
||||
/** Gets the operands corresponding to this node, if any. */
|
||||
Operand asOperand() { result = this.(OperandNode0).getOperand() }
|
||||
|
||||
/** Gets the location of this node. */
|
||||
final Location getLocation() { result = this.getLocationImpl() }
|
||||
|
||||
/** INTERNAL: Do not use. */
|
||||
Location getLocationImpl() {
|
||||
none() // overridden by subclasses
|
||||
}
|
||||
|
||||
/** INTERNAL: Do not use. */
|
||||
string toStringImpl() {
|
||||
none() // overridden by subclasses
|
||||
@@ -139,15 +131,9 @@ abstract class InstructionNode0 extends Node0Impl {
|
||||
override DataFlowType getType() { result = getInstructionType(instr, _) }
|
||||
|
||||
override string toStringImpl() {
|
||||
if instr.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = instr.getAst().toString()
|
||||
}
|
||||
|
||||
override Location getLocationImpl() {
|
||||
if exists(instr.getAst().getLocation())
|
||||
then result = instr.getAst().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
// This predicate is overridden in subclasses. This default implementation
|
||||
// does not use `Instruction.toString` because that's expensive to compute.
|
||||
result = instr.getOpcode().toString()
|
||||
}
|
||||
|
||||
final override predicate isGLValue() { exists(getInstructionType(instr, true)) }
|
||||
@@ -187,17 +173,7 @@ abstract class OperandNode0 extends Node0Impl {
|
||||
|
||||
override DataFlowType getType() { result = getOperandType(op, _) }
|
||||
|
||||
override string toStringImpl() {
|
||||
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = op.getDef().getAst().toString()
|
||||
}
|
||||
|
||||
override Location getLocationImpl() {
|
||||
if exists(op.getDef().getAst().getLocation())
|
||||
then result = op.getDef().getAst().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
override string toStringImpl() { result = op.toString() }
|
||||
|
||||
final override predicate isGLValue() { exists(getOperandType(op, true)) }
|
||||
}
|
||||
@@ -645,24 +621,6 @@ class GlobalLikeVariable extends Variable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the smallest indirection for the type `t`.
|
||||
*
|
||||
* For most types this is `1`, but for `ArrayType`s (which are allocated on
|
||||
* the stack) this is `0`
|
||||
*/
|
||||
int getMinIndirectionsForType(Type t) {
|
||||
if t.getUnspecifiedType() instanceof Cpp::ArrayType then result = 0 else result = 1
|
||||
}
|
||||
|
||||
private int getMinIndirectionForGlobalUse(Ssa::GlobalUse use) {
|
||||
result = getMinIndirectionsForType(use.getUnspecifiedType())
|
||||
}
|
||||
|
||||
private int getMinIndirectionForGlobalDef(Ssa::GlobalDef def) {
|
||||
result = getMinIndirectionsForType(def.getUnspecifiedType())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` in a way that loses the
|
||||
* calling context. For example, this would happen with flow through a
|
||||
@@ -674,7 +632,7 @@ predicate jumpStep(Node n1, Node n2) {
|
||||
v = globalUse.getVariable() and
|
||||
n1.(FinalGlobalValue).getGlobalUse() = globalUse
|
||||
|
|
||||
globalUse.getIndirection() = getMinIndirectionForGlobalUse(globalUse) and
|
||||
globalUse.getIndirection() = 1 and
|
||||
v = n2.asVariable()
|
||||
or
|
||||
v = n2.asIndirectVariable(globalUse.getIndirection())
|
||||
@@ -684,7 +642,7 @@ predicate jumpStep(Node n1, Node n2) {
|
||||
v = globalDef.getVariable() and
|
||||
n2.(InitialGlobalValue).getGlobalDef() = globalDef
|
||||
|
|
||||
globalDef.getIndirection() = getMinIndirectionForGlobalDef(globalDef) and
|
||||
globalDef.getIndirection() = 1 and
|
||||
v = n1.asVariable()
|
||||
or
|
||||
v = n1.asIndirectVariable(globalDef.getIndirection())
|
||||
|
||||
@@ -34,8 +34,7 @@ cached
|
||||
private newtype TIRDataFlowNode =
|
||||
TNode0(Node0Impl node) { DataFlowImplCommon::forceCachingInSameStage() } or
|
||||
TVariableNode(Variable var, int indirectionIndex) {
|
||||
indirectionIndex =
|
||||
[getMinIndirectionsForType(var.getUnspecifiedType()) .. Ssa::getMaxIndirectionsForType(var.getUnspecifiedType())]
|
||||
indirectionIndex = [1 .. Ssa::getMaxIndirectionsForType(var.getUnspecifiedType())]
|
||||
} or
|
||||
TPostFieldUpdateNode(FieldAddress operand, int indirectionIndex) {
|
||||
indirectionIndex =
|
||||
@@ -347,9 +346,7 @@ class Node extends TIRDataFlowNode {
|
||||
* Gets the variable corresponding to this node, if any. This can be used for
|
||||
* modeling flow in and out of global variables.
|
||||
*/
|
||||
Variable asVariable() {
|
||||
this = TVariableNode(result, getMinIndirectionsForType(result.getUnspecifiedType()))
|
||||
}
|
||||
Variable asVariable() { this = TVariableNode(result, 1) }
|
||||
|
||||
/**
|
||||
* Gets the `indirectionIndex`'th indirection of this node's underlying variable, if any.
|
||||
@@ -357,7 +354,7 @@ class Node extends TIRDataFlowNode {
|
||||
* This can be used for modeling flow in and out of global variables.
|
||||
*/
|
||||
Variable asIndirectVariable(int indirectionIndex) {
|
||||
indirectionIndex > getMinIndirectionsForType(result.getUnspecifiedType()) and
|
||||
indirectionIndex > 1 and
|
||||
this = TVariableNode(result, indirectionIndex)
|
||||
}
|
||||
|
||||
@@ -435,10 +432,6 @@ private class Node0 extends Node, TNode0 {
|
||||
|
||||
override Declaration getFunction() { result = node.getFunction() }
|
||||
|
||||
override Location getLocationImpl() { result = node.getLocation() }
|
||||
|
||||
override string toStringImpl() { result = node.toString() }
|
||||
|
||||
override DataFlowType getType() { result = node.getType() }
|
||||
|
||||
override predicate isGLValue() { node.isGLValue() }
|
||||
@@ -455,6 +448,18 @@ class InstructionNode extends Node0 {
|
||||
|
||||
/** Gets the instruction corresponding to this node. */
|
||||
Instruction getInstruction() { result = instr }
|
||||
|
||||
override Location getLocationImpl() {
|
||||
if exists(instr.getAst().getLocation())
|
||||
then result = instr.getAst().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
if instr.(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = instr.getAst().toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,6 +473,18 @@ class OperandNode extends Node, Node0 {
|
||||
|
||||
/** Gets the operand corresponding to this node. */
|
||||
Operand getOperand() { result = op }
|
||||
|
||||
override Location getLocationImpl() {
|
||||
if exists(op.getDef().getAst().getLocation())
|
||||
then result = op.getDef().getAst().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
if op.getDef().(InitializeParameterInstruction).getIRVariable() instanceof IRThisVariable
|
||||
then result = "this"
|
||||
else result = op.getDef().getAst().toString()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1276,90 +1293,31 @@ abstract private class IndirectExprNodeBase extends Node {
|
||||
}
|
||||
}
|
||||
|
||||
/** A signature for converting an indirect node to an expression. */
|
||||
private signature module IndirectNodeToIndirectExprSig {
|
||||
/** The indirect node class to be converted to an expression */
|
||||
class IndirectNode;
|
||||
|
||||
/**
|
||||
* Holds if the indirect expression at indirection index `indirectionIndex`
|
||||
* of `node` is `e`. The integer `n` specifies how many conversions has been
|
||||
* applied to `node`.
|
||||
*/
|
||||
predicate indirectNodeHasIndirectExpr(IndirectNode node, Expr e, int n, int indirectionIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* A module that implements the logic for deciding whether an indirect node
|
||||
* should be an `IndirectExprNode`.
|
||||
*/
|
||||
private module IndirectNodeToIndirectExpr<IndirectNodeToIndirectExprSig Sig> {
|
||||
import Sig
|
||||
|
||||
/**
|
||||
* This predicate shifts the indirection index by one when `conv` is a
|
||||
* `ReferenceDereferenceExpr`.
|
||||
*
|
||||
* This is necessary because `ReferenceDereferenceExpr` is a conversion
|
||||
* in the AST, but appears as a `LoadInstruction` in the IR.
|
||||
*/
|
||||
bindingset[e, indirectionIndex]
|
||||
private predicate adjustForReference(
|
||||
Expr e, int indirectionIndex, Expr conv, int adjustedIndirectionIndex
|
||||
) {
|
||||
conv.(ReferenceDereferenceExpr).getExpr() = e and
|
||||
adjustedIndirectionIndex = indirectionIndex - 1
|
||||
or
|
||||
not conv instanceof ReferenceDereferenceExpr and
|
||||
conv = e and
|
||||
adjustedIndirectionIndex = indirectionIndex
|
||||
}
|
||||
|
||||
/** Holds if `node` should be an `IndirectExprNode`. */
|
||||
predicate charpred(IndirectNode node) {
|
||||
exists(Expr e, int n, int indirectionIndex |
|
||||
indirectNodeHasIndirectExpr(node, e, n, indirectionIndex) and
|
||||
not exists(Expr conv, int adjustedIndirectionIndex |
|
||||
adjustForReference(e, indirectionIndex, conv, adjustedIndirectionIndex) and
|
||||
indirectNodeHasIndirectExpr(_, conv, n + 1, adjustedIndirectionIndex)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module IndirectOperandIndirectExprNodeImpl implements IndirectNodeToIndirectExprSig {
|
||||
class IndirectNode = IndirectOperand;
|
||||
|
||||
predicate indirectNodeHasIndirectExpr = indirectExprNodeShouldBeIndirectOperand/4;
|
||||
}
|
||||
|
||||
module IndirectOperandToIndirectExpr =
|
||||
IndirectNodeToIndirectExpr<IndirectOperandIndirectExprNodeImpl>;
|
||||
|
||||
private class IndirectOperandIndirectExprNode extends IndirectExprNodeBase instanceof IndirectOperand
|
||||
{
|
||||
IndirectOperandIndirectExprNode() { IndirectOperandToIndirectExpr::charpred(this) }
|
||||
IndirectOperandIndirectExprNode() {
|
||||
exists(Expr e, int n, int indirectionIndex |
|
||||
indirectExprNodeShouldBeIndirectOperand(this, e, n, indirectionIndex) and
|
||||
not indirectExprNodeShouldBeIndirectOperand(_, e, n + 1, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr(int n, int index) {
|
||||
IndirectOperandToIndirectExpr::indirectNodeHasIndirectExpr(this, result, n, index)
|
||||
indirectExprNodeShouldBeIndirectOperand(this, result, n, index)
|
||||
}
|
||||
}
|
||||
|
||||
private module IndirectInstructionIndirectExprNodeImpl implements IndirectNodeToIndirectExprSig {
|
||||
class IndirectNode = IndirectInstruction;
|
||||
|
||||
predicate indirectNodeHasIndirectExpr = indirectExprNodeShouldBeIndirectInstruction/4;
|
||||
}
|
||||
|
||||
module IndirectInstructionToIndirectExpr =
|
||||
IndirectNodeToIndirectExpr<IndirectInstructionIndirectExprNodeImpl>;
|
||||
|
||||
private class IndirectInstructionIndirectExprNode extends IndirectExprNodeBase instanceof IndirectInstruction
|
||||
{
|
||||
IndirectInstructionIndirectExprNode() { IndirectInstructionToIndirectExpr::charpred(this) }
|
||||
IndirectInstructionIndirectExprNode() {
|
||||
exists(Expr e, int n, int indirectionIndex |
|
||||
indirectExprNodeShouldBeIndirectInstruction(this, e, n, indirectionIndex) and
|
||||
not indirectExprNodeShouldBeIndirectInstruction(_, e, n + 1, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
final override Expr getConvertedExpr(int n, int index) {
|
||||
IndirectInstructionToIndirectExpr::indirectNodeHasIndirectExpr(this, result, n, index)
|
||||
indirectExprNodeShouldBeIndirectInstruction(this, result, n, index)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,9 +59,6 @@ private module SourceVariables {
|
||||
then result = base.getType()
|
||||
else result = getTypeImpl(base.getType(), ind - 1)
|
||||
}
|
||||
|
||||
/** Gets the location of this variable. */
|
||||
Location getLocation() { result = this.getBaseVariable().getLocation() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,7 +869,7 @@ private predicate sourceVariableIsGlobal(
|
||||
)
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
import InputSigCommon
|
||||
import SourceVariables
|
||||
|
||||
@@ -1095,7 +1092,7 @@ class Def extends DefOrUse {
|
||||
predicate isCertain() { defOrUse.isCertain() }
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<Location, SsaInput>;
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
|
||||
class PhiNode extends SsaImpl::DefinitionExt {
|
||||
PhiNode() {
|
||||
|
||||
@@ -377,9 +377,6 @@ abstract private class AbstractBaseSourceVariable extends TBaseSourceVariable {
|
||||
/** Gets a textual representation of this element. */
|
||||
abstract string toString();
|
||||
|
||||
/** Gets the location of this variable. */
|
||||
abstract Location getLocation();
|
||||
|
||||
/** Gets the type of this base source variable. */
|
||||
final DataFlowType getType() { this.getLanguageType().hasUnspecifiedType(result, _) }
|
||||
|
||||
@@ -398,8 +395,6 @@ class BaseIRVariable extends AbstractBaseSourceVariable, TBaseIRVariable {
|
||||
|
||||
override string toString() { result = var.toString() }
|
||||
|
||||
override Location getLocation() { result = var.getLocation() }
|
||||
|
||||
override CppType getLanguageType() { result = var.getLanguageType() }
|
||||
}
|
||||
|
||||
@@ -412,8 +407,6 @@ class BaseCallVariable extends AbstractBaseSourceVariable, TBaseCallVariable {
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
|
||||
override CppType getLanguageType() { result = getResultLanguageType(call) }
|
||||
}
|
||||
|
||||
@@ -879,7 +872,7 @@ private module Cached {
|
||||
upper = countIndirectionsForCppType(type) and
|
||||
ind = ind0 + [lower .. upper] and
|
||||
indirectionIndex = ind - (ind0 + lower) and
|
||||
lower = getMinIndirectionsForType(any(Type t | type.hasUnspecifiedType(t, _)))
|
||||
(if type.hasType(any(Cpp::ArrayType arrayType), true) then lower = 0 else lower = 1)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -72,16 +72,6 @@ private predicate operandToInstructionTaintStep(Operand opFrom, Instruction inst
|
||||
or
|
||||
instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union
|
||||
)
|
||||
or
|
||||
// Taint from int to boolean casts. This ensures that we have flow to `!x` in:
|
||||
// ```cpp
|
||||
// x = integer_source();
|
||||
// if(!x) { ... }
|
||||
// ```
|
||||
exists(Operand zero |
|
||||
zero.getDef().(ConstantValueInstruction).getValue() = "0" and
|
||||
instrTo.(CompareNEInstruction).hasOperands(opFrom, zero)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -229,7 +229,7 @@ private class FinalParameterUse extends UseImpl, TFinalParameterUse {
|
||||
override predicate isCertain() { any() }
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
import InputSigCommon
|
||||
import SourceVariables
|
||||
|
||||
@@ -335,7 +335,7 @@ class Def extends DefOrUse {
|
||||
predicate isIteratorDef() { defOrUse instanceof IteratorDef }
|
||||
}
|
||||
|
||||
private module SsaImpl = SsaImplCommon::Make<Location, SsaInput>;
|
||||
private module SsaImpl = SsaImplCommon::Make<SsaInput>;
|
||||
|
||||
class PhiNode extends SsaImpl::DefinitionExt {
|
||||
PhiNode() {
|
||||
|
||||
@@ -12,9 +12,6 @@ int getConstantValue(Instruction instr) {
|
||||
or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue())
|
||||
or
|
||||
getConstantValue(instr.(LogicalNotInstruction).getUnary()) != 0 and
|
||||
result = 0
|
||||
or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = unique(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
|
||||
@@ -29,25 +26,28 @@ private predicate binaryInstructionOperands(BinaryInstruction instr, int left, i
|
||||
|
||||
pragma[noinline]
|
||||
private int getBinaryInstructionValue(BinaryInstruction instr) {
|
||||
exists(int left, int right | binaryInstructionOperands(instr, left, right) |
|
||||
instr instanceof AddInstruction and result = add(left, right)
|
||||
or
|
||||
instr instanceof SubInstruction and result = sub(left, right)
|
||||
or
|
||||
instr instanceof MulInstruction and result = mul(left, right)
|
||||
or
|
||||
instr instanceof DivInstruction and result = div(left, right)
|
||||
or
|
||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||
or
|
||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||
or
|
||||
instr instanceof CompareLTInstruction and result = compareLT(left, right)
|
||||
or
|
||||
instr instanceof CompareGTInstruction and result = compareGT(left, right)
|
||||
or
|
||||
instr instanceof CompareLEInstruction and result = compareLE(left, right)
|
||||
or
|
||||
instr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
exists(int left, int right |
|
||||
binaryInstructionOperands(instr, left, right) and
|
||||
(
|
||||
instr instanceof AddInstruction and result = add(left, right)
|
||||
or
|
||||
instr instanceof SubInstruction and result = sub(left, right)
|
||||
or
|
||||
instr instanceof MulInstruction and result = mul(left, right)
|
||||
or
|
||||
instr instanceof DivInstruction and result = div(left, right)
|
||||
or
|
||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||
or
|
||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||
or
|
||||
instr instanceof CompareLTInstruction and result = compareLT(left, right)
|
||||
or
|
||||
instr instanceof CompareGTInstruction and result = compareGT(left, right)
|
||||
or
|
||||
instr instanceof CompareLEInstruction and result = compareLE(left, right)
|
||||
or
|
||||
instr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -23,8 +23,9 @@ private module Internal {
|
||||
newtype TOperand =
|
||||
// RAW
|
||||
TRegisterOperand(TRawInstruction useInstr, RegisterOperandTag tag, TRawInstruction defInstr) {
|
||||
defInstr = unique( | | RawConstruction::getRegisterOperandDefinition(useInstr, tag)) and
|
||||
not RawConstruction::isInCycle(useInstr)
|
||||
defInstr = RawConstruction::getRegisterOperandDefinition(useInstr, tag) and
|
||||
not RawConstruction::isInCycle(useInstr) and
|
||||
strictcount(RawConstruction::getRegisterOperandDefinition(useInstr, tag)) = 1
|
||||
} or
|
||||
// Placeholder for Phi and Chi operands in stages that don't have the corresponding instructions
|
||||
TNoOperand() { none() } or
|
||||
|
||||
@@ -12,9 +12,6 @@ int getConstantValue(Instruction instr) {
|
||||
or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue())
|
||||
or
|
||||
getConstantValue(instr.(LogicalNotInstruction).getUnary()) != 0 and
|
||||
result = 0
|
||||
or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = unique(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
|
||||
@@ -29,25 +26,28 @@ private predicate binaryInstructionOperands(BinaryInstruction instr, int left, i
|
||||
|
||||
pragma[noinline]
|
||||
private int getBinaryInstructionValue(BinaryInstruction instr) {
|
||||
exists(int left, int right | binaryInstructionOperands(instr, left, right) |
|
||||
instr instanceof AddInstruction and result = add(left, right)
|
||||
or
|
||||
instr instanceof SubInstruction and result = sub(left, right)
|
||||
or
|
||||
instr instanceof MulInstruction and result = mul(left, right)
|
||||
or
|
||||
instr instanceof DivInstruction and result = div(left, right)
|
||||
or
|
||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||
or
|
||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||
or
|
||||
instr instanceof CompareLTInstruction and result = compareLT(left, right)
|
||||
or
|
||||
instr instanceof CompareGTInstruction and result = compareGT(left, right)
|
||||
or
|
||||
instr instanceof CompareLEInstruction and result = compareLE(left, right)
|
||||
or
|
||||
instr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
exists(int left, int right |
|
||||
binaryInstructionOperands(instr, left, right) and
|
||||
(
|
||||
instr instanceof AddInstruction and result = add(left, right)
|
||||
or
|
||||
instr instanceof SubInstruction and result = sub(left, right)
|
||||
or
|
||||
instr instanceof MulInstruction and result = mul(left, right)
|
||||
or
|
||||
instr instanceof DivInstruction and result = div(left, right)
|
||||
or
|
||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||
or
|
||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||
or
|
||||
instr instanceof CompareLTInstruction and result = compareLT(left, right)
|
||||
or
|
||||
instr instanceof CompareGTInstruction and result = compareGT(left, right)
|
||||
or
|
||||
instr instanceof CompareLEInstruction and result = compareLE(left, right)
|
||||
or
|
||||
instr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -77,6 +77,24 @@ class TranslatedParenthesisCondition extends TranslatedFlexibleCondition {
|
||||
}
|
||||
}
|
||||
|
||||
class TranslatedNotCondition extends TranslatedFlexibleCondition {
|
||||
override NotExpr expr;
|
||||
|
||||
override Instruction getChildTrueSuccessor(TranslatedCondition child) {
|
||||
child = this.getOperand() and
|
||||
result = this.getConditionContext().getChildFalseSuccessor(this)
|
||||
}
|
||||
|
||||
override Instruction getChildFalseSuccessor(TranslatedCondition child) {
|
||||
child = this.getOperand() and
|
||||
result = this.getConditionContext().getChildTrueSuccessor(this)
|
||||
}
|
||||
|
||||
override TranslatedCondition getOperand() {
|
||||
result = getTranslatedCondition(expr.getOperand().getFullyConverted())
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TranslatedNativeCondition extends TranslatedCondition, TTranslatedNativeCondition {
|
||||
TranslatedNativeCondition() { this = TTranslatedNativeCondition(expr) }
|
||||
|
||||
|
||||
@@ -190,7 +190,10 @@ private predicate isNativeCondition(Expr expr) {
|
||||
* depending on context.
|
||||
*/
|
||||
private predicate isFlexibleCondition(Expr expr) {
|
||||
expr instanceof ParenthesisExpr and
|
||||
(
|
||||
expr instanceof ParenthesisExpr or
|
||||
expr instanceof NotExpr
|
||||
) and
|
||||
usedAsCondition(expr) and
|
||||
not isIRConstant(expr)
|
||||
}
|
||||
@@ -215,6 +218,11 @@ private predicate usedAsCondition(Expr expr) {
|
||||
condExpr.getCondition().getFullyConverted() = expr and not condExpr.isTwoOperand()
|
||||
)
|
||||
or
|
||||
exists(NotExpr notExpr |
|
||||
notExpr.getOperand().getFullyConverted() = expr and
|
||||
usedAsCondition(notExpr)
|
||||
)
|
||||
or
|
||||
exists(ParenthesisExpr paren |
|
||||
paren.getExpr() = expr and
|
||||
usedAsCondition(paren)
|
||||
|
||||
@@ -12,9 +12,6 @@ int getConstantValue(Instruction instr) {
|
||||
or
|
||||
result = getConstantValue(instr.(CopyInstruction).getSourceValue())
|
||||
or
|
||||
getConstantValue(instr.(LogicalNotInstruction).getUnary()) != 0 and
|
||||
result = 0
|
||||
or
|
||||
exists(PhiInstruction phi |
|
||||
phi = instr and
|
||||
result = unique(Operand op | op = phi.getAnInputOperand() | getConstantValue(op.getDef()))
|
||||
@@ -29,25 +26,28 @@ private predicate binaryInstructionOperands(BinaryInstruction instr, int left, i
|
||||
|
||||
pragma[noinline]
|
||||
private int getBinaryInstructionValue(BinaryInstruction instr) {
|
||||
exists(int left, int right | binaryInstructionOperands(instr, left, right) |
|
||||
instr instanceof AddInstruction and result = add(left, right)
|
||||
or
|
||||
instr instanceof SubInstruction and result = sub(left, right)
|
||||
or
|
||||
instr instanceof MulInstruction and result = mul(left, right)
|
||||
or
|
||||
instr instanceof DivInstruction and result = div(left, right)
|
||||
or
|
||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||
or
|
||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||
or
|
||||
instr instanceof CompareLTInstruction and result = compareLT(left, right)
|
||||
or
|
||||
instr instanceof CompareGTInstruction and result = compareGT(left, right)
|
||||
or
|
||||
instr instanceof CompareLEInstruction and result = compareLE(left, right)
|
||||
or
|
||||
instr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
exists(int left, int right |
|
||||
binaryInstructionOperands(instr, left, right) and
|
||||
(
|
||||
instr instanceof AddInstruction and result = add(left, right)
|
||||
or
|
||||
instr instanceof SubInstruction and result = sub(left, right)
|
||||
or
|
||||
instr instanceof MulInstruction and result = mul(left, right)
|
||||
or
|
||||
instr instanceof DivInstruction and result = div(left, right)
|
||||
or
|
||||
instr instanceof CompareEQInstruction and result = compareEQ(left, right)
|
||||
or
|
||||
instr instanceof CompareNEInstruction and result = compareNE(left, right)
|
||||
or
|
||||
instr instanceof CompareLTInstruction and result = compareLT(left, right)
|
||||
or
|
||||
instr instanceof CompareGTInstruction and result = compareGT(left, right)
|
||||
or
|
||||
instr instanceof CompareLEInstruction and result = compareLE(left, right)
|
||||
or
|
||||
instr instanceof CompareGEInstruction and result = compareGE(left, right)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,10 @@ class Getenv extends LocalFlowSourceFunction {
|
||||
}
|
||||
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
output.isReturnValueDeref() and
|
||||
(
|
||||
output.isReturnValueDeref() or
|
||||
output.isReturnValue()
|
||||
) and
|
||||
description = "an environment variable"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ private class FgetsFunction extends DataFlowFunction, TaintFunction, ArrayFuncti
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
output.isParameterDeref(0) or
|
||||
output.isReturnValue() or
|
||||
output.isReturnValueDeref()
|
||||
) and
|
||||
description = "string read by " + this.getName()
|
||||
@@ -101,6 +102,7 @@ private class GetsFunction extends DataFlowFunction, ArrayFunction, AliasFunctio
|
||||
override predicate hasLocalFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
output.isParameterDeref(0) or
|
||||
output.isReturnValue() or
|
||||
output.isReturnValueDeref()
|
||||
) and
|
||||
description = "string read by " + this.getName()
|
||||
|
||||
@@ -123,7 +123,7 @@ private class StdSequenceContainerData extends TaintFunction {
|
||||
/**
|
||||
* The standard container functions `push_back` and `push_front`.
|
||||
*/
|
||||
class StdSequenceContainerPush extends MemberFunction {
|
||||
private class StdSequenceContainerPush extends TaintFunction {
|
||||
StdSequenceContainerPush() {
|
||||
this.getClassAndName("push_back") instanceof Vector or
|
||||
this.getClassAndName(["push_back", "push_front"]) instanceof Deque or
|
||||
@@ -131,17 +131,6 @@ class StdSequenceContainerPush extends MemberFunction {
|
||||
this.getClassAndName(["push_back", "push_front"]) instanceof List
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a reference to the
|
||||
* value type of the container.
|
||||
*/
|
||||
int getAValueTypeParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceContainerPushModel extends StdSequenceContainerPush, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to qualifier
|
||||
input.isParameterDeref(0) and
|
||||
@@ -171,7 +160,7 @@ private class StdSequenceContainerFrontBack extends TaintFunction {
|
||||
/**
|
||||
* The standard container functions `insert` and `insert_after`.
|
||||
*/
|
||||
class StdSequenceContainerInsert extends MemberFunction {
|
||||
private class StdSequenceContainerInsert extends TaintFunction {
|
||||
StdSequenceContainerInsert() {
|
||||
this.getClassAndName("insert") instanceof Deque or
|
||||
this.getClassAndName("insert") instanceof List or
|
||||
@@ -192,9 +181,7 @@ class StdSequenceContainerInsert extends MemberFunction {
|
||||
* Gets the index of a parameter to this function that is an iterator.
|
||||
*/
|
||||
int getAnIteratorParameterIndex() { this.getParameter(result).getType() instanceof Iterator }
|
||||
}
|
||||
|
||||
private class StdSequenceContainerInsertModel extends StdSequenceContainerInsert, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from parameter to container itself (qualifier) and return value
|
||||
(
|
||||
@@ -266,28 +253,11 @@ private class StdSequenceContainerAt extends TaintFunction {
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard `emplace` function.
|
||||
* The standard vector `emplace` function.
|
||||
*/
|
||||
class StdSequenceEmplace extends MemberFunction {
|
||||
StdSequenceEmplace() {
|
||||
this.getClassAndName("emplace") instanceof Vector
|
||||
or
|
||||
this.getClassAndName("emplace") instanceof List
|
||||
or
|
||||
this.getClassAndName("emplace") instanceof Deque
|
||||
}
|
||||
class StdVectorEmplace extends TaintFunction {
|
||||
StdVectorEmplace() { this.getClassAndName("emplace") instanceof Vector }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a reference to the
|
||||
* value type of the container.
|
||||
*/
|
||||
int getAValueTypeParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceEmplaceModel extends StdSequenceEmplace, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter except the position iterator to qualifier and return value
|
||||
// (here we assume taint flow from any constructor parameter to the constructed object)
|
||||
@@ -299,36 +269,12 @@ private class StdSequenceEmplaceModel extends StdSequenceEmplace, TaintFunction
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard vector `emplace` function.
|
||||
*/
|
||||
class StdVectorEmplace extends StdSequenceEmplace {
|
||||
StdVectorEmplace() { this.getDeclaringType() instanceof Vector }
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard vector `emplace_back` function.
|
||||
*/
|
||||
class StdSequenceEmplaceBack extends MemberFunction {
|
||||
StdSequenceEmplaceBack() {
|
||||
this.getClassAndName("emplace_back") instanceof Vector
|
||||
or
|
||||
this.getClassAndName("emplace_back") instanceof List
|
||||
or
|
||||
this.getClassAndName("emplace_back") instanceof Deque
|
||||
}
|
||||
class StdVectorEmplaceBack extends TaintFunction {
|
||||
StdVectorEmplaceBack() { this.getClassAndName("emplace_back") instanceof Vector }
|
||||
|
||||
/**
|
||||
* Gets the index of a parameter to this function that is a reference to the
|
||||
* value type of the container.
|
||||
*/
|
||||
int getAValueTypeParameterIndex() {
|
||||
this.getParameter(result).getUnspecifiedType().(ReferenceType).getBaseType() =
|
||||
this.getDeclaringType().getTemplateArgument(0).(Type).getUnspecifiedType() // i.e. the `T` of this `std::vector<T>`
|
||||
}
|
||||
}
|
||||
|
||||
private class StdSequenceEmplaceBackModel extends StdSequenceEmplaceBack, TaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from any parameter to qualifier
|
||||
// (here we assume taint flow from any constructor parameter to the constructed object)
|
||||
@@ -336,10 +282,3 @@ private class StdSequenceEmplaceBackModel extends StdSequenceEmplaceBack, TaintF
|
||||
output.isQualifierObject()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard vector `emplace_back` function.
|
||||
*/
|
||||
class StdVectorEmplaceBack extends StdSequenceEmplaceBack {
|
||||
StdVectorEmplaceBack() { this.getDeclaringType() instanceof Vector }
|
||||
}
|
||||
|
||||
@@ -99,11 +99,9 @@ private class StdStringConstructor extends Constructor, StdStringTaintFunction {
|
||||
/**
|
||||
* The `std::string` function `c_str`.
|
||||
*/
|
||||
class StdStringCStr extends MemberFunction {
|
||||
private class StdStringCStr extends StdStringTaintFunction {
|
||||
StdStringCStr() { this.getClassAndName("c_str") instanceof StdBasicString }
|
||||
}
|
||||
|
||||
private class StdStringCStrModel extends StdStringCStr, StdStringTaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from string itself (qualifier) to return value
|
||||
input.isQualifierObject() and
|
||||
@@ -114,11 +112,9 @@ private class StdStringCStrModel extends StdStringCStr, StdStringTaintFunction {
|
||||
/**
|
||||
* The `std::string` function `data`.
|
||||
*/
|
||||
class StdStringData extends MemberFunction {
|
||||
private class StdStringData extends StdStringTaintFunction {
|
||||
StdStringData() { this.getClassAndName("data") instanceof StdBasicString }
|
||||
}
|
||||
|
||||
private class StdStringDataModel extends StdStringData, StdStringTaintFunction {
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
// flow from string itself (qualifier) to return value
|
||||
input.isQualifierObject() and
|
||||
|
||||
@@ -10,8 +10,6 @@ import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
/**
|
||||
* The standard function `strcat` and its wide, sized, and Microsoft variants.
|
||||
*
|
||||
* Does not include `strlcat`, which is covered by `StrlcatFunction`
|
||||
*/
|
||||
class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, SideEffectFunction {
|
||||
StrcatFunction() {
|
||||
@@ -92,64 +90,3 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid
|
||||
buffer = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `strlcat` function.
|
||||
*/
|
||||
class StrlcatFunction extends TaintFunction, ArrayFunction, SideEffectFunction {
|
||||
StrlcatFunction() {
|
||||
this.hasGlobalName("strlcat") // strlcat(dst, src, dst_size)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the size of the copy (in characters).
|
||||
*/
|
||||
int getParamSize() { result = 2 }
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the source of the copy.
|
||||
*/
|
||||
int getParamSrc() { result = 1 }
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the destination to be appended to.
|
||||
*/
|
||||
int getParamDest() { result = 0 }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
(
|
||||
input.isParameter(2)
|
||||
or
|
||||
input.isParameterDeref(0)
|
||||
or
|
||||
input.isParameterDeref(1)
|
||||
) and
|
||||
output.isParameterDeref(0)
|
||||
}
|
||||
|
||||
override predicate hasArrayInput(int param) {
|
||||
param = 0 or
|
||||
param = 1
|
||||
}
|
||||
|
||||
override predicate hasArrayOutput(int param) { param = 0 }
|
||||
|
||||
override predicate hasArrayWithNullTerminator(int param) { param = 1 }
|
||||
|
||||
override predicate hasArrayWithUnknownSize(int param) { param = 0 }
|
||||
|
||||
override predicate hasOnlySpecificReadSideEffects() { any() }
|
||||
|
||||
override predicate hasOnlySpecificWriteSideEffects() { any() }
|
||||
|
||||
override predicate hasSpecificWriteSideEffect(ParameterIndex i, boolean buffer, boolean mustWrite) {
|
||||
i = 0 and
|
||||
buffer = true and
|
||||
mustWrite = false
|
||||
}
|
||||
|
||||
override predicate hasSpecificReadSideEffect(ParameterIndex i, boolean buffer) {
|
||||
(i = 0 or i = 1) and
|
||||
buffer = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
"wcsxfrm_l", // _strxfrm_l(dest, src, max_amount, locale)
|
||||
"_mbsnbcpy", // _mbsnbcpy(dest, src, max_amount)
|
||||
"stpcpy", // stpcpy(dest, src)
|
||||
"stpncpy", // stpncpy(dest, src, max_amount)
|
||||
"strlcpy" // strlcpy(dst, src, dst_size)
|
||||
"stpncpy" // stpcpy(dest, src, max_amount)
|
||||
])
|
||||
or
|
||||
(
|
||||
@@ -54,11 +53,6 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
*/
|
||||
private predicate isSVariant() { this.getName().matches("%\\_s") }
|
||||
|
||||
/**
|
||||
* Holds if the function returns the total length the string would have had if the size was unlimited.
|
||||
*/
|
||||
private predicate returnsTotalLength() { this.getName() = "strlcpy" }
|
||||
|
||||
/**
|
||||
* Gets the index of the parameter that is the maximum size of the copy (in characters).
|
||||
*/
|
||||
@@ -66,7 +60,7 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
if this.isSVariant()
|
||||
then result = 1
|
||||
else (
|
||||
this.getName().matches(["%ncpy%", "%nbcpy%", "%xfrm%", "strlcpy"]) and
|
||||
this.getName().matches(["%ncpy%", "%nbcpy%", "%xfrm%"]) and
|
||||
result = 2
|
||||
)
|
||||
}
|
||||
@@ -106,7 +100,6 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
input.isParameterDeref(this.getParamSrc()) and
|
||||
output.isReturnValueDeref()
|
||||
or
|
||||
not this.returnsTotalLength() and
|
||||
input.isParameter(this.getParamDest()) and
|
||||
output.isReturnValue()
|
||||
}
|
||||
@@ -117,9 +110,8 @@ class StrcpyFunction extends ArrayFunction, DataFlowFunction, TaintFunction, Sid
|
||||
exists(this.getParamSize()) and
|
||||
input.isParameterDeref(this.getParamSrc()) and
|
||||
(
|
||||
output.isParameterDeref(this.getParamDest())
|
||||
or
|
||||
not this.returnsTotalLength() and output.isReturnValueDeref()
|
||||
output.isParameterDeref(this.getParamDest()) or
|
||||
output.isReturnValueDeref()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ class SemSsaExplicitUpdate extends SemSsaVariable {
|
||||
|
||||
SemSsaExplicitUpdate() { Specific::explicitUpdate(this, sourceExpr) }
|
||||
|
||||
final SemExpr getSourceExpr() { result = sourceExpr }
|
||||
|
||||
final SemExpr getDefiningExpr() { result = sourceExpr }
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ private predicate constantIntegerExpr(SemExpr e, int val) {
|
||||
// Copy of another constant
|
||||
exists(SemSsaExplicitUpdate v, SemExpr src |
|
||||
e = v.getAUse() and
|
||||
src = v.getDefiningExpr() and
|
||||
src = v.getSourceExpr() and
|
||||
constantIntegerExpr(src, val)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -22,7 +22,30 @@ module CppLangImplConstant implements LangSig<Sem, FloatDelta> {
|
||||
predicate hasConstantBound(SemExpr e, float bound, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e2 >= e1 + delta` (if `upper = false`) or `e2 <= e1 + delta` (if `upper = true`).
|
||||
* Holds if `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
|
||||
*/
|
||||
predicate additionalBoundFlowStep(SemExpr e2, SemExpr e1, float delta, boolean upper) { none() }
|
||||
predicate hasBound(SemExpr e, SemExpr bound, float delta, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the value of `dest` is known to be `src + delta`.
|
||||
*/
|
||||
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified expression,
|
||||
* if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateType(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified source
|
||||
* variable, if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateTypeForSsaVariable(SemSsaVariable var) { none() }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
private import RangeAnalysisConstantSpecific
|
||||
private import RangeAnalysisRelativeSpecific
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
|
||||
private import RangeUtils
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExpr
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticCFG
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticGuard
|
||||
@@ -87,18 +88,12 @@ module Sem implements Semantic {
|
||||
|
||||
class AddressType = SemAddressType;
|
||||
|
||||
SemType getExprType(SemExpr e) { result = e.getSemType() }
|
||||
|
||||
SemType getSsaType(SemSsaVariable var) { result = var.getType() }
|
||||
|
||||
class SsaVariable = SemSsaVariable;
|
||||
|
||||
class SsaPhiNode = SemSsaPhiNode;
|
||||
|
||||
class SsaExplicitUpdate = SemSsaExplicitUpdate;
|
||||
|
||||
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, int delta) { none() }
|
||||
|
||||
predicate conversionCannotOverflow(Type fromType, Type toType) {
|
||||
SemanticType::conversionCannotOverflow(fromType, toType)
|
||||
}
|
||||
@@ -106,7 +101,7 @@ module Sem implements Semantic {
|
||||
|
||||
module SignAnalysis implements SignAnalysisSig<Sem> {
|
||||
private import SignAnalysisCommon as SA
|
||||
import SA::SignAnalysis<FloatDelta>
|
||||
import SA::SignAnalysis<FloatDelta, Util>
|
||||
}
|
||||
|
||||
module ConstantBounds implements BoundSig<SemLocation, Sem, FloatDelta> {
|
||||
@@ -169,16 +164,18 @@ private module ModulusAnalysisInstantiated implements ModulusAnalysisSig<Sem> {
|
||||
class ModBound = AllBounds::SemBound;
|
||||
|
||||
private import codeql.rangeanalysis.ModulusAnalysis as MA
|
||||
import MA::ModulusAnalysis<SemLocation, Sem, FloatDelta, AllBounds>
|
||||
import MA::ModulusAnalysis<SemLocation, Sem, FloatDelta, AllBounds, Util>
|
||||
}
|
||||
|
||||
module Util = RangeUtil<FloatDelta, CppLangImplConstant>;
|
||||
|
||||
module ConstantStage =
|
||||
RangeStage<SemLocation, Sem, FloatDelta, ConstantBounds, FloatOverflow, CppLangImplConstant,
|
||||
SignAnalysis, ModulusAnalysisInstantiated>;
|
||||
SignAnalysis, ModulusAnalysisInstantiated, Util>;
|
||||
|
||||
module RelativeStage =
|
||||
RangeStage<SemLocation, Sem, FloatDelta, RelativeBounds, FloatOverflow, CppLangImplRelative,
|
||||
SignAnalysis, ModulusAnalysisInstantiated>;
|
||||
SignAnalysis, ModulusAnalysisInstantiated, Util>;
|
||||
|
||||
private newtype TSemReason =
|
||||
TSemNoReason() or
|
||||
|
||||
@@ -54,7 +54,30 @@ module CppLangImplRelative implements LangSig<Sem, FloatDelta> {
|
||||
predicate hasConstantBound(SemExpr e, float bound, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e2 >= e1 + delta` (if `upper = false`) or `e2 <= e1 + delta` (if `upper = true`).
|
||||
* Holds if `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
|
||||
*/
|
||||
predicate additionalBoundFlowStep(SemExpr e2, SemExpr e1, float delta, boolean upper) { none() }
|
||||
predicate hasBound(SemExpr e, SemExpr bound, float delta, boolean upper) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the value of `dest` is known to be `src + delta`.
|
||||
*/
|
||||
predicate additionalValueFlowStep(SemExpr dest, SemExpr src, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified expression,
|
||||
* if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateType(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Gets the type that range analysis should use to track the result of the specified source
|
||||
* variable, if a type other than the original type of the expression is to be used.
|
||||
*
|
||||
* This predicate is commonly used in languages that support immutable "boxed" types that are
|
||||
* actually references but whose values can be tracked as the type contained in the box.
|
||||
*/
|
||||
SemType getAlternateTypeForSsaVariable(SemSsaVariable var) { none() }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Provides utility predicates for range analysis.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import RangeAnalysisRelativeSpecific
|
||||
private import codeql.rangeanalysis.RangeAnalysis
|
||||
private import RangeAnalysisImpl
|
||||
private import ConstantAnalysis
|
||||
|
||||
module RangeUtil<DeltaSig D, LangSig<Sem, D> Lang> implements UtilSig<Sem, D> {
|
||||
/**
|
||||
* Gets an expression that equals `v - d`.
|
||||
*/
|
||||
private SemExpr semSsaRead(SemSsaVariable v, D::Delta delta) {
|
||||
// There are various language-specific extension points that can be removed once we no longer
|
||||
// expect to match the original Java implementation's results exactly.
|
||||
result = v.getAUse() and delta = D::fromInt(0)
|
||||
or
|
||||
exists(D::Delta d1, SemConstantIntegerExpr c |
|
||||
result.(SemAddExpr).hasOperands(semSsaRead(v, d1), c) and
|
||||
delta = D::fromFloat(D::toFloat(d1) - c.getIntValue())
|
||||
)
|
||||
or
|
||||
exists(SemSubExpr sub, D::Delta d1, SemConstantIntegerExpr c |
|
||||
result = sub and
|
||||
sub.getLeftOperand() = semSsaRead(v, d1) and
|
||||
sub.getRightOperand() = c and
|
||||
delta = D::fromFloat(D::toFloat(d1) + c.getIntValue())
|
||||
)
|
||||
or
|
||||
result = v.(SemSsaExplicitUpdate).getSourceExpr() and
|
||||
delta = D::fromFloat(0)
|
||||
or
|
||||
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta)
|
||||
or
|
||||
result.(SemStoreExpr).getOperand() = semSsaRead(v, delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a condition that tests whether `v` equals `e + delta`.
|
||||
*
|
||||
* If the condition evaluates to `testIsTrue`:
|
||||
* - `isEq = true` : `v == e + delta`
|
||||
* - `isEq = false` : `v != e + delta`
|
||||
*/
|
||||
pragma[nomagic]
|
||||
SemGuard semEqFlowCond(
|
||||
SemSsaVariable v, SemExpr e, D::Delta delta, boolean isEq, boolean testIsTrue
|
||||
) {
|
||||
exists(boolean eqpolarity |
|
||||
result.isEquality(semSsaRead(v, delta), e, eqpolarity) and
|
||||
(testIsTrue = true or testIsTrue = false) and
|
||||
eqpolarity.booleanXor(testIsTrue).booleanNot() = isEq
|
||||
)
|
||||
or
|
||||
exists(boolean testIsTrue0 |
|
||||
semImplies_v2(result, testIsTrue, semEqFlowCond(v, e, delta, isEq, testIsTrue0), testIsTrue0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` is an `SsaExplicitUpdate` that equals `e + delta`.
|
||||
*/
|
||||
predicate semSsaUpdateStep(SemSsaExplicitUpdate v, SemExpr e, D::Delta delta) {
|
||||
exists(SemExpr defExpr | defExpr = v.getSourceExpr() |
|
||||
defExpr.(SemCopyValueExpr).getOperand() = e and delta = D::fromFloat(0)
|
||||
or
|
||||
defExpr.(SemStoreExpr).getOperand() = e and delta = D::fromFloat(0)
|
||||
or
|
||||
defExpr.(SemAddOneExpr).getOperand() = e and delta = D::fromFloat(1)
|
||||
or
|
||||
defExpr.(SemSubOneExpr).getOperand() = e and delta = D::fromFloat(-1)
|
||||
or
|
||||
e = defExpr and
|
||||
not (
|
||||
defExpr instanceof SemCopyValueExpr or
|
||||
defExpr instanceof SemStoreExpr or
|
||||
defExpr instanceof SemAddOneExpr or
|
||||
defExpr instanceof SemSubOneExpr
|
||||
) and
|
||||
delta = D::fromFloat(0)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e1 + delta` equals `e2`.
|
||||
*/
|
||||
predicate semValueFlowStep(SemExpr e2, SemExpr e1, D::Delta delta) {
|
||||
e2.(SemCopyValueExpr).getOperand() = e1 and delta = D::fromFloat(0)
|
||||
or
|
||||
e2.(SemStoreExpr).getOperand() = e1 and delta = D::fromFloat(0)
|
||||
or
|
||||
e2.(SemAddOneExpr).getOperand() = e1 and delta = D::fromFloat(1)
|
||||
or
|
||||
e2.(SemSubOneExpr).getOperand() = e1 and delta = D::fromFloat(-1)
|
||||
or
|
||||
Lang::additionalValueFlowStep(e2, e1, delta)
|
||||
or
|
||||
exists(SemExpr x | e2.(SemAddExpr).hasOperands(e1, x) |
|
||||
D::fromInt(x.(SemConstantIntegerExpr).getIntValue()) = delta
|
||||
)
|
||||
or
|
||||
exists(SemExpr x, SemSubExpr sub |
|
||||
e2 = sub and
|
||||
sub.getLeftOperand() = e1 and
|
||||
sub.getRightOperand() = x
|
||||
|
|
||||
D::fromInt(-x.(SemConstantIntegerExpr).getIntValue()) = delta
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified expression's range information.
|
||||
*
|
||||
* Usually, this just `e.getSemType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedType(SemExpr e) {
|
||||
result = Lang::getAlternateType(e)
|
||||
or
|
||||
not exists(Lang::getAlternateType(e)) and result = e.getSemType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type used to track the specified source variable's range information.
|
||||
*
|
||||
* Usually, this just `e.getType()`, but the language can override this to track immutable boxed
|
||||
* primitive types as the underlying primitive type.
|
||||
*/
|
||||
SemType getTrackedTypeForSsaVariable(SemSsaVariable var) {
|
||||
result = Lang::getAlternateTypeForSsaVariable(var)
|
||||
or
|
||||
not exists(Lang::getAlternateTypeForSsaVariable(var)) and result = var.getType()
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,10 @@ private import RangeAnalysisImpl
|
||||
private import SignAnalysisSpecific as Specific
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import ConstantAnalysis
|
||||
private import RangeUtils
|
||||
private import Sign
|
||||
|
||||
module SignAnalysis<DeltaSig D> {
|
||||
module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
|
||||
private import codeql.rangeanalysis.internal.RangeUtils::MakeUtils<Sem, D>
|
||||
|
||||
/**
|
||||
@@ -38,7 +39,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
|
||||
/** An SSA definition whose sign is determined by the sign of that definitions source expression. */
|
||||
private class ExplicitSignDef extends FlowSignDef instanceof SemSsaExplicitUpdate {
|
||||
final override Sign getSign() { result = semExprSign(super.getDefiningExpr()) }
|
||||
final override Sign getSign() { result = semExprSign(super.getSourceExpr()) }
|
||||
}
|
||||
|
||||
/** An SSA Phi definition, whose sign is the union of the signs of its inputs. */
|
||||
@@ -147,7 +148,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
not this instanceof ConstantSignExpr and
|
||||
(
|
||||
// Only track numeric types.
|
||||
Sem::getExprType(this) instanceof SemNumericType
|
||||
Utils::getTrackedType(this) instanceof SemNumericType
|
||||
or
|
||||
// Unless the language says to track this expression anyway.
|
||||
Specific::trackUnknownNonNumericExpr(this)
|
||||
@@ -202,7 +203,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
|
||||
/** An expression of an unsigned type. */
|
||||
private class UnsignedExpr extends FlowSignExpr {
|
||||
UnsignedExpr() { Sem::getExprType(this) instanceof SemUnsignedIntegerType }
|
||||
UnsignedExpr() { Utils::getTrackedType(this) instanceof SemUnsignedIntegerType }
|
||||
|
||||
override Sign getSignRestriction() {
|
||||
result = TPos() or
|
||||
@@ -275,7 +276,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
override SemUnboxExpr cast;
|
||||
|
||||
UnboxSignExpr() {
|
||||
exists(SemType fromType | fromType = Sem::getExprType(cast.getOperand()) |
|
||||
exists(SemType fromType | fromType = Utils::getTrackedType(cast.getOperand()) |
|
||||
// Only numeric source types are handled here.
|
||||
fromType instanceof SemNumericType
|
||||
)
|
||||
@@ -470,7 +471,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
Sign semExprSign(SemExpr e) {
|
||||
exists(Sign s | s = e.(SignExpr).getSign() |
|
||||
if
|
||||
Sem::getExprType(e) instanceof SemUnsignedIntegerType and
|
||||
Utils::getTrackedType(e) instanceof SemUnsignedIntegerType and
|
||||
s = TNeg() and
|
||||
not Specific::ignoreTypeRestrictions(e)
|
||||
then result = TPos()
|
||||
|
||||
@@ -405,8 +405,6 @@ function_deleted(unique int id: @function ref);
|
||||
|
||||
function_defaulted(unique int id: @function ref);
|
||||
|
||||
function_prototyped(unique int id: @function ref)
|
||||
|
||||
member_function_this_type(
|
||||
unique int id: @function ref,
|
||||
int this_type: @type ref
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
description: Expose whether a function was prototyped or not
|
||||
compatibility: partial
|
||||
@@ -1,23 +1,3 @@
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.9.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The `cpp/tainted-format-string-through-global` query has been deleted. This does not lead to a loss of relevant alerts, as the query duplicated a subset of the alerts from `cpp/tainted-format-string`.
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `cpp/use-of-string-after-lifetime-ends`, to detect calls to `c_str` on strings that will be destroyed immediately.
|
||||
|
||||
## 0.8.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The `cpp/uninitialized-local` query has been improved to produce fewer false positives.
|
||||
|
||||
## 0.8.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -27,26 +27,16 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
|
||||
ReturnStackAllocatedMemoryConfig() { this = "ReturnStackAllocatedMemoryConfig" }
|
||||
|
||||
override predicate isSource(Instruction source) {
|
||||
exists(Function func |
|
||||
// Holds if `source` is a node that represents the use of a stack variable
|
||||
exists(VariableAddressInstruction var, Function func |
|
||||
var = source and
|
||||
func = source.getEnclosingFunction() and
|
||||
var.getAstVariable() instanceof StackVariable and
|
||||
// Pointer-to-member types aren't properly handled in the dbscheme.
|
||||
not var.getResultType() instanceof PointerToMemberType and
|
||||
// Rule out FPs caused by extraction errors.
|
||||
not any(ErrorExpr e).getEnclosingFunction() = func and
|
||||
not intentionallyReturnsStackPointer(func) and
|
||||
func = source.getEnclosingFunction()
|
||||
|
|
||||
// `source` is an instruction that represents the use of a stack variable
|
||||
exists(VariableAddressInstruction var |
|
||||
var = source and
|
||||
var.getAstVariable() instanceof StackVariable and
|
||||
// Pointer-to-member types aren't properly handled in the dbscheme.
|
||||
not var.getResultType() instanceof PointerToMemberType
|
||||
)
|
||||
or
|
||||
// `source` is an instruction that represents the return value of a
|
||||
// function that is known to return stack-allocated memory.
|
||||
exists(Call call |
|
||||
call.getTarget().hasGlobalName(["alloca", "strdupa", "strndupa", "_alloca", "_malloca"]) and
|
||||
source.getUnconvertedResultExpression() = call
|
||||
)
|
||||
not intentionallyReturnsStackPointer(func)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -95,10 +85,10 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
|
||||
}
|
||||
|
||||
from
|
||||
MustFlowPathNode source, MustFlowPathNode sink, Instruction instr,
|
||||
MustFlowPathNode source, MustFlowPathNode sink, VariableAddressInstruction var,
|
||||
ReturnStackAllocatedMemoryConfig conf
|
||||
where
|
||||
conf.hasFlowPath(pragma[only_bind_into](source), pragma[only_bind_into](sink)) and
|
||||
source.getInstruction() = instr
|
||||
source.getInstruction() = var
|
||||
select sink.getInstruction(), source, sink, "May return stack-allocated memory from $@.",
|
||||
instr.getAst(), instr.getAst().toString()
|
||||
var.getAst(), var.getAst().toString()
|
||||
|
||||
@@ -14,44 +14,25 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.ir.IR
|
||||
import Flow::PathGraph
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
predicate isProcessOperationExplanation(DataFlow::Node arg, string processOperation) {
|
||||
predicate isProcessOperationExplanation(Expr arg, string processOperation) {
|
||||
exists(int processOperationArg, FunctionCall call |
|
||||
isProcessOperationArgument(processOperation, processOperationArg) and
|
||||
call.getTarget().getName() = processOperation and
|
||||
call.getArgument(processOperationArg) = [arg.asExpr(), arg.asIndirectExpr()]
|
||||
call.getArgument(processOperationArg) = arg
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSource(FlowSource source, string sourceType) { sourceType = source.getSourceType() }
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { isSource(node, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node node) { isProcessOperationExplanation(node, _) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
isSink(node) and node.asExpr().getUnspecifiedType() instanceof ArithmeticType
|
||||
or
|
||||
node.asInstruction().(StoreInstruction).getResultType() instanceof ArithmeticType
|
||||
}
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element arg) { isProcessOperationExplanation(arg, _) }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
from
|
||||
string processOperation, string sourceType, DataFlow::Node source, DataFlow::Node sink,
|
||||
Flow::PathNode sourceNode, Flow::PathNode sinkNode
|
||||
from string processOperation, Expr arg, Expr source, PathNode sourceNode, PathNode sinkNode
|
||||
where
|
||||
source = sourceNode.getNode() and
|
||||
sink = sinkNode.getNode() and
|
||||
isSource(source, sourceType) and
|
||||
isProcessOperationExplanation(sink, processOperation) and
|
||||
Flow::flowPath(sourceNode, sinkNode)
|
||||
select sink, sourceNode, sinkNode,
|
||||
isProcessOperationExplanation(arg, processOperation) and
|
||||
taintedWithPath(source, arg, sourceNode, sinkNode)
|
||||
select arg, sourceNode, sinkNode,
|
||||
"The value of this argument may come from $@ and is being passed to " + processOperation + ".",
|
||||
source, sourceType
|
||||
source, source.toString()
|
||||
|
||||
@@ -52,17 +52,16 @@ predicate isUnboundedWrite(BufferWrite bw) {
|
||||
* Holds if `e` is a source buffer going into an unbounded write `bw` or a
|
||||
* qualifier of (a qualifier of ...) such a source.
|
||||
*/
|
||||
predicate unboundedWriteSource(Expr e, BufferWrite bw, boolean qualifier) {
|
||||
isUnboundedWrite(bw) and e = bw.getASource() and qualifier = false
|
||||
predicate unboundedWriteSource(Expr e, BufferWrite bw) {
|
||||
isUnboundedWrite(bw) and e = bw.getASource()
|
||||
or
|
||||
exists(FieldAccess fa | unboundedWriteSource(fa, bw, _) and e = fa.getQualifier()) and
|
||||
qualifier = true
|
||||
exists(FieldAccess fa | unboundedWriteSource(fa, bw) and e = fa.getQualifier())
|
||||
}
|
||||
|
||||
predicate isSource(FS::FlowSource source, string sourceType) { source.getSourceType() = sourceType }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, BufferWrite bw, boolean qualifier) {
|
||||
unboundedWriteSource(sink.asIndirectExpr(), bw, qualifier)
|
||||
predicate isSink(DataFlow::Node sink, BufferWrite bw) {
|
||||
unboundedWriteSource(sink.asIndirectExpr(), bw)
|
||||
or
|
||||
// `gets` and `scanf` reads from stdin so there's no real input.
|
||||
// The `BufferWrite` library models this as the call itself being
|
||||
@@ -70,7 +69,7 @@ predicate isSink(DataFlow::Node sink, BufferWrite bw, boolean qualifier) {
|
||||
// the sink so that we report a path where source = sink (because
|
||||
// the same output argument is also included in `isSource`).
|
||||
bw.getASource() = bw and
|
||||
unboundedWriteSource(sink.asDefiningArgument(), bw, qualifier)
|
||||
unboundedWriteSource(sink.asDefiningArgument(), bw)
|
||||
}
|
||||
|
||||
predicate lessThanOrEqual(IRGuardCondition g, Expr e, boolean branch) {
|
||||
@@ -85,9 +84,9 @@ predicate lessThanOrEqual(IRGuardCondition g, Expr e, boolean branch) {
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(sink, _, _) }
|
||||
predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) { isSink(node, _, false) }
|
||||
predicate isBarrierOut(DataFlow::Node node) { isSink(node) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// Block flow if the node is guarded by any <, <= or = operations.
|
||||
@@ -117,7 +116,7 @@ from BufferWrite bw, Flow::PathNode source, Flow::PathNode sink, string sourceTy
|
||||
where
|
||||
Flow::flowPath(source, sink) and
|
||||
isSource(source.getNode(), sourceType) and
|
||||
isSink(sink.getNode(), bw, _)
|
||||
isSink(sink.getNode(), bw)
|
||||
select bw, source, sink,
|
||||
"This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.",
|
||||
source.getNode(), sourceType
|
||||
|
||||
@@ -16,47 +16,22 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.ir.IR
|
||||
import Flow::PathGraph
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
predicate isSource(FlowSource source, string sourceType) { sourceType = source.getSourceType() }
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { isSource(node, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(PrintfLikeFunction printf |
|
||||
printf.outermostWrapperFunctionCall([node.asExpr(), node.asIndirectExpr()], _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isArithmeticNonCharType(ArithmeticType type) {
|
||||
not type instanceof CharType and
|
||||
not type instanceof Char8Type and
|
||||
not type instanceof Char16Type and
|
||||
not type instanceof Char32Type
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
isSink(node) and isArithmeticNonCharType(node.asExpr().getUnspecifiedType())
|
||||
or
|
||||
isArithmeticNonCharType(node.asInstruction().(StoreInstruction).getResultType())
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element tainted) {
|
||||
exists(PrintfLikeFunction printf | printf.outermostWrapperFunctionCall(tainted, _))
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
from
|
||||
PrintfLikeFunction printf, string printfFunction, string sourceType, DataFlow::Node source,
|
||||
DataFlow::Node sink, Flow::PathNode sourceNode, Flow::PathNode sinkNode
|
||||
PrintfLikeFunction printf, Expr arg, PathNode sourceNode, PathNode sinkNode,
|
||||
string printfFunction, Expr userValue, string cause
|
||||
where
|
||||
source = sourceNode.getNode() and
|
||||
sink = sinkNode.getNode() and
|
||||
isSource(source, sourceType) and
|
||||
printf.outermostWrapperFunctionCall([sink.asExpr(), sink.asIndirectExpr()], printfFunction) and
|
||||
Flow::flowPath(sourceNode, sinkNode)
|
||||
select sink, sourceNode, sinkNode,
|
||||
printf.outermostWrapperFunctionCall(arg, printfFunction) and
|
||||
taintedWithPath(userValue, arg, sourceNode, sinkNode) and
|
||||
isUserInput(userValue, cause)
|
||||
select arg, sourceNode, sinkNode,
|
||||
"The value of this argument may come from $@ and is being used as a formatting argument to " +
|
||||
printfFunction + ".", source, sourceType
|
||||
printfFunction + ".", userValue, cause
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <stdio.h>
|
||||
|
||||
char *copy;
|
||||
|
||||
void copyArgv(char **argv) {
|
||||
copy = argv[1];
|
||||
}
|
||||
|
||||
void printWrapper(char *str) {
|
||||
printf(str);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
copyArgv(argv);
|
||||
|
||||
// This should be avoided
|
||||
printf(copy);
|
||||
|
||||
// This should be avoided too, because it has the same effect
|
||||
printWrapper(copy);
|
||||
|
||||
// This is fine
|
||||
printf("%s", copy);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>The program uses input from the user, propagated via a global variable, as a format string for <code>printf</code> style functions.
|
||||
This can lead to buffer overflows or data representation problems. An attacker can exploit this weakness to crash the program,
|
||||
disclose information or even execute arbitrary code.</p>
|
||||
|
||||
<p>This rule only identifies inputs from the user that are transferred through global variables before being used in <code>printf</code> style functions.
|
||||
Analyzing the flow of data through global variables is more prone to errors and so this rule may identify some examples of code where
|
||||
the input is not really from the user. For example, when a global variable is set in two places, one that comes from the user and one that does not.
|
||||
In this case we would mark all usages of the global variable as input from the user, but the input from the user may always came after the call to the
|
||||
<code>printf</code> style functions.</p>
|
||||
|
||||
<p>The results of this rule should be considered alongside the related rule "Uncontrolled format string" which tracks the flow of the
|
||||
values input by a user, excluding global variables, until the values are used as the format argument for a <code>printf</code> like function call.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>Use constant expressions as the format strings. If you need to print a value from the user, use <code>printf("%s", value_from_user)</code>.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<sample src="UncontrolledFormatStringThroughGlobalVar.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>CERT C Coding
|
||||
Standard: <a href="https://www.securecoding.cert.org/confluence/display/c/FIO30-C.+Exclude+user+input+from+format+strings">FIO30-C. Exclude
|
||||
user input from format strings</a>.</li>
|
||||
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @name Uncontrolled format string (through global variable)
|
||||
* @description Using externally-controlled format strings in
|
||||
* printf-style functions can lead to buffer overflows
|
||||
* or data representation problems.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 9.3
|
||||
* @precision high
|
||||
* @id cpp/tainted-format-string-through-global
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-134
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.FunctionWithWrappers
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element tainted) {
|
||||
exists(PrintfLikeFunction printf | printf.outermostWrapperFunctionCall(tainted, _))
|
||||
}
|
||||
|
||||
override predicate taintThroughGlobals() { any() }
|
||||
}
|
||||
|
||||
from
|
||||
PrintfLikeFunction printf, Expr arg, PathNode sourceNode, PathNode sinkNode,
|
||||
string printfFunction, Expr userValue, string cause
|
||||
where
|
||||
printf.outermostWrapperFunctionCall(arg, printfFunction) and
|
||||
not taintedWithoutGlobals(arg) and
|
||||
taintedWithPath(userValue, arg, sourceNode, sinkNode) and
|
||||
isUserInput(userValue, cause)
|
||||
select arg, sourceNode, sinkNode,
|
||||
"The value of this argument may come from $@ and is being used as a formatting argument to " +
|
||||
printfFunction + ".", userValue, cause
|
||||
@@ -12,44 +12,79 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.NullTermination
|
||||
import semmle.code.cpp.security.FlowSources as FS
|
||||
import semmle.code.cpp.dataflow.new.TaintTracking
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
|
||||
predicate isSource(FS::FlowSource source, string sourceType) {
|
||||
sourceType = source.getSourceType() and
|
||||
exists(VariableAccess va, Call call |
|
||||
va = source.asDefiningArgument() and
|
||||
call.getAnArgument() = va and
|
||||
va.getTarget() instanceof SemanticStackVariable and
|
||||
call.getTarget().hasGlobalName(["read", "fread", "recv", "recvfrom", "recvmsg"])
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, VariableAccess va) {
|
||||
va = [sink.asExpr(), sink.asIndirectExpr()] and
|
||||
variableMustBeNullTerminated(va)
|
||||
}
|
||||
|
||||
private module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
isSink(node) and node.asExpr().getUnspecifiedType() instanceof ArithmeticType
|
||||
or
|
||||
node.asInstruction().(StoreInstruction).getResultType() instanceof ArithmeticType
|
||||
or
|
||||
mayAddNullTerminator(_, node.asIndirectExpr())
|
||||
/** A user-controlled expression that may not be null terminated. */
|
||||
class TaintSource extends VariableAccess {
|
||||
TaintSource() {
|
||||
exists(SecurityOptions x, string cause |
|
||||
this.getTarget() instanceof SemanticStackVariable and
|
||||
x.isUserInput(this, cause)
|
||||
|
|
||||
cause = ["read", "fread", "recv", "recvfrom", "recvmsg"]
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
|
||||
/**
|
||||
* Holds if `sink` is a tainted variable access that must be null
|
||||
* terminated.
|
||||
*/
|
||||
private predicate isSink(VariableAccess sink) {
|
||||
tainted(this, sink) and
|
||||
variableMustBeNullTerminated(sink)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this source can reach `va`, possibly using intermediate
|
||||
* reassignments.
|
||||
*/
|
||||
private predicate sourceReaches(VariableAccess va) {
|
||||
definitionUsePair(_, this, va)
|
||||
or
|
||||
exists(VariableAccess mid, Expr def |
|
||||
this.sourceReaches(mid) and
|
||||
exprDefinition(_, def, mid) and
|
||||
definitionUsePair(_, def, va)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the sink `sink` is reachable both from this source and
|
||||
* from `va`, possibly using intermediate reassignments.
|
||||
*/
|
||||
private predicate reachesSink(VariableAccess va, VariableAccess sink) {
|
||||
this.isSink(sink) and
|
||||
va = sink
|
||||
or
|
||||
exists(VariableAccess mid, Expr def |
|
||||
this.reachesSink(mid, sink) and
|
||||
exprDefinition(_, def, va) and
|
||||
definitionUsePair(_, def, mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a tainted variable access that must be null
|
||||
* terminated, and no access which null terminates its contents can
|
||||
* either reach the sink or be reached from the source. (Ideally,
|
||||
* we should instead look for such accesses only on the path from
|
||||
* this source to `sink` found via `tainted(source, sink)`.)
|
||||
*/
|
||||
predicate reaches(VariableAccess sink) {
|
||||
this.isSink(sink) and
|
||||
not exists(VariableAccess va |
|
||||
va != this and
|
||||
va != sink and
|
||||
mayAddNullTerminator(_, va)
|
||||
|
|
||||
this.sourceReaches(va)
|
||||
or
|
||||
this.reachesSink(va, sink)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink, VariableAccess va, string sourceType
|
||||
where
|
||||
Flow::flow(source, sink) and
|
||||
isSource(source, sourceType) and
|
||||
isSink(sink, va)
|
||||
select va, "String operation depends on $@ that may not be null terminated.", source, sourceType
|
||||
from TaintSource source, VariableAccess sink
|
||||
where source.reaches(sink)
|
||||
select sink, "String operation depends on a $@ that may not be null terminated.", source,
|
||||
"user-provided value"
|
||||
|
||||
@@ -14,13 +14,10 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.dataflow.new.TaintTracking
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.controlflow.IRGuards as IRGuards
|
||||
import semmle.code.cpp.security.FlowSources as FS
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
import Bounded
|
||||
import Flow::PathGraph
|
||||
|
||||
bindingset[op]
|
||||
predicate missingGuard(Operation op, Expr e, string effect) {
|
||||
@@ -31,90 +28,28 @@ predicate missingGuard(Operation op, Expr e, string effect) {
|
||||
not e instanceof VariableAccess and effect = "overflow"
|
||||
}
|
||||
|
||||
predicate isSource(FS::FlowSource source, string sourceType) { sourceType = source.getSourceType() }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, Operation op, Expr e) {
|
||||
e = sink.asExpr() and
|
||||
missingGuard(op, e, _) and
|
||||
op.getAnOperand() = e and
|
||||
(
|
||||
op instanceof UnaryArithmeticOperation or
|
||||
op instanceof BinaryArithmeticOperation or
|
||||
op instanceof AssignArithmeticOperation
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
predicate constantInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction or
|
||||
constantInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
|
||||
predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
predicate nodeIsBarrierEqualityCandidate(DataFlow::Node node, Operand access, Variable checkedVar) {
|
||||
exists(Instruction instr | instr = node.asInstruction() |
|
||||
readsVariable(instr, checkedVar) and
|
||||
any(IRGuards::IRGuardCondition guard).ensuresEq(access, _, _, instr.getBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(sink, _, _) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
exists(StoreInstruction store | store = node.asInstruction() |
|
||||
// Block flow to "likely small expressions"
|
||||
bounded(store.getSourceValue().getUnconvertedResultExpression())
|
||||
or
|
||||
// Block flow to "small types"
|
||||
store.getResultType().getUnspecifiedType().(IntegralType).getSize() <= 1
|
||||
)
|
||||
or
|
||||
// Block flow if there's an upper bound check of the variable anywhere in the program
|
||||
exists(Variable checkedVar, Instruction instr | instr = node.asInstruction() |
|
||||
readsVariable(instr, checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
// Block flow if the node is guarded by an equality check
|
||||
exists(Variable checkedVar, Operand access |
|
||||
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
|
||||
readsVariable(access.getDef(), checkedVar)
|
||||
)
|
||||
or
|
||||
// Block flow to any binary instruction whose operands are both non-constants.
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not constantInstruction(iTo.getLeft()) and
|
||||
not constantInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of constantness
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element e) {
|
||||
exists(Operation op |
|
||||
missingGuard(op, e, _) and
|
||||
op.getAnOperand() = e
|
||||
|
|
||||
op instanceof UnaryArithmeticOperation or
|
||||
op instanceof BinaryArithmeticOperation or
|
||||
op instanceof AssignArithmeticOperation
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isBarrier(Expr e) {
|
||||
super.isBarrier(e) or bounded(e) or e.getUnspecifiedType().(IntegralType).getSize() <= 1
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
from
|
||||
Expr e, string effect, Flow::PathNode source, Flow::PathNode sink, Operation op, string sourceType
|
||||
from Expr origin, Expr e, string effect, PathNode sourceNode, PathNode sinkNode, Operation op
|
||||
where
|
||||
Flow::flowPath(source, sink) and
|
||||
isSource(source.getNode(), sourceType) and
|
||||
isSink(sink.getNode(), op, e) and
|
||||
taintedWithPath(origin, e, sourceNode, sinkNode) and
|
||||
op.getAnOperand() = e and
|
||||
missingGuard(op, e, effect)
|
||||
select e, source, sink,
|
||||
select e, sourceNode, sinkNode,
|
||||
"$@ flows to an operand of an arithmetic expression, potentially causing an " + effect + ".",
|
||||
source, sourceType
|
||||
origin, "User-provided value"
|
||||
|
||||
@@ -16,30 +16,45 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.dataflow.new.TaintTracking
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.controlflow.IRGuards as IRGuards
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
|
||||
predicate isMaxValue(Expr mie) {
|
||||
exists(MacroInvocation mi |
|
||||
mi.getExpr() = mie and
|
||||
mi.getMacroName() = ["CHAR_MAX", "LLONG_MAX", "INT_MAX", "SHRT_MAX", "UINT_MAX"]
|
||||
(
|
||||
mi.getMacroName() = "CHAR_MAX" or
|
||||
mi.getMacroName() = "LLONG_MAX" or
|
||||
mi.getMacroName() = "INT_MAX" or
|
||||
mi.getMacroName() = "SHRT_MAX" or
|
||||
mi.getMacroName() = "UINT_MAX"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isMinValue(Expr mie) {
|
||||
exists(MacroInvocation mi |
|
||||
mi.getExpr() = mie and
|
||||
mi.getMacroName() = ["CHAR_MIN", "LLONG_MIN", "INT_MIN", "SHRT_MIN"]
|
||||
(
|
||||
mi.getMacroName() = "CHAR_MIN" or
|
||||
mi.getMacroName() = "LLONG_MIN" or
|
||||
mi.getMacroName() = "INT_MIN" or
|
||||
mi.getMacroName() = "SHRT_MIN"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source, string cause) {
|
||||
exists(Expr expr | expr = source.asExpr() |
|
||||
class SecurityOptionsArith extends SecurityOptions {
|
||||
override predicate isUserInput(Expr expr, string cause) {
|
||||
isMaxValue(expr) and cause = "max value"
|
||||
or
|
||||
isMinValue(expr) and cause = "min value"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate taintedVarAccess(Expr origin, VariableAccess va, string cause) {
|
||||
isUserInput(origin, cause) and
|
||||
tainted(origin, va)
|
||||
}
|
||||
|
||||
predicate causeEffectCorrespond(string cause, string effect) {
|
||||
@@ -50,79 +65,16 @@ predicate causeEffectCorrespond(string cause, string effect) {
|
||||
effect = "underflow"
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink, VariableAccess va, string effect) {
|
||||
exists(Operation op |
|
||||
sink.asExpr() = va and
|
||||
op.getAnOperand() = va
|
||||
|
|
||||
from Expr origin, Operation op, VariableAccess va, string cause, string effect
|
||||
where
|
||||
taintedVarAccess(origin, va, cause) and
|
||||
op.getAnOperand() = va and
|
||||
(
|
||||
missingGuardAgainstUnderflow(op, va) and effect = "underflow"
|
||||
or
|
||||
missingGuardAgainstOverflow(op, va) and effect = "overflow"
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
predicate constantInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction or
|
||||
constantInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
|
||||
predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
predicate nodeIsBarrierEqualityCandidate(DataFlow::Node node, Operand access, Variable checkedVar) {
|
||||
exists(Instruction instr | instr = node.asInstruction() |
|
||||
readsVariable(instr, checkedVar) and
|
||||
any(IRGuards::IRGuardCondition guard).ensuresEq(access, _, _, instr.getBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(sink, _, _) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// Block flow if there's an upper bound check of the variable anywhere in the program
|
||||
exists(Variable checkedVar, Instruction instr | instr = node.asInstruction() |
|
||||
readsVariable(instr, checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
// Block flow if the node is guarded by an equality check
|
||||
exists(Variable checkedVar, Operand access |
|
||||
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
|
||||
readsVariable(access.getDef(), checkedVar)
|
||||
)
|
||||
or
|
||||
// Block flow to any binary instruction whose operands are both non-constants.
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not constantInstruction(iTo.getLeft()) and
|
||||
not constantInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of constantness
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink, VariableAccess va, string cause, string effect
|
||||
where
|
||||
Flow::flow(source, sink) and
|
||||
isSource(source, cause) and
|
||||
causeEffectCorrespond(cause, effect) and
|
||||
isSink(sink, va, effect)
|
||||
) and
|
||||
causeEffectCorrespond(cause, effect)
|
||||
select va,
|
||||
"$@ flows to an operand of an arithmetic expression, potentially causing an " + effect + ".",
|
||||
source, "Extreme value"
|
||||
origin, "Extreme value"
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.security.FlowSources as FS
|
||||
import semmle.code.cpp.dataflow.new.TaintTracking
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.controlflow.IRGuards as IRGuards
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
|
||||
/** Holds if `expr` might overflow. */
|
||||
predicate outOfBoundsExpr(Expr expr, string kind) {
|
||||
@@ -31,76 +27,13 @@ predicate outOfBoundsExpr(Expr expr, string kind) {
|
||||
else none()
|
||||
}
|
||||
|
||||
predicate isSource(FS::FlowSource source, string sourceType) { sourceType = source.getSourceType() }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, string kind) {
|
||||
exists(Expr use |
|
||||
use = sink.asExpr() and
|
||||
not use.getUnspecifiedType() instanceof PointerType and
|
||||
outOfBoundsExpr(use, kind) and
|
||||
not inSystemMacroExpansion(use)
|
||||
)
|
||||
}
|
||||
|
||||
predicate hasUpperBoundsCheck(Variable var) {
|
||||
exists(RelationalOperation oper, VariableAccess access |
|
||||
oper.getAnOperand() = access and
|
||||
access.getTarget() = var and
|
||||
// Comparing to 0 is not an upper bound check
|
||||
not oper.getAnOperand().getValue() = "0"
|
||||
)
|
||||
}
|
||||
|
||||
predicate constantInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction or
|
||||
constantInstruction(instr.(UnaryInstruction).getUnary())
|
||||
}
|
||||
|
||||
predicate readsVariable(LoadInstruction load, Variable var) {
|
||||
load.getSourceAddress().(VariableAddressInstruction).getAstVariable() = var
|
||||
}
|
||||
|
||||
predicate nodeIsBarrierEqualityCandidate(DataFlow::Node node, Operand access, Variable checkedVar) {
|
||||
exists(Instruction instr | instr = node.asInstruction() |
|
||||
readsVariable(instr, checkedVar) and
|
||||
any(IRGuards::IRGuardCondition guard).ensuresEq(access, _, _, instr.getBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// Block flow if there's an upper bound check of the variable anywhere in the program
|
||||
exists(Variable checkedVar, Instruction instr | instr = node.asInstruction() |
|
||||
readsVariable(instr, checkedVar) and
|
||||
hasUpperBoundsCheck(checkedVar)
|
||||
)
|
||||
or
|
||||
// Block flow if the node is guarded by an equality check
|
||||
exists(Variable checkedVar, Operand access |
|
||||
nodeIsBarrierEqualityCandidate(node, access, checkedVar) and
|
||||
readsVariable(access.getDef(), checkedVar)
|
||||
)
|
||||
or
|
||||
// Block flow to any binary instruction whose operands are both non-constants.
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not constantInstruction(iTo.getLeft()) and
|
||||
not constantInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of constantness
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink, string kind, string sourceType
|
||||
from Expr use, Expr origin, string kind
|
||||
where
|
||||
Flow::flow(source, sink) and
|
||||
isSource(source, sourceType) and
|
||||
isSink(sink, kind)
|
||||
select sink, "$@ flows an expression which might " + kind + ".", source, sourceType
|
||||
not use.getUnspecifiedType() instanceof PointerType and
|
||||
outOfBoundsExpr(use, kind) and
|
||||
tainted(origin, use) and
|
||||
origin != use and
|
||||
not inSystemMacroExpansion(use) and
|
||||
// Avoid double-counting: don't include all the conversions of `use`.
|
||||
not use instanceof Conversion
|
||||
select use, "$@ flows an expression which might " + kind + ".", origin, "User-provided value"
|
||||
|
||||
@@ -12,10 +12,8 @@
|
||||
* external/cwe/cwe-290
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.TaintTracking
|
||||
import semmle.code.cpp.security.FlowSources as FS
|
||||
import Flow::PathGraph
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
string getATopLevelDomain() {
|
||||
result =
|
||||
@@ -62,26 +60,13 @@ predicate hardCodedAddressInCondition(Expr subexpression, Expr condition) {
|
||||
condition = any(IfStmt ifStmt).getCondition()
|
||||
}
|
||||
|
||||
predicate isSource(FS::FlowSource source, string sourceType) { source.getSourceType() = sourceType }
|
||||
|
||||
predicate isSink(DataFlow::Node sink, Expr condition) {
|
||||
hardCodedAddressInCondition([sink.asExpr(), sink.asIndirectExpr()], condition)
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element sink) { hardCodedAddressInCondition(sink, _) }
|
||||
}
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(sink, _) }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
from
|
||||
Expr subexpression, Expr condition, Flow::PathNode source, Flow::PathNode sink, string sourceType
|
||||
from Expr subexpression, Expr source, Expr condition, PathNode sourceNode, PathNode sinkNode
|
||||
where
|
||||
hardCodedAddressInCondition(subexpression, condition) and
|
||||
isSource(source.getNode(), sourceType) and
|
||||
Flow::flowPath(source, sink) and
|
||||
isSink(sink.getNode(), condition)
|
||||
select condition, source, sink, "Untrusted input $@ might be vulnerable to a spoofing attack.",
|
||||
source, sourceType
|
||||
taintedWithPath(source, subexpression, sourceNode, sinkNode)
|
||||
select condition, sourceNode, sinkNode,
|
||||
"Untrusted input $@ might be vulnerable to a spoofing attack.", source, source.toString()
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>Calling <code>c_str</code> on a <code>std::string</code> object returns a pointer to the underlying character array.
|
||||
When the <code>std::string</code> object is destroyed, the pointer returned by <code>c_str</code> is no
|
||||
longer valid. If the pointer is used after the <code>std::string</code> object is destroyed, then the behavior is undefined.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Ensure that the pointer returned by <code>c_str</code> does not outlive the underlying <code>std::string</code> object.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>
|
||||
The following example concatenates two <code>std::string</code> objects, and then converts the resulting string to a
|
||||
C string using <code>c_str</code> so that it can be passed to the <code>work</code> function.
|
||||
|
||||
However, the underlying <code>std::string</code> object that represents the concatenated string is destroyed as soon as the call
|
||||
to <code>c_str</code> returns. This means that <code>work</code> is given a pointer to invalid memory.
|
||||
</p>
|
||||
|
||||
<sample src="UseOfStringAfterLifetimeEndsBad.cpp" />
|
||||
|
||||
<p>
|
||||
The following example fixes the above code by ensuring that the pointer returned by the call to <code>c_str</code> does
|
||||
not outlive the underlying <code>std::string</code> objects. This ensures that the pointer passed to <code>work</code>
|
||||
points to valid memory.
|
||||
</p>
|
||||
|
||||
<sample src="UseOfStringAfterLifetimeEndsGood.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/MEM50-CPP.+Do+not+access+freed+memory">MEM50-CPP. Do not access freed memory</a>.</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,100 +0,0 @@
|
||||
/**
|
||||
* @name Use of string after lifetime ends
|
||||
* @description If the value of a call to 'c_str' outlives the underlying object it may lead to unexpected behavior.
|
||||
* @kind problem
|
||||
* @precision high
|
||||
* @id cpp/use-of-string-after-lifetime-ends
|
||||
* @problem.severity warning
|
||||
* @security-severity 8.8
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-416
|
||||
* external/cwe/cwe-664
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.implementations.StdString
|
||||
import semmle.code.cpp.models.implementations.StdContainer
|
||||
|
||||
/**
|
||||
* Holds if `e` will be consumed by its parent as a glvalue and does not have
|
||||
* an lvalue-to-rvalue conversion. This means that it will be materialized into
|
||||
* a temporary object.
|
||||
*/
|
||||
predicate isTemporary(Expr e) {
|
||||
e instanceof TemporaryObjectExpr
|
||||
or
|
||||
e.isPRValueCategory() and
|
||||
e.getUnspecifiedType() instanceof Class and
|
||||
not e.hasLValueToRValueConversion()
|
||||
}
|
||||
|
||||
/** Holds if `e` is written to a container. */
|
||||
predicate isStoredInContainer(Expr e) {
|
||||
exists(StdSequenceContainerInsert insert, Call call, int index |
|
||||
call = insert.getACallToThisFunction() and
|
||||
index = insert.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceContainerPush push, Call call, int index |
|
||||
call = push.getACallToThisFunction() and
|
||||
index = push.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceEmplace emplace, Call call, int index |
|
||||
call = emplace.getACallToThisFunction() and
|
||||
index = emplace.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
or
|
||||
exists(StdSequenceEmplaceBack emplaceBack, Call call, int index |
|
||||
call = emplaceBack.getACallToThisFunction() and
|
||||
index = emplaceBack.getAValueTypeParameterIndex() and
|
||||
call.getArgument(index) = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the value of `e` outlives the enclosing full expression. For
|
||||
* example, because the value is stored in a local variable.
|
||||
*/
|
||||
predicate outlivesFullExpr(Expr e) {
|
||||
any(Assignment assign).getRValue() = e
|
||||
or
|
||||
any(Variable v).getInitializer().getExpr() = e
|
||||
or
|
||||
any(ReturnStmt ret).getExpr() = e
|
||||
or
|
||||
exists(ConditionalExpr cond |
|
||||
outlivesFullExpr(cond) and
|
||||
[cond.getThen(), cond.getElse()] = e
|
||||
)
|
||||
or
|
||||
exists(BinaryOperation bin |
|
||||
outlivesFullExpr(bin) and
|
||||
bin.getAnOperand() = e
|
||||
)
|
||||
or
|
||||
exists(ClassAggregateLiteral aggr |
|
||||
outlivesFullExpr(aggr) and
|
||||
aggr.getAFieldExpr(_) = e
|
||||
)
|
||||
or
|
||||
exists(ArrayAggregateLiteral aggr |
|
||||
outlivesFullExpr(aggr) and
|
||||
aggr.getAnElementExpr(_) = e
|
||||
)
|
||||
or
|
||||
isStoredInContainer(e)
|
||||
}
|
||||
|
||||
from Call c
|
||||
where
|
||||
outlivesFullExpr(c) and
|
||||
not c.isFromUninstantiatedTemplate(_) and
|
||||
(c.getTarget() instanceof StdStringCStr or c.getTarget() instanceof StdStringData) and
|
||||
isTemporary(c.getQualifier().getFullyConverted())
|
||||
select c,
|
||||
"The underlying string object is destroyed after the call to '" + c.getTarget() + "' returns."
|
||||
@@ -1,9 +0,0 @@
|
||||
#include <string>
|
||||
void work(const char*);
|
||||
|
||||
// BAD: the concatenated string is deallocated when `c_str` returns. So `work`
|
||||
// is given a pointer to invalid memory.
|
||||
void work_with_combined_string_bad(std::string s1, std::string s2) {
|
||||
const char* combined_string = (s1 + s2).c_str();
|
||||
work(combined_string);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
#include <string>
|
||||
void work(const char*);
|
||||
|
||||
// GOOD: the concatenated string outlives the call to `work`. So the pointer
|
||||
// obtainted from `c_str` is valid.
|
||||
void work_with_combined_string_good(std::string s1, std::string s2) {
|
||||
auto combined_string = s1 + s2;
|
||||
work(combined_string.c_str());
|
||||
}
|
||||
@@ -12,12 +12,8 @@
|
||||
* external/cwe/cwe-807
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.ir.IR
|
||||
import Flow::PathGraph
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
predicate sensitiveCondition(Expr condition, Expr raise) {
|
||||
raisesPrivilege(raise) and
|
||||
@@ -27,62 +23,19 @@ predicate sensitiveCondition(Expr condition, Expr raise) {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate constantInstruction(Instruction instr) {
|
||||
instr instanceof ConstantInstruction
|
||||
or
|
||||
instr instanceof StringConstantInstruction
|
||||
or
|
||||
constantInstruction(instr.(UnaryInstruction).getUnary())
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element tainted) { sensitiveCondition(tainted, _) }
|
||||
}
|
||||
|
||||
predicate isSource(FlowSource source, string sourceType) { sourceType = source.getSourceType() }
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { isSource(node, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
sensitiveCondition([node.asExpr(), node.asIndirectExpr()], _)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// Block flow into binary instructions if both operands are non-constant
|
||||
exists(BinaryInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
not constantInstruction(iTo.getLeft()) and
|
||||
not constantInstruction(iTo.getRight()) and
|
||||
// propagate taint from either the pointer or the offset, regardless of constant-ness
|
||||
not iTo instanceof PointerArithmeticInstruction
|
||||
)
|
||||
or
|
||||
// Block flow through calls to pure functions if two or more operands are non-constant
|
||||
exists(Instruction iFrom1, Instruction iFrom2, CallInstruction iTo |
|
||||
iTo = node.asInstruction() and
|
||||
isPureFunction(iTo.getStaticCallTarget().getName()) and
|
||||
iFrom1 = iTo.getAnArgument() and
|
||||
iFrom2 = iTo.getAnArgument() and
|
||||
not constantInstruction(iFrom1) and
|
||||
not constantInstruction(iFrom2) and
|
||||
iFrom1 != iFrom2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
/*
|
||||
* Produce an alert if there is an 'if' statement whose condition `condition`
|
||||
* is influenced by tainted data `source`, and the body contains
|
||||
* `raise` which escalates privilege.
|
||||
*/
|
||||
|
||||
from
|
||||
Expr raise, string sourceType, DataFlow::Node source, DataFlow::Node sink,
|
||||
Flow::PathNode sourceNode, Flow::PathNode sinkNode
|
||||
from Expr source, Expr condition, Expr raise, PathNode sourceNode, PathNode sinkNode
|
||||
where
|
||||
source = sourceNode.getNode() and
|
||||
sink = sinkNode.getNode() and
|
||||
isSource(source, sourceType) and
|
||||
sensitiveCondition([sink.asExpr(), sink.asIndirectExpr()], raise) and
|
||||
Flow::flowPath(sourceNode, sinkNode)
|
||||
select sink, sourceNode, sinkNode, "Reliance on $@ to raise privilege at $@.", source, sourceType,
|
||||
raise, raise.toString()
|
||||
taintedWithPath(source, condition, sourceNode, sinkNode) and
|
||||
sensitiveCondition(condition, raise)
|
||||
select condition, sourceNode, sinkNode, "Reliance on untrusted input $@ to raise privilege at $@.",
|
||||
source, source.toString(), raise, raise.toString()
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* telemetry
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
4
cpp/ql/src/change-notes/2023-10-31-odbc-models.md
Normal file
4
cpp/ql/src/change-notes/2023-10-31-odbc-models.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added SQL API models for `ODBC`.
|
||||
@@ -1,5 +1,4 @@
|
||||
## 0.8.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `cpp/uninitialized-local` query has been improved to produce fewer false positives.
|
||||
@@ -1,9 +0,0 @@
|
||||
## 0.9.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* The `cpp/tainted-format-string-through-global` query has been deleted. This does not lead to a loss of relevant alerts, as the query duplicated a subset of the alerts from `cpp/tainted-format-string`.
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `cpp/use-of-string-after-lifetime-ends`, to detect calls to `c_str` on strings that will be destroyed immediately.
|
||||
@@ -1,3 +0,0 @@
|
||||
## 0.9.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.9.1
|
||||
lastReleaseVersion: 0.8.2
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.controlflow.Nullness
|
||||
|
||||
class StarOperator extends Operator {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.DataFlow as ASTDataFlow
|
||||
import experimental.cryptography.Concepts
|
||||
|
||||
from HashAlgorithm alg, Expr confSink, string msg
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/all-asymmetric-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/all-cryptographic-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/all-asymmetric-encryption-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/authenticated-encryption-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/block-cipher-mode
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/iv-sources
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/unkown-iv-sources
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/elliptic-curve-key-length
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/elliptic-curve-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/hash-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/key-exchange
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/asymmetric-key-generation
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/signing-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/symmetric-encryption-algorithms
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @kind problem
|
||||
* @id cpp/quantum-readiness/cbom/unkwon-asymmetric-key-generation
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @tags cbom
|
||||
* cryptography
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.9.2-dev
|
||||
version: 0.8.3-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -450,7 +450,6 @@ irGuards
|
||||
| test.c:126:12:126:26 | Call: call to test3_condition |
|
||||
| test.c:131:7:131:7 | Load: b |
|
||||
| test.c:137:7:137:7 | Constant: 0 |
|
||||
| test.c:146:7:146:8 | LogicalNot: ! ... |
|
||||
| test.c:146:8:146:8 | Load: x |
|
||||
| test.c:152:10:152:10 | Load: x |
|
||||
| test.c:152:15:152:15 | Load: y |
|
||||
@@ -641,7 +640,6 @@ irGuardsControl
|
||||
| test.c:126:12:126:26 | Call: call to test3_condition | true | 127 | 127 |
|
||||
| test.c:131:7:131:7 | Load: b | true | 132 | 132 |
|
||||
| test.c:137:7:137:7 | Constant: 0 | false | 142 | 142 |
|
||||
| test.c:146:7:146:8 | LogicalNot: ! ... | true | 147 | 147 |
|
||||
| test.c:146:8:146:8 | Load: x | false | 147 | 147 |
|
||||
| test.c:152:10:152:10 | Load: x | true | 152 | 152 |
|
||||
| test.c:152:15:152:15 | Load: y | true | 152 | 152 |
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
module AstTest {
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
|
||||
* S in `if (guarded(x)) S`.
|
||||
*/
|
||||
// This is tested in `BarrierGuard.cpp`.
|
||||
predicate testBarrierGuard(GuardCondition g, Expr checked, boolean isTrue) {
|
||||
g.(FunctionCall).getTarget().getName() = "guarded" and
|
||||
checked = g.(FunctionCall).getArgument(0) and
|
||||
isTrue = true
|
||||
}
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
module AstTestAllocationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
or
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "indirect_source"
|
||||
or
|
||||
source.(DataFlow::DefinitionByReferenceNode).getParameter().getName().matches("ref_source%")
|
||||
or
|
||||
// Track uninitialized variables
|
||||
exists(source.asUninitialized())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = ["sink", "indirect_sink"] and
|
||||
sink.asExpr() = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") or
|
||||
barrier = DataFlow::BarrierGuard<testBarrierGuard/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
module AstFlow = DataFlow::Global<AstTestAllocationConfig>;
|
||||
}
|
||||
|
||||
module IRTest {
|
||||
private import cpp
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
|
||||
/**
|
||||
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
|
||||
* S in `if (guarded(x)) S`.
|
||||
*/
|
||||
// This is tested in `BarrierGuard.cpp`.
|
||||
predicate testBarrierGuard(IRGuardCondition g, Expr checked, boolean isTrue) {
|
||||
exists(Call call |
|
||||
call = g.getUnconvertedResultExpression() and
|
||||
call.getTarget().hasName("guarded") and
|
||||
checked = call.getArgument(0) and
|
||||
isTrue = true
|
||||
)
|
||||
}
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
module IRTestAllocationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asIndirectExpr(1).(FunctionCall).getTarget().getName() = "indirect_source"
|
||||
or
|
||||
source.asExpr().(StringLiteral).getValue() = "source"
|
||||
or
|
||||
// indirect_source(n) gives the dataflow node representing the indirect node after n dereferences.
|
||||
exists(int n, string s |
|
||||
n = s.regexpCapture("indirect_source\\((\\d)\\)", 1).toInt() and
|
||||
source.asIndirectExpr(n).(StringLiteral).getValue() = s
|
||||
)
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
or
|
||||
source.(DataFlow::DefinitionByReferenceNode).getParameter().getName().matches("ref_source%")
|
||||
or
|
||||
exists(source.asUninitialized())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call, Expr e | e = call.getAnArgument() |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = e
|
||||
or
|
||||
call.getTarget().getName() = "indirect_sink" and
|
||||
sink.asIndirectExpr() = e
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node barrier) {
|
||||
exists(Expr barrierExpr | barrierExpr in [barrier.asExpr(), barrier.asIndirectExpr()] |
|
||||
barrierExpr.(VariableAccess).getTarget().hasName("barrier")
|
||||
)
|
||||
or
|
||||
barrier = DataFlow::BarrierGuard<testBarrierGuard/3>::getABarrierNode()
|
||||
or
|
||||
barrier = DataFlow::BarrierGuard<testBarrierGuard/3>::getAnIndirectBarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
module IRFlow = DataFlow::Global<IRTestAllocationConfig>;
|
||||
}
|
||||
@@ -1,11 +1,5 @@
|
||||
uniqueEnclosingCallable
|
||||
| test.cpp:864:44:864:58 | {...} | Node should have one enclosing callable but has 0. |
|
||||
| test.cpp:864:47:864:54 | call to source | Node should have one enclosing callable but has 0. |
|
||||
| test.cpp:872:46:872:51 | call to source | Node should have one enclosing callable but has 0. |
|
||||
| test.cpp:872:53:872:56 | 1 | Node should have one enclosing callable but has 0. |
|
||||
uniqueCallEnclosingCallable
|
||||
| test.cpp:864:47:864:54 | call to source | Call should have one enclosing callable but has 0. |
|
||||
| test.cpp:872:46:872:51 | call to source | Call should have one enclosing callable but has 0. |
|
||||
uniqueType
|
||||
uniqueNodeLocation
|
||||
missingLocation
|
||||
@@ -30,7 +24,6 @@ argHasPostUpdate
|
||||
| lambdas.cpp:45:2:45:2 | e | ArgumentNode is missing PostUpdateNode. |
|
||||
| test.cpp:67:29:67:35 | source1 | ArgumentNode is missing PostUpdateNode. |
|
||||
| test.cpp:813:19:813:35 | * ... | ArgumentNode is missing PostUpdateNode. |
|
||||
| test.cpp:848:23:848:25 | rpx | ArgumentNode is missing PostUpdateNode. |
|
||||
postWithInFlow
|
||||
| BarrierGuard.cpp:49:6:49:6 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| BarrierGuard.cpp:60:7:60:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
WARNING: Module DataFlow has been deprecated and may be removed in future (test-source-sink.ql:3,25-42)
|
||||
WARNING: Module DataFlow has been deprecated and may be removed in future (test-source-sink.ql:3,57-74)
|
||||
astFlow
|
||||
| BarrierGuard.cpp:5:19:5:24 | source | BarrierGuard.cpp:9:10:9:15 | source |
|
||||
| BarrierGuard.cpp:13:17:13:22 | source | BarrierGuard.cpp:15:10:15:15 | source |
|
||||
| BarrierGuard.cpp:21:17:21:22 | source | BarrierGuard.cpp:25:10:25:15 | source |
|
||||
| BarrierGuard.cpp:29:16:29:21 | source | BarrierGuard.cpp:31:10:31:15 | source |
|
||||
| BarrierGuard.cpp:29:16:29:21 | source | BarrierGuard.cpp:33:10:33:15 | source |
|
||||
| BarrierGuard.cpp:49:10:49:15 | call to source | BarrierGuard.cpp:51:13:51:13 | x |
|
||||
| BarrierGuard.cpp:49:10:49:15 | call to source | BarrierGuard.cpp:53:13:53:13 | x |
|
||||
| BarrierGuard.cpp:49:10:49:15 | call to source | BarrierGuard.cpp:55:13:55:13 | x |
|
||||
| BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:62:14:62:14 | x |
|
||||
| BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:64:14:64:14 | x |
|
||||
| BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:66:14:66:14 | x |
|
||||
| acrossLinkTargets.cpp:19:27:19:32 | call to source | acrossLinkTargets.cpp:12:8:12:8 | x |
|
||||
| clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:18:8:18:19 | sourceArray1 |
|
||||
| clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:22:8:22:20 | & ... |
|
||||
| clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:23:17:23:29 | & ... |
|
||||
| clang.cpp:29:27:29:32 | call to source | clang.cpp:30:27:30:28 | m1 |
|
||||
| clang.cpp:29:27:29:32 | call to source | clang.cpp:31:27:31:34 | call to getFirst |
|
||||
| clang.cpp:35:32:35:37 | call to source | clang.cpp:38:10:38:11 | m2 |
|
||||
| clang.cpp:44:35:44:40 | call to source | clang.cpp:46:17:46:18 | m2 |
|
||||
| clang.cpp:51:19:51:24 | call to source | clang.cpp:52:8:52:17 | stackArray |
|
||||
| clang.cpp:51:19:51:24 | call to source | clang.cpp:53:17:53:26 | stackArray |
|
||||
| dispatch.cpp:9:37:9:42 | call to source | dispatch.cpp:35:16:35:25 | call to notSource1 |
|
||||
| dispatch.cpp:9:37:9:42 | call to source | dispatch.cpp:43:15:43:24 | call to notSource1 |
|
||||
| dispatch.cpp:10:37:10:42 | call to source | dispatch.cpp:36:16:36:25 | call to notSource2 |
|
||||
| dispatch.cpp:10:37:10:42 | call to source | dispatch.cpp:44:15:44:24 | call to notSource2 |
|
||||
| dispatch.cpp:37:19:37:24 | call to source | dispatch.cpp:11:38:11:38 | x |
|
||||
| dispatch.cpp:45:18:45:23 | call to source | dispatch.cpp:11:38:11:38 | x |
|
||||
| globals.cpp:5:17:5:22 | call to source | globals.cpp:6:10:6:14 | local |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:14:3:14:6 | t |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:18:8:18:8 | call to operator() |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:21:3:21:6 | t |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:29:3:29:6 | t |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:35:8:35:8 | a |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:41:8:41:8 | a |
|
||||
| lambdas.cpp:43:7:43:12 | call to source | lambdas.cpp:46:7:46:7 | w |
|
||||
| ref.cpp:29:11:29:16 | call to source | ref.cpp:62:10:62:11 | x3 |
|
||||
| ref.cpp:53:9:53:10 | x1 | ref.cpp:56:10:56:11 | x1 |
|
||||
| ref.cpp:53:13:53:14 | x2 | ref.cpp:59:10:59:11 | x2 |
|
||||
| ref.cpp:53:17:53:18 | x3 | ref.cpp:62:10:62:11 | x3 |
|
||||
| ref.cpp:53:21:53:22 | x4 | ref.cpp:65:10:65:11 | x4 |
|
||||
| ref.cpp:55:23:55:28 | call to source | ref.cpp:56:10:56:11 | x1 |
|
||||
| ref.cpp:94:15:94:20 | call to source | ref.cpp:129:13:129:15 | val |
|
||||
| ref.cpp:109:15:109:20 | call to source | ref.cpp:132:13:132:15 | val |
|
||||
| ref.cpp:122:23:122:28 | call to source | ref.cpp:123:13:123:15 | val |
|
||||
| ref.cpp:125:19:125:24 | call to source | ref.cpp:126:13:126:15 | val |
|
||||
| self-Iterator.cpp:19:23:19:28 | call to source | self-Iterator.cpp:20:10:20:10 | x |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:7:8:7:9 | t1 |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:9:8:9:9 | t1 |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:10:8:10:9 | t2 |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:15:8:15:9 | t2 |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:26:8:26:9 | t1 |
|
||||
| test.cpp:35:10:35:15 | call to source | test.cpp:30:8:30:8 | t |
|
||||
| test.cpp:36:13:36:18 | call to source | test.cpp:31:8:31:8 | c |
|
||||
| test.cpp:50:14:50:19 | call to source | test.cpp:58:10:58:10 | t |
|
||||
| test.cpp:66:30:66:36 | source1 | test.cpp:71:8:71:9 | x4 |
|
||||
| test.cpp:75:7:75:8 | u1 | test.cpp:76:8:76:9 | u1 |
|
||||
| test.cpp:83:7:83:8 | u2 | test.cpp:84:8:84:18 | ... ? ... : ... |
|
||||
| test.cpp:83:7:83:8 | u2 | test.cpp:86:8:86:9 | i1 |
|
||||
| test.cpp:89:28:89:34 | source1 | test.cpp:90:8:90:14 | source1 |
|
||||
| test.cpp:100:13:100:18 | call to source | test.cpp:103:10:103:12 | ref |
|
||||
| test.cpp:138:27:138:32 | call to source | test.cpp:140:8:140:8 | y |
|
||||
| test.cpp:151:33:151:38 | call to source | test.cpp:144:8:144:8 | s |
|
||||
| test.cpp:151:33:151:38 | call to source | test.cpp:152:8:152:8 | y |
|
||||
| test.cpp:164:34:164:39 | call to source | test.cpp:157:8:157:8 | x |
|
||||
| test.cpp:164:34:164:39 | call to source | test.cpp:165:8:165:8 | y |
|
||||
| test.cpp:171:11:171:16 | call to source | test.cpp:178:8:178:8 | y |
|
||||
| test.cpp:245:14:245:19 | call to source | test.cpp:260:12:260:12 | x |
|
||||
| test.cpp:265:22:265:27 | call to source | test.cpp:266:12:266:12 | x |
|
||||
| test.cpp:305:17:305:22 | call to source | test.cpp:289:14:289:14 | x |
|
||||
| test.cpp:314:4:314:9 | call to source | test.cpp:318:7:318:7 | x |
|
||||
| test.cpp:347:17:347:22 | call to source | test.cpp:349:10:349:18 | globalVar |
|
||||
| test.cpp:359:13:359:18 | call to source | test.cpp:365:10:365:14 | field |
|
||||
| test.cpp:373:13:373:18 | call to source | test.cpp:369:10:369:14 | field |
|
||||
| test.cpp:373:13:373:18 | call to source | test.cpp:375:10:375:14 | field |
|
||||
| test.cpp:382:48:382:54 | source1 | test.cpp:385:8:385:10 | tmp |
|
||||
| test.cpp:388:53:388:59 | source1 | test.cpp:392:8:392:10 | tmp |
|
||||
| test.cpp:388:53:388:59 | source1 | test.cpp:394:10:394:12 | tmp |
|
||||
| test.cpp:399:7:399:9 | tmp | test.cpp:401:8:401:10 | tmp |
|
||||
| test.cpp:405:7:405:9 | tmp | test.cpp:408:8:408:10 | tmp |
|
||||
| test.cpp:416:7:416:11 | local | test.cpp:418:8:418:12 | local |
|
||||
| test.cpp:417:16:417:20 | ref arg local | test.cpp:418:8:418:12 | local |
|
||||
| test.cpp:422:7:422:11 | local | test.cpp:424:8:424:12 | local |
|
||||
| test.cpp:423:20:423:25 | ref arg & ... | test.cpp:424:8:424:12 | local |
|
||||
| test.cpp:433:7:433:11 | local | test.cpp:435:8:435:12 | local |
|
||||
| test.cpp:433:7:433:11 | local | test.cpp:436:8:436:13 | * ... |
|
||||
| test.cpp:434:20:434:24 | ref arg local | test.cpp:435:8:435:12 | local |
|
||||
| test.cpp:434:20:434:24 | ref arg local | test.cpp:436:8:436:13 | * ... |
|
||||
| test.cpp:440:7:440:11 | local | test.cpp:442:8:442:12 | local |
|
||||
| test.cpp:441:18:441:23 | ref arg & ... | test.cpp:442:8:442:12 | local |
|
||||
| test.cpp:448:7:448:11 | local | test.cpp:450:8:450:12 | local |
|
||||
| test.cpp:448:7:448:11 | local | test.cpp:451:8:451:13 | * ... |
|
||||
| test.cpp:449:18:449:22 | ref arg local | test.cpp:450:8:450:12 | local |
|
||||
| test.cpp:449:18:449:22 | ref arg local | test.cpp:451:8:451:13 | * ... |
|
||||
| test.cpp:456:26:456:32 | source1 | test.cpp:457:9:457:22 | (statement expression) |
|
||||
| test.cpp:456:26:456:32 | source1 | test.cpp:468:8:468:12 | local |
|
||||
| test.cpp:472:8:472:13 | call to source | test.cpp:478:8:478:8 | x |
|
||||
| test.cpp:506:8:506:13 | call to source | test.cpp:513:8:513:8 | x |
|
||||
| test.cpp:517:7:517:16 | stackArray | test.cpp:521:8:521:20 | access to array |
|
||||
| test.cpp:519:19:519:24 | call to source | test.cpp:521:8:521:20 | access to array |
|
||||
| test.cpp:551:9:551:9 | y | test.cpp:541:10:541:10 | y |
|
||||
| test.cpp:583:11:583:16 | call to source | test.cpp:590:8:590:8 | x |
|
||||
| test.cpp:628:20:628:25 | ref arg buffer | test.cpp:629:17:629:22 | buffer |
|
||||
| test.cpp:633:18:633:23 | call to source | test.cpp:634:8:634:8 | x |
|
||||
| test.cpp:702:38:702:43 | source | test.cpp:695:8:695:10 | buf |
|
||||
| test.cpp:726:11:726:16 | call to source | test.cpp:735:8:735:8 | x |
|
||||
| test.cpp:733:7:733:7 | x | test.cpp:735:8:735:8 | x |
|
||||
| test.cpp:749:27:749:32 | call to source | test.cpp:740:10:740:10 | x |
|
||||
| test.cpp:751:27:751:32 | call to source | test.cpp:740:10:740:10 | x |
|
||||
| test.cpp:753:32:753:37 | call to source | test.cpp:740:10:740:10 | x |
|
||||
| test.cpp:755:32:755:37 | call to source | test.cpp:740:10:740:10 | x |
|
||||
| test.cpp:769:27:769:32 | call to source | test.cpp:760:10:760:10 | x |
|
||||
| test.cpp:771:27:771:32 | call to source | test.cpp:760:10:760:10 | x |
|
||||
| test.cpp:773:32:773:37 | call to source | test.cpp:760:10:760:10 | x |
|
||||
| test.cpp:775:32:775:37 | call to source | test.cpp:760:10:760:10 | x |
|
||||
| test.cpp:788:31:788:36 | call to source | test.cpp:782:12:782:12 | x |
|
||||
| test.cpp:790:31:790:36 | call to source | test.cpp:782:12:782:12 | x |
|
||||
| test.cpp:797:22:797:28 | ref arg content | test.cpp:798:19:798:25 | content |
|
||||
| test.cpp:842:11:842:16 | call to source | test.cpp:844:8:844:8 | y |
|
||||
| test.cpp:846:13:846:27 | call to indirect_source | test.cpp:848:23:848:25 | rpx |
|
||||
| test.cpp:860:54:860:59 | call to source | test.cpp:861:10:861:37 | static_local_pointer_dynamic |
|
||||
| true_upon_entry.cpp:17:11:17:16 | call to source | true_upon_entry.cpp:21:8:21:8 | x |
|
||||
| true_upon_entry.cpp:27:9:27:14 | call to source | true_upon_entry.cpp:29:8:29:8 | x |
|
||||
| true_upon_entry.cpp:33:11:33:16 | call to source | true_upon_entry.cpp:39:8:39:8 | x |
|
||||
| true_upon_entry.cpp:43:11:43:16 | call to source | true_upon_entry.cpp:49:8:49:8 | x |
|
||||
| true_upon_entry.cpp:54:11:54:16 | call to source | true_upon_entry.cpp:57:8:57:8 | x |
|
||||
| true_upon_entry.cpp:70:11:70:16 | call to source | true_upon_entry.cpp:78:8:78:8 | x |
|
||||
| true_upon_entry.cpp:83:11:83:16 | call to source | true_upon_entry.cpp:86:8:86:8 | x |
|
||||
irFlow
|
||||
| BarrierGuard.cpp:5:19:5:24 | source | BarrierGuard.cpp:9:10:9:15 | source |
|
||||
| BarrierGuard.cpp:13:17:13:22 | source | BarrierGuard.cpp:15:10:15:15 | source |
|
||||
| BarrierGuard.cpp:21:17:21:22 | source | BarrierGuard.cpp:25:10:25:15 | source |
|
||||
| BarrierGuard.cpp:29:16:29:21 | source | BarrierGuard.cpp:31:10:31:15 | source |
|
||||
| BarrierGuard.cpp:29:16:29:21 | source | BarrierGuard.cpp:33:10:33:15 | source |
|
||||
| BarrierGuard.cpp:49:10:49:15 | call to source | BarrierGuard.cpp:53:13:53:13 | x |
|
||||
| BarrierGuard.cpp:49:10:49:15 | call to source | BarrierGuard.cpp:55:13:55:13 | x |
|
||||
| BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:64:14:64:14 | x |
|
||||
| BarrierGuard.cpp:60:11:60:16 | call to source | BarrierGuard.cpp:66:14:66:14 | x |
|
||||
| acrossLinkTargets.cpp:19:27:19:32 | call to source | acrossLinkTargets.cpp:12:8:12:8 | x |
|
||||
| clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:18:8:18:19 | sourceArray1 |
|
||||
| clang.cpp:12:9:12:20 | sourceArray1 | clang.cpp:23:17:23:29 | & ... indirection |
|
||||
| clang.cpp:29:27:29:32 | call to source | clang.cpp:30:27:30:28 | m1 |
|
||||
| clang.cpp:29:27:29:32 | call to source | clang.cpp:31:27:31:34 | call to getFirst |
|
||||
| clang.cpp:35:32:35:37 | call to source | clang.cpp:38:10:38:11 | m2 |
|
||||
| clang.cpp:40:42:40:47 | call to source | clang.cpp:42:18:42:19 | m2 |
|
||||
| clang.cpp:44:35:44:40 | call to source | clang.cpp:46:17:46:18 | m2 |
|
||||
| clang.cpp:50:7:50:16 | definition of stackArray | clang.cpp:52:8:52:17 | stackArray |
|
||||
| clang.cpp:50:25:50:30 | call to source | clang.cpp:53:17:53:26 | stackArray indirection |
|
||||
| clang.cpp:50:35:50:40 | call to source | clang.cpp:53:17:53:26 | stackArray indirection |
|
||||
| clang.cpp:51:19:51:24 | call to source | clang.cpp:53:17:53:26 | stackArray indirection |
|
||||
| dispatch.cpp:9:37:9:42 | call to source | dispatch.cpp:35:16:35:25 | call to notSource1 |
|
||||
| dispatch.cpp:9:37:9:42 | call to source | dispatch.cpp:43:15:43:24 | call to notSource1 |
|
||||
| dispatch.cpp:10:37:10:42 | call to source | dispatch.cpp:36:16:36:25 | call to notSource2 |
|
||||
| dispatch.cpp:10:37:10:42 | call to source | dispatch.cpp:44:15:44:24 | call to notSource2 |
|
||||
| dispatch.cpp:16:37:16:42 | call to source | dispatch.cpp:32:16:32:24 | call to isSource2 |
|
||||
| dispatch.cpp:16:37:16:42 | call to source | dispatch.cpp:40:15:40:23 | call to isSource2 |
|
||||
| dispatch.cpp:22:37:22:42 | call to source | dispatch.cpp:31:16:31:24 | call to isSource1 |
|
||||
| dispatch.cpp:22:37:22:42 | call to source | dispatch.cpp:39:15:39:23 | call to isSource1 |
|
||||
| dispatch.cpp:22:37:22:42 | call to source | dispatch.cpp:55:22:55:30 | call to isSource1 |
|
||||
| dispatch.cpp:22:37:22:42 | call to source | dispatch.cpp:58:28:58:36 | call to isSource1 |
|
||||
| dispatch.cpp:33:18:33:23 | call to source | dispatch.cpp:23:38:23:38 | x |
|
||||
| dispatch.cpp:37:19:37:24 | call to source | dispatch.cpp:11:38:11:38 | x |
|
||||
| dispatch.cpp:41:17:41:22 | call to source | dispatch.cpp:23:38:23:38 | x |
|
||||
| dispatch.cpp:45:18:45:23 | call to source | dispatch.cpp:11:38:11:38 | x |
|
||||
| dispatch.cpp:69:15:69:20 | call to source | dispatch.cpp:23:38:23:38 | x |
|
||||
| dispatch.cpp:73:14:73:19 | call to source | dispatch.cpp:23:38:23:38 | x |
|
||||
| dispatch.cpp:81:13:81:18 | call to source | dispatch.cpp:23:38:23:38 | x |
|
||||
| dispatch.cpp:107:17:107:22 | call to source | dispatch.cpp:96:8:96:8 | x |
|
||||
| dispatch.cpp:140:8:140:13 | call to source | dispatch.cpp:96:8:96:8 | x |
|
||||
| dispatch.cpp:144:8:144:13 | call to source | dispatch.cpp:96:8:96:8 | x |
|
||||
| flowOut.cpp:5:16:5:21 | call to source | flowOut.cpp:19:9:19:9 | x |
|
||||
| globals.cpp:5:17:5:22 | call to source | globals.cpp:6:10:6:14 | local |
|
||||
| globals.cpp:13:23:13:28 | call to source | globals.cpp:12:10:12:24 | flowTestGlobal1 |
|
||||
| globals.cpp:23:23:23:28 | call to source | globals.cpp:19:10:19:24 | flowTestGlobal2 |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:14:8:14:8 | t |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:18:8:18:8 | call to operator() |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:21:8:21:8 | t |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:29:8:29:8 | t |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:35:8:35:8 | a |
|
||||
| lambdas.cpp:8:10:8:15 | call to source | lambdas.cpp:41:8:41:8 | a |
|
||||
| lambdas.cpp:43:7:43:12 | call to source | lambdas.cpp:46:7:46:7 | w |
|
||||
| ref.cpp:29:11:29:16 | call to source | ref.cpp:62:10:62:11 | x3 |
|
||||
| ref.cpp:53:9:53:10 | definition of x1 | ref.cpp:56:10:56:11 | x1 |
|
||||
| ref.cpp:53:13:53:14 | definition of x2 | ref.cpp:59:10:59:11 | x2 |
|
||||
| ref.cpp:53:17:53:18 | definition of x3 | ref.cpp:62:10:62:11 | x3 |
|
||||
| ref.cpp:53:21:53:22 | definition of x4 | ref.cpp:65:10:65:11 | x4 |
|
||||
| ref.cpp:55:23:55:28 | call to source | ref.cpp:56:10:56:11 | x1 |
|
||||
| ref.cpp:94:15:94:20 | call to source | ref.cpp:129:13:129:15 | val |
|
||||
| ref.cpp:109:15:109:20 | call to source | ref.cpp:132:13:132:15 | val |
|
||||
| ref.cpp:122:23:122:28 | call to source | ref.cpp:123:13:123:15 | val |
|
||||
| ref.cpp:125:19:125:24 | call to source | ref.cpp:126:13:126:15 | val |
|
||||
| self-Iterator.cpp:19:23:19:30 | call to source | self-Iterator.cpp:20:10:20:10 | x |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:7:8:7:9 | t1 |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:9:8:9:9 | t1 |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:10:8:10:9 | t2 |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:15:8:15:9 | t2 |
|
||||
| test.cpp:6:12:6:17 | call to source | test.cpp:26:8:26:9 | t1 |
|
||||
| test.cpp:35:10:35:15 | call to source | test.cpp:30:8:30:8 | t |
|
||||
| test.cpp:36:13:36:18 | call to source | test.cpp:31:8:31:8 | c |
|
||||
| test.cpp:50:14:50:19 | call to source | test.cpp:58:10:58:10 | t |
|
||||
| test.cpp:66:30:66:36 | source1 | test.cpp:71:8:71:9 | x4 |
|
||||
| test.cpp:75:7:75:8 | definition of u1 | test.cpp:76:8:76:9 | u1 |
|
||||
| test.cpp:83:7:83:8 | definition of u2 | test.cpp:84:8:84:18 | ... ? ... : ... |
|
||||
| test.cpp:83:7:83:8 | definition of u2 | test.cpp:86:8:86:9 | i1 |
|
||||
| test.cpp:89:28:89:34 | source1 indirection | test.cpp:90:8:90:14 | source1 |
|
||||
| test.cpp:100:13:100:18 | call to source | test.cpp:103:10:103:12 | ref |
|
||||
| test.cpp:138:27:138:32 | call to source | test.cpp:140:8:140:8 | y |
|
||||
| test.cpp:151:33:151:38 | call to source | test.cpp:144:8:144:8 | s |
|
||||
| test.cpp:151:33:151:38 | call to source | test.cpp:152:8:152:8 | y |
|
||||
| test.cpp:164:34:164:39 | call to source | test.cpp:157:8:157:8 | x |
|
||||
| test.cpp:164:34:164:39 | call to source | test.cpp:165:8:165:8 | y |
|
||||
| test.cpp:171:11:171:16 | call to source | test.cpp:178:8:178:8 | y |
|
||||
| test.cpp:245:14:245:19 | call to source | test.cpp:260:12:260:12 | x |
|
||||
| test.cpp:265:22:265:27 | call to source | test.cpp:266:12:266:12 | x |
|
||||
| test.cpp:305:17:305:22 | call to source | test.cpp:289:14:289:14 | x |
|
||||
| test.cpp:314:4:314:9 | call to source | test.cpp:318:7:318:7 | x |
|
||||
| test.cpp:333:17:333:22 | call to source | test.cpp:337:10:337:18 | globalVar |
|
||||
| test.cpp:333:17:333:22 | call to source | test.cpp:339:10:339:18 | globalVar |
|
||||
| test.cpp:333:17:333:22 | call to source | test.cpp:343:10:343:18 | globalVar |
|
||||
| test.cpp:333:17:333:22 | call to source | test.cpp:349:10:349:18 | globalVar |
|
||||
| test.cpp:347:17:347:22 | call to source | test.cpp:337:10:337:18 | globalVar |
|
||||
| test.cpp:347:17:347:22 | call to source | test.cpp:339:10:339:18 | globalVar |
|
||||
| test.cpp:347:17:347:22 | call to source | test.cpp:343:10:343:18 | globalVar |
|
||||
| test.cpp:347:17:347:22 | call to source | test.cpp:349:10:349:18 | globalVar |
|
||||
| test.cpp:359:13:359:18 | call to source | test.cpp:365:10:365:14 | field |
|
||||
| test.cpp:373:13:373:18 | call to source | test.cpp:369:10:369:14 | field |
|
||||
| test.cpp:373:13:373:18 | call to source | test.cpp:375:10:375:14 | field |
|
||||
| test.cpp:382:48:382:54 | source1 | test.cpp:385:8:385:10 | tmp |
|
||||
| test.cpp:388:53:388:59 | source1 | test.cpp:392:8:392:10 | tmp |
|
||||
| test.cpp:388:53:388:59 | source1 | test.cpp:394:10:394:12 | tmp |
|
||||
| test.cpp:399:7:399:9 | definition of tmp | test.cpp:401:8:401:10 | tmp |
|
||||
| test.cpp:405:7:405:9 | definition of tmp | test.cpp:408:8:408:10 | tmp |
|
||||
| test.cpp:416:7:416:11 | definition of local | test.cpp:418:8:418:12 | local |
|
||||
| test.cpp:417:16:417:20 | intRefSource output argument | test.cpp:418:8:418:12 | local |
|
||||
| test.cpp:422:7:422:11 | definition of local | test.cpp:424:8:424:12 | local |
|
||||
| test.cpp:423:20:423:25 | intPointerSource output argument | test.cpp:424:8:424:12 | local |
|
||||
| test.cpp:433:7:433:11 | definition of local | test.cpp:435:8:435:12 | local |
|
||||
| test.cpp:434:20:434:24 | intPointerSource output argument | test.cpp:436:8:436:13 | * ... |
|
||||
| test.cpp:440:7:440:11 | definition of local | test.cpp:442:8:442:12 | local |
|
||||
| test.cpp:441:18:441:23 | intArraySource output argument | test.cpp:442:8:442:12 | local |
|
||||
| test.cpp:448:7:448:11 | definition of local | test.cpp:450:8:450:12 | local |
|
||||
| test.cpp:449:18:449:22 | intArraySource output argument | test.cpp:451:8:451:13 | * ... |
|
||||
| test.cpp:456:26:456:32 | source1 | test.cpp:457:9:457:22 | (statement expression) |
|
||||
| test.cpp:456:26:456:32 | source1 | test.cpp:468:8:468:12 | local |
|
||||
| test.cpp:472:8:472:13 | call to source | test.cpp:478:8:478:8 | x |
|
||||
| test.cpp:506:8:506:13 | call to source | test.cpp:513:8:513:8 | x |
|
||||
| test.cpp:519:19:519:24 | call to source | test.cpp:521:8:521:20 | access to array |
|
||||
| test.cpp:531:29:531:34 | call to source | test.cpp:532:8:532:9 | * ... |
|
||||
| test.cpp:547:9:547:9 | definition of x | test.cpp:536:10:536:11 | * ... |
|
||||
| test.cpp:551:9:551:9 | definition of y | test.cpp:541:10:541:10 | y |
|
||||
| test.cpp:562:17:562:31 | call to indirect_source indirection | test.cpp:566:10:566:19 | * ... |
|
||||
| test.cpp:562:17:562:31 | call to indirect_source indirection | test.cpp:568:10:568:19 | * ... |
|
||||
| test.cpp:562:17:562:31 | call to indirect_source indirection | test.cpp:572:10:572:19 | * ... |
|
||||
| test.cpp:562:17:562:31 | call to indirect_source indirection | test.cpp:578:10:578:19 | * ... |
|
||||
| test.cpp:576:17:576:31 | call to indirect_source indirection | test.cpp:566:10:566:19 | * ... |
|
||||
| test.cpp:576:17:576:31 | call to indirect_source indirection | test.cpp:568:10:568:19 | * ... |
|
||||
| test.cpp:576:17:576:31 | call to indirect_source indirection | test.cpp:572:10:572:19 | * ... |
|
||||
| test.cpp:576:17:576:31 | call to indirect_source indirection | test.cpp:578:10:578:19 | * ... |
|
||||
| test.cpp:594:12:594:26 | call to indirect_source indirection | test.cpp:597:8:597:13 | * ... |
|
||||
| test.cpp:601:20:601:20 | intPointerSource output argument | test.cpp:603:8:603:9 | * ... |
|
||||
| test.cpp:607:20:607:20 | intPointerSource output argument | test.cpp:609:8:609:9 | * ... |
|
||||
| test.cpp:614:20:614:20 | intPointerSource output argument | test.cpp:616:8:616:17 | * ... |
|
||||
| test.cpp:628:20:628:25 | intPointerSource output argument | test.cpp:629:17:629:22 | buffer indirection |
|
||||
| test.cpp:633:18:633:23 | call to source | test.cpp:634:8:634:8 | x |
|
||||
| test.cpp:646:7:646:12 | call to source | test.cpp:645:8:645:8 | x |
|
||||
| test.cpp:660:7:660:12 | call to source | test.cpp:658:8:658:8 | x |
|
||||
| test.cpp:664:18:664:23 | call to source | test.cpp:666:8:666:16 | * ... |
|
||||
| test.cpp:681:7:681:12 | call to source | test.cpp:679:8:679:16 | * ... |
|
||||
| test.cpp:733:7:733:7 | definition of x | test.cpp:735:8:735:8 | x |
|
||||
| test.cpp:751:27:751:32 | call to source | test.cpp:740:10:740:10 | x |
|
||||
| test.cpp:753:32:753:37 | call to source | test.cpp:740:10:740:10 | x |
|
||||
| test.cpp:755:32:755:37 | call to source | test.cpp:740:10:740:10 | x |
|
||||
| test.cpp:771:27:771:32 | call to source | test.cpp:760:10:760:10 | x |
|
||||
| test.cpp:773:32:773:37 | call to source | test.cpp:760:10:760:10 | x |
|
||||
| test.cpp:775:32:775:37 | call to source | test.cpp:760:10:760:10 | x |
|
||||
| test.cpp:788:31:788:36 | call to source | test.cpp:782:12:782:12 | x |
|
||||
| test.cpp:790:31:790:36 | call to source | test.cpp:782:12:782:12 | x |
|
||||
| test.cpp:797:22:797:28 | intPointerSource output argument | test.cpp:798:19:798:25 | content indirection |
|
||||
| test.cpp:808:25:808:39 | call to indirect_source indirection | test.cpp:813:19:813:35 | * ... indirection |
|
||||
| test.cpp:818:26:818:31 | call to source | test.cpp:823:10:823:27 | * ... |
|
||||
| test.cpp:832:21:832:26 | call to source | test.cpp:836:10:836:22 | global_direct |
|
||||
| test.cpp:842:11:842:16 | call to source | test.cpp:844:8:844:8 | y |
|
||||
| test.cpp:846:13:846:27 | call to indirect_source indirection | test.cpp:848:17:848:25 | rpx indirection |
|
||||
| test.cpp:853:55:853:62 | call to source | test.cpp:854:10:854:36 | * ... |
|
||||
| test.cpp:860:54:860:59 | call to source | test.cpp:861:10:861:37 | static_local_pointer_dynamic |
|
||||
| test.cpp:872:46:872:51 | call to source | test.cpp:875:10:875:31 | global_pointer_dynamic |
|
||||
| test.cpp:880:64:880:83 | indirect_source(1) indirection | test.cpp:883:10:883:45 | static_local_array_static_indirect_1 |
|
||||
| test.cpp:881:64:881:83 | indirect_source(2) indirection | test.cpp:886:19:886:54 | static_local_array_static_indirect_2 indirection |
|
||||
| test.cpp:890:54:890:61 | source | test.cpp:893:10:893:36 | static_local_pointer_static |
|
||||
| test.cpp:891:65:891:84 | indirect_source(1) indirection | test.cpp:895:19:895:56 | static_local_pointer_static_indirect_1 indirection |
|
||||
| test.cpp:901:56:901:75 | indirect_source(1) indirection | test.cpp:907:10:907:39 | global_array_static_indirect_1 |
|
||||
| test.cpp:902:56:902:75 | indirect_source(2) indirection | test.cpp:911:19:911:48 | global_array_static_indirect_2 indirection |
|
||||
| test.cpp:914:46:914:53 | source | test.cpp:919:10:919:30 | global_pointer_static |
|
||||
| test.cpp:915:57:915:76 | indirect_source(1) indirection | test.cpp:921:19:921:50 | global_pointer_static_indirect_1 indirection |
|
||||
| true_upon_entry.cpp:9:11:9:16 | call to source | true_upon_entry.cpp:13:8:13:8 | x |
|
||||
| true_upon_entry.cpp:17:11:17:16 | call to source | true_upon_entry.cpp:21:8:21:8 | x |
|
||||
| true_upon_entry.cpp:27:9:27:14 | call to source | true_upon_entry.cpp:29:8:29:8 | x |
|
||||
| true_upon_entry.cpp:33:11:33:16 | call to source | true_upon_entry.cpp:39:8:39:8 | x |
|
||||
| true_upon_entry.cpp:43:11:43:16 | call to source | true_upon_entry.cpp:49:8:49:8 | x |
|
||||
| true_upon_entry.cpp:54:11:54:16 | call to source | true_upon_entry.cpp:57:8:57:8 | x |
|
||||
| true_upon_entry.cpp:62:11:62:16 | call to source | true_upon_entry.cpp:66:8:66:8 | x |
|
||||
| true_upon_entry.cpp:70:11:70:16 | call to source | true_upon_entry.cpp:78:8:78:8 | x |
|
||||
| true_upon_entry.cpp:83:11:83:16 | call to source | true_upon_entry.cpp:86:8:86:8 | x |
|
||||
| true_upon_entry.cpp:98:11:98:16 | call to source | true_upon_entry.cpp:105:8:105:8 | x |
|
||||
@@ -1,9 +0,0 @@
|
||||
import TestBase
|
||||
|
||||
query predicate astFlow(AstTest::DataFlow::Node source, AstTest::DataFlow::Node sink) {
|
||||
AstTest::AstFlow::flow(source, sink)
|
||||
}
|
||||
|
||||
query predicate irFlow(IRTest::DataFlow::Node source, IRTest::DataFlow::Node sink) {
|
||||
IRTest::IRFlow::flow(source, sink)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
int source();
|
||||
void sink(...); void indirect_sink(...);
|
||||
void sink(int); void sink(const int *); void sink(int **); void indirect_sink(...);
|
||||
|
||||
void intraprocedural_with_local_flow() {
|
||||
int t2;
|
||||
@@ -836,90 +836,4 @@ namespace MoreGlobalTests {
|
||||
sink(global_direct); // $ ir MISSING: ast
|
||||
indirect_sink(global_direct); // clean
|
||||
}
|
||||
}
|
||||
|
||||
void test_references() {
|
||||
int x = source();
|
||||
int &y = x;
|
||||
sink(y); // $ ast,ir
|
||||
|
||||
int* px = indirect_source();
|
||||
int*& rpx = px;
|
||||
indirect_sink((int*)rpx); // $ ast,ir
|
||||
}
|
||||
|
||||
namespace GlobalArrays {
|
||||
void test1() {
|
||||
static const int static_local_array_dynamic[] = { ::source() };
|
||||
sink(*static_local_array_dynamic); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
const int* source(bool);
|
||||
|
||||
void test2() {
|
||||
static const int* static_local_pointer_dynamic = source(true);
|
||||
sink(static_local_pointer_dynamic); // $ ast,ir
|
||||
}
|
||||
|
||||
static const int global_array_dynamic[] = { ::source() };
|
||||
|
||||
void test3() {
|
||||
sink(*global_array_dynamic); // $ MISSING: ir,ast // Missing in IR because no 'IRFunction' for global_array is generated because the type of global_array_dynamic is "deeply const".
|
||||
}
|
||||
|
||||
const int* source(bool);
|
||||
|
||||
static const int* global_pointer_dynamic = source(true);
|
||||
|
||||
void test4() {
|
||||
sink(global_pointer_dynamic); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test5() {
|
||||
static const char static_local_array_static[] = "source";
|
||||
static const char static_local_array_static_indirect_1[] = "indirect_source(1)";
|
||||
static const char static_local_array_static_indirect_2[] = "indirect_source(2)";
|
||||
sink(static_local_array_static); // clean
|
||||
sink(static_local_array_static_indirect_1); // $ ir MISSING: ast
|
||||
indirect_sink(static_local_array_static_indirect_1); // clean
|
||||
sink(static_local_array_static_indirect_2); // clean
|
||||
indirect_sink(static_local_array_static_indirect_2); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test6() {
|
||||
static const char* static_local_pointer_static = "source";
|
||||
static const char* static_local_pointer_static_indirect_1 = "indirect_source(1)";
|
||||
static const char* static_local_pointer_static_indirect_2 = "indirect_source(2)";
|
||||
sink(static_local_pointer_static); // $ ir MISSING: ast
|
||||
sink(static_local_pointer_static_indirect_1); // clean
|
||||
indirect_sink(static_local_pointer_static_indirect_1); // $ ir MISSING: ast
|
||||
sink(static_local_pointer_static_indirect_2); // clean: static_local_pointer_static_indirect_2 does not have 2 indirections
|
||||
indirect_sink(static_local_pointer_static_indirect_2); // clean: static_local_pointer_static_indirect_2 does not have 2 indirections
|
||||
}
|
||||
|
||||
static const char global_array_static[] = "source";
|
||||
static const char global_array_static_indirect_1[] = "indirect_source(1)";
|
||||
static const char global_array_static_indirect_2[] = "indirect_source(2)";
|
||||
|
||||
void test7() {
|
||||
sink(global_array_static); // clean
|
||||
sink(*global_array_static); // clean
|
||||
sink(global_array_static_indirect_1); // $ ir MISSING: ast
|
||||
sink(*global_array_static_indirect_1); // clean
|
||||
indirect_sink(global_array_static); // clean
|
||||
indirect_sink(global_array_static_indirect_1); // clean
|
||||
indirect_sink(global_array_static_indirect_2); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
static const char* global_pointer_static = "source";
|
||||
static const char* global_pointer_static_indirect_1 = "indirect_source(1)";
|
||||
static const char* global_pointer_static_indirect_2 = "indirect_source(2)";
|
||||
|
||||
void test8() {
|
||||
sink(global_pointer_static); // $ ir MISSING: ast
|
||||
sink(global_pointer_static_indirect_1); // clean
|
||||
indirect_sink(global_pointer_static_indirect_1); // $ ir MISSING: ast
|
||||
sink(global_pointer_static_indirect_2); // clean: global_pointer_static_indirect_2 does not have 2 indirections
|
||||
indirect_sink(global_pointer_static_indirect_2); // clean: global_pointer_static_indirect_2 does not have 2 indirections
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,9 @@
|
||||
WARNING: Module DataFlow has been deprecated and may be removed in future (test.ql:19,45-53)
|
||||
WARNING: Module DataFlow has been deprecated and may be removed in future (test.ql:20,24-32)
|
||||
WARNING: Module DataFlow has been deprecated and may be removed in future (test.ql:27,15-23)
|
||||
WARNING: Module DataFlow has been deprecated and may be removed in future (test.ql:33,22-30)
|
||||
WARNING: Module DataFlow has been deprecated and may be removed in future (test.ql:40,25-33)
|
||||
WARNING: Module DataFlow has been deprecated and may be removed in future (test.ql:42,17-25)
|
||||
WARNING: Module DataFlow has been deprecated and may be removed in future (test.ql:46,20-28)
|
||||
testFailures
|
||||
failures
|
||||
|
||||
@@ -1,3 +1,107 @@
|
||||
import TestBase
|
||||
import TestUtilities.dataflow.FlowTestCommon
|
||||
|
||||
module AstTest {
|
||||
private import semmle.code.cpp.dataflow.DataFlow
|
||||
private import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
|
||||
* S in `if (guarded(x)) S`.
|
||||
*/
|
||||
// This is tested in `BarrierGuard.cpp`.
|
||||
predicate testBarrierGuard(GuardCondition g, Expr checked, boolean isTrue) {
|
||||
g.(FunctionCall).getTarget().getName() = "guarded" and
|
||||
checked = g.(FunctionCall).getArgument(0) and
|
||||
isTrue = true
|
||||
}
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
module AstTestAllocationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
or
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "indirect_source"
|
||||
or
|
||||
source.(DataFlow::DefinitionByReferenceNode).getParameter().getName().matches("ref_source%")
|
||||
or
|
||||
// Track uninitialized variables
|
||||
exists(source.asUninitialized())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call |
|
||||
call.getTarget().getName() = ["sink", "indirect_sink"] and
|
||||
sink.asExpr() = call.getAnArgument()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node barrier) {
|
||||
barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") or
|
||||
barrier = DataFlow::BarrierGuard<testBarrierGuard/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
module AstFlow = DataFlow::Global<AstTestAllocationConfig>;
|
||||
}
|
||||
|
||||
module IRTest {
|
||||
private import cpp
|
||||
private import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.controlflow.IRGuards
|
||||
|
||||
/**
|
||||
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
|
||||
* S in `if (guarded(x)) S`.
|
||||
*/
|
||||
// This is tested in `BarrierGuard.cpp`.
|
||||
predicate testBarrierGuard(IRGuardCondition g, Expr checked, boolean isTrue) {
|
||||
exists(Call call |
|
||||
call = g.getUnconvertedResultExpression() and
|
||||
call.getTarget().hasName("guarded") and
|
||||
checked = call.getArgument(0) and
|
||||
isTrue = true
|
||||
)
|
||||
}
|
||||
|
||||
/** Common data flow configuration to be used by tests. */
|
||||
module IRTestAllocationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(FunctionCall).getTarget().getName() = "source"
|
||||
or
|
||||
source.asIndirectExpr(1).(FunctionCall).getTarget().getName() = "indirect_source"
|
||||
or
|
||||
source.asParameter().getName().matches("source%")
|
||||
or
|
||||
source.(DataFlow::DefinitionByReferenceNode).getParameter().getName().matches("ref_source%")
|
||||
or
|
||||
exists(source.asUninitialized())
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(FunctionCall call, Expr e | e = call.getAnArgument() |
|
||||
call.getTarget().getName() = "sink" and
|
||||
sink.asExpr() = e
|
||||
or
|
||||
call.getTarget().getName() = "indirect_sink" and
|
||||
sink.asIndirectExpr() = e
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node barrier) {
|
||||
exists(Expr barrierExpr | barrierExpr in [barrier.asExpr(), barrier.asIndirectExpr()] |
|
||||
barrierExpr.(VariableAccess).getTarget().hasName("barrier")
|
||||
)
|
||||
or
|
||||
barrier = DataFlow::BarrierGuard<testBarrierGuard/3>::getABarrierNode()
|
||||
or
|
||||
barrier = DataFlow::BarrierGuard<testBarrierGuard/3>::getAnIndirectBarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
module IRFlow = DataFlow::Global<IRTestAllocationConfig>;
|
||||
}
|
||||
|
||||
import MakeTest<MergeTests<AstFlowTest<AstTest::AstFlow>, IRFlowTest<IRTest::IRFlow>>>
|
||||
|
||||
@@ -2770,65 +2770,43 @@ ir.cpp:
|
||||
# 462| m462_2(int) = Uninitialized[x] : &:r462_1
|
||||
# 463| r463_1(glval<bool>) = VariableAddress[a] :
|
||||
# 463| r463_2(bool) = Load[a] : &:r463_1, m461_6
|
||||
# 463| r463_3(bool) = LogicalNot : r463_2
|
||||
# 463| v463_4(void) = ConditionalBranch : r463_3
|
||||
#-----| False -> Block 5
|
||||
#-----| True -> Block 1
|
||||
# 463| v463_3(void) = ConditionalBranch : r463_2
|
||||
#-----| False -> Block 1
|
||||
#-----| True -> Block 2
|
||||
|
||||
# 464| Block 1
|
||||
# 464| r464_1(int) = Constant[1] :
|
||||
# 464| r464_2(glval<int>) = VariableAddress[x] :
|
||||
# 464| m464_3(int) = Store[x] : &:r464_2, r464_1
|
||||
#-----| Goto -> Block 5
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 467| Block 2
|
||||
# 467| r467_1(glval<bool>) = VariableAddress[#temp467:11] :
|
||||
# 467| r467_2(bool) = Constant[0] :
|
||||
# 467| m467_3(bool) = Store[#temp467:11] : &:r467_1, r467_2
|
||||
#-----| Goto -> Block 3
|
||||
# 467| r467_1(glval<bool>) = VariableAddress[a] :
|
||||
# 467| r467_2(bool) = Load[a] : &:r467_1, m461_6
|
||||
# 467| v467_3(void) = ConditionalBranch : r467_2
|
||||
#-----| False -> Block 4
|
||||
#-----| True -> Block 3
|
||||
|
||||
# 467| Block 3
|
||||
# 467| m467_4(bool) = Phi : from 2:m467_3, from 4:m467_11
|
||||
# 467| r467_5(glval<bool>) = VariableAddress[#temp467:11] :
|
||||
# 467| r467_6(bool) = Load[#temp467:11] : &:r467_5, m467_4
|
||||
# 467| r467_7(bool) = LogicalNot : r467_6
|
||||
# 467| v467_8(void) = ConditionalBranch : r467_7
|
||||
#-----| False -> Block 8
|
||||
#-----| True -> Block 7
|
||||
# 467| r467_4(glval<bool>) = VariableAddress[b] :
|
||||
# 467| r467_5(bool) = Load[b] : &:r467_4, m461_8
|
||||
# 467| v467_6(void) = ConditionalBranch : r467_5
|
||||
#-----| False -> Block 4
|
||||
#-----| True -> Block 5
|
||||
|
||||
# 467| Block 4
|
||||
# 467| r467_9(glval<bool>) = VariableAddress[#temp467:11] :
|
||||
# 467| r467_10(bool) = Constant[1] :
|
||||
# 467| m467_11(bool) = Store[#temp467:11] : &:r467_9, r467_10
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 467| Block 5
|
||||
# 467| r467_12(glval<bool>) = VariableAddress[a] :
|
||||
# 467| r467_13(bool) = Load[a] : &:r467_12, m461_6
|
||||
# 467| v467_14(void) = ConditionalBranch : r467_13
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 6
|
||||
|
||||
# 467| Block 6
|
||||
# 467| r467_15(glval<bool>) = VariableAddress[b] :
|
||||
# 467| r467_16(bool) = Load[b] : &:r467_15, m461_8
|
||||
# 467| v467_17(void) = ConditionalBranch : r467_16
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 4
|
||||
|
||||
# 468| Block 7
|
||||
# 468| Block 4
|
||||
# 468| r468_1(int) = Constant[2] :
|
||||
# 468| r468_2(glval<int>) = VariableAddress[x] :
|
||||
# 468| m468_3(int) = Store[x] : &:r468_2, r468_1
|
||||
#-----| Goto -> Block 9
|
||||
#-----| Goto -> Block 6
|
||||
|
||||
# 471| Block 8
|
||||
# 471| Block 5
|
||||
# 471| r471_1(int) = Constant[3] :
|
||||
# 471| r471_2(glval<int>) = VariableAddress[x] :
|
||||
# 471| m471_3(int) = Store[x] : &:r471_2, r471_1
|
||||
#-----| Goto -> Block 9
|
||||
#-----| Goto -> Block 6
|
||||
|
||||
# 473| Block 9
|
||||
# 473| Block 6
|
||||
# 473| v473_1(void) = NoOp :
|
||||
# 461| v461_9(void) = ReturnVoid :
|
||||
# 461| v461_10(void) = AliasedUse : m461_3
|
||||
|
||||
@@ -2398,27 +2398,16 @@
|
||||
| ir.cpp:461:22:461:22 | Address | &:r461_5 |
|
||||
| ir.cpp:461:30:461:30 | Address | &:r461_7 |
|
||||
| ir.cpp:462:9:462:9 | Address | &:r462_1 |
|
||||
| ir.cpp:463:9:463:10 | Condition | r463_3 |
|
||||
| ir.cpp:463:10:463:10 | Address | &:r463_1 |
|
||||
| ir.cpp:463:10:463:10 | Condition | r463_2 |
|
||||
| ir.cpp:463:10:463:10 | Load | m461_6 |
|
||||
| ir.cpp:463:10:463:10 | Unary | r463_2 |
|
||||
| ir.cpp:464:9:464:9 | Address | &:r464_2 |
|
||||
| ir.cpp:464:13:464:13 | StoreValue | r464_1 |
|
||||
| ir.cpp:467:9:467:17 | Condition | r467_7 |
|
||||
| ir.cpp:467:11:467:11 | Address | &:r467_12 |
|
||||
| ir.cpp:467:11:467:11 | Condition | r467_13 |
|
||||
| ir.cpp:467:11:467:11 | Address | &:r467_1 |
|
||||
| ir.cpp:467:11:467:11 | Condition | r467_2 |
|
||||
| ir.cpp:467:11:467:11 | Load | m461_6 |
|
||||
| ir.cpp:467:11:467:16 | Address | &:r467_1 |
|
||||
| ir.cpp:467:11:467:16 | Address | &:r467_5 |
|
||||
| ir.cpp:467:11:467:16 | Address | &:r467_9 |
|
||||
| ir.cpp:467:11:467:16 | Load | m467_4 |
|
||||
| ir.cpp:467:11:467:16 | Phi | from 2:m467_3 |
|
||||
| ir.cpp:467:11:467:16 | Phi | from 4:m467_11 |
|
||||
| ir.cpp:467:11:467:16 | StoreValue | r467_2 |
|
||||
| ir.cpp:467:11:467:16 | StoreValue | r467_10 |
|
||||
| ir.cpp:467:11:467:16 | Unary | r467_6 |
|
||||
| ir.cpp:467:16:467:16 | Address | &:r467_15 |
|
||||
| ir.cpp:467:16:467:16 | Condition | r467_16 |
|
||||
| ir.cpp:467:16:467:16 | Address | &:r467_4 |
|
||||
| ir.cpp:467:16:467:16 | Condition | r467_5 |
|
||||
| ir.cpp:467:16:467:16 | Load | m461_8 |
|
||||
| ir.cpp:468:9:468:9 | Address | &:r468_2 |
|
||||
| ir.cpp:468:13:468:13 | StoreValue | r468_1 |
|
||||
|
||||
@@ -2725,64 +2725,43 @@ ir.cpp:
|
||||
# 462| mu462_2(int) = Uninitialized[x] : &:r462_1
|
||||
# 463| r463_1(glval<bool>) = VariableAddress[a] :
|
||||
# 463| r463_2(bool) = Load[a] : &:r463_1, ~m?
|
||||
# 463| r463_3(bool) = LogicalNot : r463_2
|
||||
# 463| v463_4(void) = ConditionalBranch : r463_3
|
||||
#-----| False -> Block 5
|
||||
#-----| True -> Block 1
|
||||
# 463| v463_3(void) = ConditionalBranch : r463_2
|
||||
#-----| False -> Block 1
|
||||
#-----| True -> Block 2
|
||||
|
||||
# 464| Block 1
|
||||
# 464| r464_1(int) = Constant[1] :
|
||||
# 464| r464_2(glval<int>) = VariableAddress[x] :
|
||||
# 464| mu464_3(int) = Store[x] : &:r464_2, r464_1
|
||||
#-----| Goto -> Block 5
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 467| Block 2
|
||||
# 467| r467_1(glval<bool>) = VariableAddress[#temp467:11] :
|
||||
# 467| r467_2(bool) = Constant[0] :
|
||||
# 467| mu467_3(bool) = Store[#temp467:11] : &:r467_1, r467_2
|
||||
#-----| Goto -> Block 3
|
||||
# 467| r467_1(glval<bool>) = VariableAddress[a] :
|
||||
# 467| r467_2(bool) = Load[a] : &:r467_1, ~m?
|
||||
# 467| v467_3(void) = ConditionalBranch : r467_2
|
||||
#-----| False -> Block 4
|
||||
#-----| True -> Block 3
|
||||
|
||||
# 467| Block 3
|
||||
# 467| r467_4(glval<bool>) = VariableAddress[#temp467:11] :
|
||||
# 467| r467_5(bool) = Load[#temp467:11] : &:r467_4, ~m?
|
||||
# 467| r467_6(bool) = LogicalNot : r467_5
|
||||
# 467| v467_7(void) = ConditionalBranch : r467_6
|
||||
#-----| False -> Block 8
|
||||
#-----| True -> Block 7
|
||||
# 467| r467_4(glval<bool>) = VariableAddress[b] :
|
||||
# 467| r467_5(bool) = Load[b] : &:r467_4, ~m?
|
||||
# 467| v467_6(void) = ConditionalBranch : r467_5
|
||||
#-----| False -> Block 4
|
||||
#-----| True -> Block 5
|
||||
|
||||
# 467| Block 4
|
||||
# 467| r467_8(glval<bool>) = VariableAddress[#temp467:11] :
|
||||
# 467| r467_9(bool) = Constant[1] :
|
||||
# 467| mu467_10(bool) = Store[#temp467:11] : &:r467_8, r467_9
|
||||
#-----| Goto -> Block 3
|
||||
|
||||
# 467| Block 5
|
||||
# 467| r467_11(glval<bool>) = VariableAddress[a] :
|
||||
# 467| r467_12(bool) = Load[a] : &:r467_11, ~m?
|
||||
# 467| v467_13(void) = ConditionalBranch : r467_12
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 6
|
||||
|
||||
# 467| Block 6
|
||||
# 467| r467_14(glval<bool>) = VariableAddress[b] :
|
||||
# 467| r467_15(bool) = Load[b] : &:r467_14, ~m?
|
||||
# 467| v467_16(void) = ConditionalBranch : r467_15
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 4
|
||||
|
||||
# 468| Block 7
|
||||
# 468| Block 4
|
||||
# 468| r468_1(int) = Constant[2] :
|
||||
# 468| r468_2(glval<int>) = VariableAddress[x] :
|
||||
# 468| mu468_3(int) = Store[x] : &:r468_2, r468_1
|
||||
#-----| Goto -> Block 9
|
||||
#-----| Goto -> Block 6
|
||||
|
||||
# 471| Block 8
|
||||
# 471| Block 5
|
||||
# 471| r471_1(int) = Constant[3] :
|
||||
# 471| r471_2(glval<int>) = VariableAddress[x] :
|
||||
# 471| mu471_3(int) = Store[x] : &:r471_2, r471_1
|
||||
#-----| Goto -> Block 9
|
||||
#-----| Goto -> Block 6
|
||||
|
||||
# 473| Block 9
|
||||
# 473| Block 6
|
||||
# 473| v473_1(void) = NoOp :
|
||||
# 461| v461_8(void) = ReturnVoid :
|
||||
# 461| v461_9(void) = AliasedUse : ~m?
|
||||
|
||||
@@ -2,6 +2,7 @@ import cpp
|
||||
import codeql.rangeanalysis.ModulusAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticLocation
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeUtils
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysisRelativeSpecific
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysisImpl
|
||||
@@ -9,7 +10,9 @@ import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific
|
||||
import semmle.code.cpp.ir.IR as IR
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
module ModulusAnalysisInstantiated = ModulusAnalysis<SemLocation, Sem, FloatDelta, ConstantBounds>;
|
||||
module ModulusAnalysisInstantiated =
|
||||
ModulusAnalysis<SemLocation, Sem, FloatDelta, ConstantBounds,
|
||||
RangeUtil<FloatDelta, CppLangImplRelative>>;
|
||||
|
||||
module ModulusAnalysisTest implements TestSig {
|
||||
string getARelevantTag() { result = "mod" }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user