mirror of
https://github.com/github/codeql.git
synced 2026-05-24 08:07:07 +02:00
Compare commits
111 Commits
post-relea
...
rasmuswl/g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf1cbb2e14 | ||
|
|
e4a6f8a719 | ||
|
|
aa0de781cd | ||
|
|
c4c8ab3363 | ||
|
|
d8679d4af3 | ||
|
|
b00eb625a4 | ||
|
|
83f6460fc0 | ||
|
|
e1b808ceb4 | ||
|
|
ff06643538 | ||
|
|
bf7af7876c | ||
|
|
a20f06f8e0 | ||
|
|
7a224f85b2 | ||
|
|
2d3aca7e69 | ||
|
|
0496e6fae1 | ||
|
|
3c919ea7dd | ||
|
|
6ddc2596fc | ||
|
|
8dfc3e5679 | ||
|
|
addcd12107 | ||
|
|
6425a810ad | ||
|
|
954e8f9ecb | ||
|
|
9765cee27c | ||
|
|
c4f5924d84 | ||
|
|
f4ccc629da | ||
|
|
b7f43354fa | ||
|
|
ef693ea0aa | ||
|
|
8c9204a345 | ||
|
|
a937e249a1 | ||
|
|
3931ce7f29 | ||
|
|
ce953d241e | ||
|
|
85eb3fa460 | ||
|
|
e558ea796d | ||
|
|
81b9ee9328 | ||
|
|
ce3a0b07d2 | ||
|
|
c485e31314 | ||
|
|
f30cc7c2d4 | ||
|
|
75235c0c02 | ||
|
|
a737685307 | ||
|
|
3ddac90482 | ||
|
|
aaaa942fa0 | ||
|
|
1e3ca8aeb5 | ||
|
|
7d185922d8 | ||
|
|
10898b00ab | ||
|
|
5feb2a7726 | ||
|
|
70491ce3a0 | ||
|
|
55dc75615a | ||
|
|
f0102be052 | ||
|
|
c2df81a838 | ||
|
|
94e0bc06c7 | ||
|
|
0844563523 | ||
|
|
5016c5568c | ||
|
|
359a162522 | ||
|
|
1c0af9d759 | ||
|
|
84ba18cfc2 | ||
|
|
e060cfc554 | ||
|
|
ebd9802e74 | ||
|
|
1178894ad4 | ||
|
|
c08587c84c | ||
|
|
4f75e5fa5c | ||
|
|
da01b95801 | ||
|
|
e1e4ec8936 | ||
|
|
8500acd996 | ||
|
|
0d20a712fd | ||
|
|
7acf287c70 | ||
|
|
71b9bd3f21 | ||
|
|
1a22f5f0e5 | ||
|
|
99beef7d7e | ||
|
|
215c954dea | ||
|
|
7c8bd409ad | ||
|
|
2f5eaac8e4 | ||
|
|
92b5ac8b6a | ||
|
|
4da728d452 | ||
|
|
c27f6eaa18 | ||
|
|
e860d5e1e5 | ||
|
|
eb69e6a342 | ||
|
|
e34e0a0426 | ||
|
|
369d35872d | ||
|
|
cfd69b22bc | ||
|
|
332423dd25 | ||
|
|
2896741d56 | ||
|
|
3a1a8ad4fc | ||
|
|
93eabd9ce8 | ||
|
|
61689c8de3 | ||
|
|
a15af39007 | ||
|
|
50ec22bf4e | ||
|
|
79fa277862 | ||
|
|
0932471e07 | ||
|
|
bc1fdb0aab | ||
|
|
06fbab9e15 | ||
|
|
ef6113c45c | ||
|
|
b4c4fe102f | ||
|
|
1922a1f41c | ||
|
|
9fe02edf9a | ||
|
|
7633d0e71a | ||
|
|
e55acb8918 | ||
|
|
7949403bd8 | ||
|
|
a23fe366a1 | ||
|
|
3cf86ee6ce | ||
|
|
6232fa1a5f | ||
|
|
cd50fd5b62 | ||
|
|
d4acc704a4 | ||
|
|
fd7de0ee92 | ||
|
|
c76bd71a3c | ||
|
|
fa6f2a1f09 | ||
|
|
8121debbd7 | ||
|
|
bef7eccbf1 | ||
|
|
afc00567f0 | ||
|
|
4d69cb33eb | ||
|
|
6a3f1270ea | ||
|
|
88eb91aa70 | ||
|
|
a6a09b4a7a | ||
|
|
7d6d9a7420 |
@@ -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"
|
||||
@@ -29,7 +29,6 @@ provide:
|
||||
- "swift/extractor-pack/codeql-extractor.yml"
|
||||
- "swift/integration-tests/qlpack.yml"
|
||||
- "ql/extractor-pack/codeql-extractor.yml"
|
||||
- ".github/codeql/extensions/**/codeql-pack.yml"
|
||||
|
||||
versionPolicies:
|
||||
default:
|
||||
|
||||
@@ -145,9 +145,9 @@ namespace Semmle.Autobuild.Cpp.Tests
|
||||
|
||||
bool IBuildActions.IsMacOs() => IsMacOs;
|
||||
|
||||
public bool IsRunningOnAppleSilicon { get; set; }
|
||||
public bool IsArm { get; set; }
|
||||
|
||||
bool IBuildActions.IsRunningOnAppleSilicon() => IsRunningOnAppleSilicon;
|
||||
bool IBuildActions.IsArm() => IsArm;
|
||||
|
||||
string IBuildActions.PathCombine(params string[] parts)
|
||||
{
|
||||
|
||||
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
|
||||
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: Removed @assignpaddexpr and @assignpsubexpr from @assign_bitwise_expr
|
||||
compatibility: full
|
||||
@@ -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
|
||||
|
||||
@@ -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.0
|
||||
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.
|
||||
|
||||
@@ -31,11 +31,6 @@ abstract class MustFlowConfiguration extends string {
|
||||
*/
|
||||
abstract predicate isSink(Operand sink);
|
||||
|
||||
/**
|
||||
* Holds if data flow through `instr` is prohibited.
|
||||
*/
|
||||
predicate isBarrier(Instruction instr) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the additional flow step from `node1` to `node2` must be taken
|
||||
* into account in the analysis.
|
||||
@@ -53,21 +48,18 @@ abstract class MustFlowConfiguration extends string {
|
||||
*/
|
||||
final predicate hasFlowPath(MustFlowPathNode source, MustFlowPathSink sink) {
|
||||
this.isSource(source.getInstruction()) and
|
||||
source.getASuccessor*() = sink
|
||||
source.getASuccessor+() = sink
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `node` flows from a source. */
|
||||
pragma[nomagic]
|
||||
private predicate flowsFromSource(Instruction node, MustFlowConfiguration config) {
|
||||
not config.isBarrier(node) and
|
||||
(
|
||||
config.isSource(node)
|
||||
or
|
||||
exists(Instruction mid |
|
||||
step(mid, node, config) and
|
||||
flowsFromSource(mid, pragma[only_bind_into](config))
|
||||
)
|
||||
config.isSource(node)
|
||||
or
|
||||
exists(Instruction mid |
|
||||
step(mid, node, config) and
|
||||
flowsFromSource(mid, pragma[only_bind_into](config))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,20 +632,20 @@ predicate jumpStep(Node n1, Node n2) {
|
||||
v = globalUse.getVariable() and
|
||||
n1.(FinalGlobalValue).getGlobalUse() = globalUse
|
||||
|
|
||||
globalUse.getIndirection() = getMinIndirectionForGlobalUse(globalUse) and
|
||||
globalUse.getIndirectionIndex() = 1 and
|
||||
v = n2.asVariable()
|
||||
or
|
||||
v = n2.asIndirectVariable(globalUse.getIndirection())
|
||||
v = n2.asIndirectVariable(globalUse.getIndirectionIndex())
|
||||
)
|
||||
or
|
||||
exists(Ssa::GlobalDef globalDef |
|
||||
v = globalDef.getVariable() and
|
||||
n2.(InitialGlobalValue).getGlobalDef() = globalDef
|
||||
|
|
||||
globalDef.getIndirection() = getMinIndirectionForGlobalDef(globalDef) and
|
||||
globalDef.getIndirectionIndex() = 1 and
|
||||
v = n1.asVariable()
|
||||
or
|
||||
v = n1.asIndirectVariable(globalDef.getIndirection())
|
||||
v = n1.asIndirectVariable(globalDef.getIndirectionIndex())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
@@ -45,12 +44,11 @@ private newtype TIRDataFlowNode =
|
||||
TIndirectArgumentOutNode(ArgumentOperand operand, int indirectionIndex) {
|
||||
Ssa::isModifiableByCall(operand, indirectionIndex)
|
||||
} or
|
||||
TRawIndirectOperand0(Node0Impl node, int indirectionIndex) {
|
||||
Ssa::hasRawIndirectOperand(node.asOperand(), indirectionIndex)
|
||||
TRawIndirectOperand(Operand op, int indirectionIndex) {
|
||||
Ssa::hasRawIndirectOperand(op, indirectionIndex)
|
||||
} or
|
||||
TRawIndirectInstruction0(Node0Impl node, int indirectionIndex) {
|
||||
not exists(node.asOperand()) and
|
||||
Ssa::hasRawIndirectInstruction(node.asInstruction(), indirectionIndex)
|
||||
TRawIndirectInstruction(Instruction instr, int indirectionIndex) {
|
||||
Ssa::hasRawIndirectInstruction(instr, indirectionIndex)
|
||||
} or
|
||||
TFinalParameterNode(Parameter p, int indirectionIndex) {
|
||||
exists(Ssa::FinalParameterUse use |
|
||||
@@ -347,9 +345,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 +353,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 +431,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 +447,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 +472,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()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -902,146 +918,48 @@ Type getTypeImpl(Type t, int indirectionIndex) {
|
||||
result instanceof UnknownType
|
||||
}
|
||||
|
||||
private module RawIndirectNodes {
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an operand in the IR
|
||||
* after `index` number of loads.
|
||||
*/
|
||||
private class RawIndirectOperand0 extends Node, TRawIndirectOperand0 {
|
||||
Node0Impl node;
|
||||
int indirectionIndex;
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an operand in the IR
|
||||
* after `index` number of loads.
|
||||
*/
|
||||
class RawIndirectOperand extends Node, TRawIndirectOperand {
|
||||
Operand operand;
|
||||
int indirectionIndex;
|
||||
|
||||
RawIndirectOperand0() { this = TRawIndirectOperand0(node, indirectionIndex) }
|
||||
RawIndirectOperand() { this = TRawIndirectOperand(operand, indirectionIndex) }
|
||||
|
||||
/** Gets the underlying instruction. */
|
||||
Operand getOperand() { result = node.asOperand() }
|
||||
/** Gets the underlying instruction. */
|
||||
Operand getOperand() { result = operand }
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override Declaration getFunction() {
|
||||
result = this.getOperand().getDef().getEnclosingFunction()
|
||||
}
|
||||
override Declaration getFunction() { result = this.getOperand().getDef().getEnclosingFunction() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override DataFlowType getType() {
|
||||
exists(int sub, DataFlowType type, boolean isGLValue |
|
||||
type = getOperandType(this.getOperand(), isGLValue) and
|
||||
if isGLValue = true then sub = 1 else sub = 0
|
||||
|
|
||||
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
|
||||
)
|
||||
}
|
||||
|
||||
final override Location getLocationImpl() {
|
||||
if exists(this.getOperand().getLocation())
|
||||
then result = this.getOperand().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = operandNode(this.getOperand()).toStringImpl() + " indirection"
|
||||
}
|
||||
override DataFlowType getType() {
|
||||
exists(int sub, DataFlowType type, boolean isGLValue |
|
||||
type = getOperandType(operand, isGLValue) and
|
||||
if isGLValue = true then sub = 1 else sub = 0
|
||||
|
|
||||
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an instruction in the IR
|
||||
* after `index` number of loads.
|
||||
*/
|
||||
private class RawIndirectInstruction0 extends Node, TRawIndirectInstruction0 {
|
||||
Node0Impl node;
|
||||
int indirectionIndex;
|
||||
|
||||
RawIndirectInstruction0() { this = TRawIndirectInstruction0(node, indirectionIndex) }
|
||||
|
||||
/** Gets the underlying instruction. */
|
||||
Instruction getInstruction() { result = node.asInstruction() }
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override DataFlowType getType() {
|
||||
exists(int sub, DataFlowType type, boolean isGLValue |
|
||||
type = getInstructionType(this.getInstruction(), isGLValue) and
|
||||
if isGLValue = true then sub = 1 else sub = 0
|
||||
|
|
||||
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
|
||||
)
|
||||
}
|
||||
|
||||
final override Location getLocationImpl() {
|
||||
if exists(this.getInstruction().getLocation())
|
||||
then result = this.getInstruction().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = instructionNode(this.getInstruction()).toStringImpl() + " indirection"
|
||||
}
|
||||
final override Location getLocationImpl() {
|
||||
if exists(this.getOperand().getLocation())
|
||||
then result = this.getOperand().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an operand in the IR
|
||||
* after a number of loads.
|
||||
*/
|
||||
class RawIndirectOperand extends Node {
|
||||
int indirectionIndex;
|
||||
Operand operand;
|
||||
|
||||
RawIndirectOperand() {
|
||||
exists(Node0Impl node | operand = node.asOperand() |
|
||||
this = TRawIndirectOperand0(node, indirectionIndex)
|
||||
or
|
||||
this = TRawIndirectInstruction0(node, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the operand associated with this node. */
|
||||
Operand getOperand() { result = operand }
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an instruction in the IR
|
||||
* after a number of loads.
|
||||
*/
|
||||
class RawIndirectInstruction extends Node {
|
||||
int indirectionIndex;
|
||||
Instruction instr;
|
||||
|
||||
RawIndirectInstruction() {
|
||||
exists(Node0Impl node | instr = node.asInstruction() |
|
||||
this = TRawIndirectOperand0(node, indirectionIndex)
|
||||
or
|
||||
this = TRawIndirectInstruction0(node, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the instruction associated with this node. */
|
||||
Instruction getInstruction() { result = instr }
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
override string toStringImpl() {
|
||||
result = operandNode(this.getOperand()).toStringImpl() + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
import RawIndirectNodes
|
||||
|
||||
/**
|
||||
* INTERNAL: do not use.
|
||||
*
|
||||
@@ -1103,6 +1021,48 @@ class UninitializedNode extends Node {
|
||||
LocalVariable getLocalVariable() { result = v }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
*
|
||||
* A node that represents the indirect value of an instruction in the IR
|
||||
* after `index` number of loads.
|
||||
*/
|
||||
class RawIndirectInstruction extends Node, TRawIndirectInstruction {
|
||||
Instruction instr;
|
||||
int indirectionIndex;
|
||||
|
||||
RawIndirectInstruction() { this = TRawIndirectInstruction(instr, indirectionIndex) }
|
||||
|
||||
/** Gets the underlying instruction. */
|
||||
Instruction getInstruction() { result = instr }
|
||||
|
||||
/** Gets the underlying indirection index. */
|
||||
int getIndirectionIndex() { result = indirectionIndex }
|
||||
|
||||
override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
|
||||
|
||||
override Declaration getEnclosingCallable() { result = this.getFunction() }
|
||||
|
||||
override DataFlowType getType() {
|
||||
exists(int sub, DataFlowType type, boolean isGLValue |
|
||||
type = getInstructionType(instr, isGLValue) and
|
||||
if isGLValue = true then sub = 1 else sub = 0
|
||||
|
|
||||
result = getTypeImpl(type.getUnspecifiedType(), indirectionIndex - sub)
|
||||
)
|
||||
}
|
||||
|
||||
final override Location getLocationImpl() {
|
||||
if exists(this.getInstruction().getLocation())
|
||||
then result = this.getInstruction().getLocation()
|
||||
else result instanceof UnknownDefaultLocation
|
||||
}
|
||||
|
||||
override string toStringImpl() {
|
||||
result = instructionNode(this.getInstruction()).toStringImpl() + " indirection"
|
||||
}
|
||||
}
|
||||
|
||||
private module GetConvertedResultExpression {
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.TranslatedExpr
|
||||
private import semmle.code.cpp.ir.implementation.raw.internal.InstructionTag
|
||||
@@ -1276,90 +1236,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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1699,29 +1600,26 @@ private module Cached {
|
||||
predicate localFlowStep(Node nodeFrom, Node nodeTo) { simpleLocalFlowStep(nodeFrom, nodeTo) }
|
||||
|
||||
private predicate indirectionOperandFlow(RawIndirectOperand nodeFrom, Node nodeTo) {
|
||||
nodeFrom != nodeTo and
|
||||
(
|
||||
// Reduce the indirection count by 1 if we're passing through a `LoadInstruction`.
|
||||
exists(int ind, LoadInstruction load |
|
||||
hasOperandAndIndex(nodeFrom, load.getSourceAddressOperand(), ind) and
|
||||
nodeHasInstruction(nodeTo, load, ind - 1)
|
||||
)
|
||||
or
|
||||
// If an operand flows to an instruction, then the indirection of
|
||||
// the operand also flows to the indirection of the instruction.
|
||||
exists(Operand operand, Instruction instr, int indirectionIndex |
|
||||
simpleInstructionLocalFlowStep(operand, instr) and
|
||||
hasOperandAndIndex(nodeFrom, operand, pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, instr, pragma[only_bind_into](indirectionIndex))
|
||||
)
|
||||
or
|
||||
// If there's indirect flow to an operand, then there's also indirect
|
||||
// flow to the operand after applying some pointer arithmetic.
|
||||
exists(PointerArithmeticInstruction pointerArith, int indirectionIndex |
|
||||
hasOperandAndIndex(nodeFrom, pointerArith.getAnOperand(),
|
||||
pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pointerArith, pragma[only_bind_into](indirectionIndex))
|
||||
)
|
||||
// Reduce the indirection count by 1 if we're passing through a `LoadInstruction`.
|
||||
exists(int ind, LoadInstruction load |
|
||||
hasOperandAndIndex(nodeFrom, load.getSourceAddressOperand(), ind) and
|
||||
nodeHasInstruction(nodeTo, load, ind - 1)
|
||||
)
|
||||
or
|
||||
// If an operand flows to an instruction, then the indirection of
|
||||
// the operand also flows to the indirection of the instruction.
|
||||
exists(Operand operand, Instruction instr, int indirectionIndex |
|
||||
simpleInstructionLocalFlowStep(operand, instr) and
|
||||
hasOperandAndIndex(nodeFrom, operand, pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, instr, pragma[only_bind_into](indirectionIndex))
|
||||
)
|
||||
or
|
||||
// If there's indirect flow to an operand, then there's also indirect
|
||||
// flow to the operand after applying some pointer arithmetic.
|
||||
exists(PointerArithmeticInstruction pointerArith, int indirectionIndex |
|
||||
hasOperandAndIndex(nodeFrom, pointerArith.getAnOperand(),
|
||||
pragma[only_bind_into](indirectionIndex)) and
|
||||
hasInstructionAndIndex(nodeTo, pointerArith, pragma[only_bind_into](indirectionIndex))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1747,7 +1645,6 @@ private module Cached {
|
||||
private predicate indirectionInstructionFlow(
|
||||
RawIndirectInstruction nodeFrom, IndirectOperand nodeTo
|
||||
) {
|
||||
nodeFrom != nodeTo and
|
||||
// If there's flow from an instruction to an operand, then there's also flow from the
|
||||
// indirect instruction to the indirect operand.
|
||||
exists(Operand operand, Instruction instr, int indirectionIndex |
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,12 +113,22 @@ private newtype TDefOrUseImpl =
|
||||
TGlobalUse(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
|
||||
// Represents a final "use" of a global variable to ensure that
|
||||
// the assignment to a global variable isn't ruled out as dead.
|
||||
isGlobalUse(v, f, _, indirectionIndex)
|
||||
exists(VariableAddressInstruction vai, int defIndex |
|
||||
vai.getEnclosingIRFunction() = f and
|
||||
vai.getAstVariable() = v and
|
||||
isDef(_, _, _, vai, _, defIndex) and
|
||||
indirectionIndex = [0 .. defIndex] + 1
|
||||
)
|
||||
} or
|
||||
TGlobalDefImpl(GlobalLikeVariable v, IRFunction f, int indirectionIndex) {
|
||||
// Represents the initial "definition" of a global variable when entering
|
||||
// a function body.
|
||||
isGlobalDefImpl(v, f, _, indirectionIndex)
|
||||
exists(VariableAddressInstruction vai |
|
||||
vai.getEnclosingIRFunction() = f and
|
||||
vai.getAstVariable() = v and
|
||||
isUse(_, _, vai, _, indirectionIndex) and
|
||||
not isDef(_, _, vai.getAUse(), _, _, _)
|
||||
)
|
||||
} or
|
||||
TIteratorDef(
|
||||
Operand iteratorDerefAddress, BaseSourceVariableInstruction container, int indirectionIndex
|
||||
@@ -143,27 +150,6 @@ private newtype TDefOrUseImpl =
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isGlobalUse(
|
||||
GlobalLikeVariable v, IRFunction f, int indirection, int indirectionIndex
|
||||
) {
|
||||
exists(VariableAddressInstruction vai |
|
||||
vai.getEnclosingIRFunction() = f and
|
||||
vai.getAstVariable() = v and
|
||||
isDef(_, _, _, vai, indirection, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate isGlobalDefImpl(
|
||||
GlobalLikeVariable v, IRFunction f, int indirection, int indirectionIndex
|
||||
) {
|
||||
exists(VariableAddressInstruction vai |
|
||||
vai.getEnclosingIRFunction() = f and
|
||||
vai.getAstVariable() = v and
|
||||
isUse(_, _, vai, indirection, indirectionIndex) and
|
||||
not isDef(_, _, _, vai, _, indirectionIndex)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate unspecifiedTypeIsModifiableAt(Type unspecified, int indirectionIndex) {
|
||||
indirectionIndex = [1 .. getIndirectionForUnspecifiedType(unspecified).getNumberOfIndirections()] and
|
||||
exists(CppType cppType |
|
||||
@@ -452,7 +438,7 @@ class GlobalUse extends UseImpl, TGlobalUse {
|
||||
|
||||
override FinalGlobalValue getNode() { result.getGlobalUse() = this }
|
||||
|
||||
override int getIndirection() { isGlobalUse(global, f, result, ind) }
|
||||
override int getIndirection() { result = ind + 1 }
|
||||
|
||||
/** Gets the global variable associated with this use. */
|
||||
GlobalLikeVariable getVariable() { result = global }
|
||||
@@ -474,9 +460,7 @@ class GlobalUse extends UseImpl, TGlobalUse {
|
||||
)
|
||||
}
|
||||
|
||||
override SourceVariable getSourceVariable() {
|
||||
sourceVariableIsGlobal(result, global, f, this.getIndirection())
|
||||
}
|
||||
override SourceVariable getSourceVariable() { sourceVariableIsGlobal(result, global, f, ind) }
|
||||
|
||||
final override Cpp::Location getLocation() { result = f.getLocation() }
|
||||
|
||||
@@ -517,18 +501,16 @@ class GlobalDefImpl extends DefOrUseImpl, TGlobalDefImpl {
|
||||
|
||||
/** Gets the global variable associated with this definition. */
|
||||
override SourceVariable getSourceVariable() {
|
||||
sourceVariableIsGlobal(result, global, f, this.getIndirection())
|
||||
sourceVariableIsGlobal(result, global, f, indirectionIndex)
|
||||
}
|
||||
|
||||
int getIndirection() { result = indirectionIndex }
|
||||
|
||||
/**
|
||||
* Gets the type of this use after specifiers have been deeply stripped
|
||||
* and typedefs have been resolved.
|
||||
*/
|
||||
Type getUnspecifiedType() { result = global.getUnspecifiedType() }
|
||||
|
||||
override string toString() { result = "Def of " + this.getSourceVariable() }
|
||||
override string toString() { result = "GlobalDef" }
|
||||
|
||||
override Location getLocation() { result = f.getLocation() }
|
||||
|
||||
@@ -872,7 +854,7 @@ private predicate sourceVariableIsGlobal(
|
||||
)
|
||||
}
|
||||
|
||||
private module SsaInput implements SsaImplCommon::InputSig<Location> {
|
||||
private module SsaInput implements SsaImplCommon::InputSig {
|
||||
import InputSigCommon
|
||||
import SourceVariables
|
||||
|
||||
@@ -998,7 +980,7 @@ class GlobalDef extends TGlobalDef, SsaDefOrUse {
|
||||
final override Location getLocation() { result = global.getLocation() }
|
||||
|
||||
/** Gets a textual representation of this definition. */
|
||||
override string toString() { result = global.toString() }
|
||||
override string toString() { result = "GlobalDef" }
|
||||
|
||||
/**
|
||||
* Holds if this definition has index `index` in block `block`, and
|
||||
@@ -1008,9 +990,6 @@ class GlobalDef extends TGlobalDef, SsaDefOrUse {
|
||||
global.hasIndexInBlock(block, index, sv)
|
||||
}
|
||||
|
||||
/** Gets the indirection index of this definition. */
|
||||
int getIndirection() { result = global.getIndirection() }
|
||||
|
||||
/** Gets the indirection index of this definition. */
|
||||
int getIndirectionIndex() { result = global.getIndirectionIndex() }
|
||||
|
||||
@@ -1095,7 +1074,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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ private import implementations.Accept
|
||||
private import implementations.Poll
|
||||
private import implementations.Select
|
||||
private import implementations.MySql
|
||||
private import implementations.ODBC
|
||||
private import implementations.SqLite3
|
||||
private import implementations.PostgreSql
|
||||
private import implementations.System
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.models.interfaces.Allocation
|
||||
import semmle.code.cpp.models.interfaces.Taint
|
||||
|
||||
/**
|
||||
* An allocation function (such as `malloc`) that has an argument for the size
|
||||
@@ -122,7 +121,7 @@ private class CallocAllocationFunction extends AllocationFunction {
|
||||
* An allocation function (such as `realloc`) that has an argument for the size
|
||||
* in bytes, and an argument for an existing pointer that is to be reallocated.
|
||||
*/
|
||||
private class ReallocAllocationFunction extends AllocationFunction, TaintFunction {
|
||||
private class ReallocAllocationFunction extends AllocationFunction {
|
||||
int sizeArg;
|
||||
int reallocArg;
|
||||
|
||||
@@ -152,10 +151,6 @@ private class ReallocAllocationFunction extends AllocationFunction, TaintFunctio
|
||||
override int getSizeArg() { result = sizeArg }
|
||||
|
||||
override int getReallocPtrArg() { result = reallocArg }
|
||||
|
||||
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
|
||||
input.isParameterDeref(this.getReallocPtrArg()) and output.isReturnValueDeref()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,10 +49,10 @@ private class FgetsFunction extends DataFlowFunction, TaintFunction, ArrayFuncti
|
||||
}
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
(
|
||||
output.isParameterDeref(0) or
|
||||
output.isReturnValueDeref()
|
||||
) and
|
||||
output.isParameterDeref(0) and
|
||||
description = "string read by " + this.getName()
|
||||
or
|
||||
output.isReturnValue() and
|
||||
description = "string read by " + this.getName()
|
||||
}
|
||||
|
||||
@@ -101,6 +101,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()
|
||||
|
||||
@@ -157,7 +157,7 @@ private class Getaddrinfo extends TaintFunction, ArrayFunction, RemoteFlowSource
|
||||
override predicate hasArrayWithNullTerminator(int bufParam) { bufParam in [0, 1] }
|
||||
|
||||
override predicate hasRemoteFlowSource(FunctionOutput output, string description) {
|
||||
output.isParameterDeref(3, 2) and
|
||||
output.isParameterDeref(3) and
|
||||
description = "address returned by " + this.getName()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,17 +9,18 @@ import semmle.code.cpp.models.interfaces.DataFlow
|
||||
import semmle.code.cpp.models.interfaces.Alias
|
||||
import semmle.code.cpp.models.interfaces.SideEffect
|
||||
|
||||
private class MemsetFunctionModel extends ArrayFunction, DataFlowFunction, AliasFunction,
|
||||
/**
|
||||
* The standard function `memset` and its assorted variants
|
||||
*/
|
||||
private class MemsetFunction extends ArrayFunction, DataFlowFunction, AliasFunction,
|
||||
SideEffectFunction
|
||||
{
|
||||
MemsetFunctionModel() {
|
||||
MemsetFunction() {
|
||||
this.hasGlobalOrStdOrBslName("memset")
|
||||
or
|
||||
this.hasGlobalOrStdName("wmemset")
|
||||
or
|
||||
this.hasGlobalName([
|
||||
bzero(), "__builtin_memset", "__builtin_memset_chk", "RtlZeroMemory", "RtlSecureZeroMemory"
|
||||
])
|
||||
this.hasGlobalName([bzero(), "__builtin_memset", "__builtin_memset_chk"])
|
||||
}
|
||||
|
||||
override predicate hasArrayOutput(int bufParam) { bufParam = 0 }
|
||||
@@ -59,8 +60,3 @@ private class MemsetFunctionModel extends ArrayFunction, DataFlowFunction, Alias
|
||||
}
|
||||
|
||||
private string bzero() { result = ["bzero", "explicit_bzero"] }
|
||||
|
||||
/**
|
||||
* The standard function `memset` and its assorted variants
|
||||
*/
|
||||
class MemsetFunction extends Function instanceof MemsetFunctionModel { }
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Provides implementation classes modeling the ODBC C/C++ API.
|
||||
* See `semmle.code.cpp.models.Models` for usage information.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.models.interfaces.Sql
|
||||
private import semmle.code.cpp.models.interfaces.FunctionInputsAndOutputs
|
||||
|
||||
/**
|
||||
* The `SQLExecDirect`, and `SQLPrepare` from the ODBC C/C++ API:
|
||||
* https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlexecdirect-function?view=sql-server-ver16
|
||||
* https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlprepare-function?view=sql-server-ver16
|
||||
*
|
||||
* Note, `SQLExecute` is not included because it operates on a SQLHSTMT type, not a string.
|
||||
* The SQLHSTMT parameter for `SQLExecute` is set through a `SQLPrepare`, which is modeled.
|
||||
* The other source of input to a `SQLExecute` is via a `SQLBindParameter`, which sanitizes user input,
|
||||
* and would be considered a barrier to SQL injection.
|
||||
*/
|
||||
private class ODBCExecutionFunction extends SqlExecutionFunction {
|
||||
ODBCExecutionFunction() { this.hasGlobalName(["SQLExecDirect", "SQLPrepare"]) }
|
||||
|
||||
override predicate hasSqlArgument(FunctionInput input) { input.isParameterDeref(1) }
|
||||
}
|
||||
// NOTE: no need to define a barrier explicitly.
|
||||
// `SQLBindParameter` is the typical means for sanitizing user input.
|
||||
// https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function?view=sql-server-ver16
|
||||
// First a query is establisehed via `SQLPrepare`, then parameters are bound via `SQLBindParameter`, before
|
||||
// the query is executed via `SQLExecute`. We are not modeling SQLExecute, so we do not need to model SQLBindParameter.
|
||||
@@ -147,32 +147,19 @@ private class SnprintfImpl extends Snprintf {
|
||||
|
||||
/**
|
||||
* The Microsoft `StringCchPrintf` function and variants.
|
||||
* See: https://learn.microsoft.com/en-us/windows/win32/api/strsafe/
|
||||
* and
|
||||
* https://learn.microsoft.com/en-us/previous-versions/windows/embedded/ms860435(v=msdn.10)
|
||||
*/
|
||||
private class StringCchPrintf extends FormattingFunction {
|
||||
StringCchPrintf() {
|
||||
this instanceof TopLevelFunction and
|
||||
exists(string baseName |
|
||||
baseName in [
|
||||
"StringCchPrintf", //StringCchPrintf(pszDest, cchDest, pszFormat, ...)
|
||||
"StringCchPrintfEx", //StringCchPrintfEx(pszDest,cchDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, ...)
|
||||
"StringCchPrintf_l", //StringCchPrintf_l(pszDest, cbDest, pszFormat, locale, ...)
|
||||
"StringCchPrintf_lEx", //StringCchPrintf_lEx(pszDest, cchDest, ppszDestEnd, pcchRemaining, dwFlags, pszFormat, locale, ...)
|
||||
"StringCbPrintf", //StringCbPrintf(pszDest, cbDest, pszFormat, ...)
|
||||
"StringCbPrintfEx", //StringCbPrintfEx(pszDest, cbDest, ppszDestEnd, pcbRemaining, dwFlags, pszFormat, ...)
|
||||
"StringCbPrintf_l", //StringCbPrintf_l(pszDest, cbDest, pszFormat, locale, ...)
|
||||
"StringCbPrintf_lEx" //StringCbPrintf_lEx(pszDest, cbDest, ppszDestEnd, pcbRemaining, dwFlags, pszFormat, locale, ...)
|
||||
]
|
||||
|
|
||||
this.hasGlobalName(baseName + ["", "A", "W"])
|
||||
) and
|
||||
this.hasGlobalName([
|
||||
"StringCchPrintf", "StringCchPrintfEx", "StringCchPrintf_l", "StringCchPrintf_lEx",
|
||||
"StringCbPrintf", "StringCbPrintfEx", "StringCbPrintf_l", "StringCbPrintf_lEx"
|
||||
]) and
|
||||
not exists(this.getDefinition().getFile().getRelativePath())
|
||||
}
|
||||
|
||||
override int getFormatParameterIndex() {
|
||||
if this.getName().matches("%Ex" + ["", "A", "W"]) then result = 5 else result = 2
|
||||
if this.getName().matches("%Ex") then result = 5 else result = 2
|
||||
}
|
||||
|
||||
override int getOutputParameterIndex(boolean isStream) { result = 0 and isStream = false }
|
||||
|
||||
@@ -58,7 +58,7 @@ private class Send extends AliasFunction, ArrayFunction, SideEffectFunction, Rem
|
||||
override ParameterIndex getParameterSizeIndex(ParameterIndex i) { i = 1 and result = 2 }
|
||||
|
||||
override predicate hasRemoteFlowSink(FunctionInput input, string description) {
|
||||
input.isParameterDeref(1, 1) and description = "buffer sent by " + this.getName()
|
||||
input.isParameterDeref(1) and description = "buffer sent by " + this.getName()
|
||||
}
|
||||
|
||||
override predicate hasSocketInput(FunctionInput input) { input.isParameter(0) }
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import semmle.code.cpp.Parameter
|
||||
|
||||
private newtype TFunctionInput =
|
||||
TInParameter(ParameterIndex i) or
|
||||
TInParameterDeref(ParameterIndex i, int indirectionIndex) { indirectionIndex = [1, 2] } or
|
||||
TInParameterDeref(ParameterIndex i) or
|
||||
TInQualifierObject() or
|
||||
TInQualifierAddress() or
|
||||
TInReturnValueDeref()
|
||||
@@ -245,18 +245,15 @@ class InParameter extends FunctionInput, TInParameter {
|
||||
*/
|
||||
class InParameterDeref extends FunctionInput, TInParameterDeref {
|
||||
ParameterIndex index;
|
||||
int indirectionIndex;
|
||||
|
||||
InParameterDeref() { this = TInParameterDeref(index, indirectionIndex) }
|
||||
InParameterDeref() { this = TInParameterDeref(index) }
|
||||
|
||||
override string toString() { result = "InParameterDeref " + index.toString() }
|
||||
|
||||
/** Gets the zero-based index of the parameter. */
|
||||
ParameterIndex getIndex() { result = index }
|
||||
|
||||
override predicate isParameterDeref(ParameterIndex i, int indirection) {
|
||||
i = index and indirectionIndex = indirection
|
||||
}
|
||||
override predicate isParameterDeref(ParameterIndex i) { i = index }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,10 +321,10 @@ class InReturnValueDeref extends FunctionInput, TInReturnValueDeref {
|
||||
}
|
||||
|
||||
private newtype TFunctionOutput =
|
||||
TOutParameterDeref(ParameterIndex i, int indirectionIndex) { indirectionIndex = [1, 2] } or
|
||||
TOutParameterDeref(ParameterIndex i) or
|
||||
TOutQualifierObject() or
|
||||
TOutReturnValue() or
|
||||
TOutReturnValueDeref(int indirections) { indirections = [1, 2] }
|
||||
TOutReturnValueDeref()
|
||||
|
||||
/**
|
||||
* An output from a function. This can be:
|
||||
@@ -501,16 +498,17 @@ class FunctionOutput extends TFunctionOutput {
|
||||
*/
|
||||
class OutParameterDeref extends FunctionOutput, TOutParameterDeref {
|
||||
ParameterIndex index;
|
||||
int indirectionIndex;
|
||||
|
||||
OutParameterDeref() { this = TOutParameterDeref(index, indirectionIndex) }
|
||||
OutParameterDeref() { this = TOutParameterDeref(index) }
|
||||
|
||||
override string toString() { result = "OutParameterDeref " + index.toString() }
|
||||
|
||||
ParameterIndex getIndex() { result = index }
|
||||
|
||||
override predicate isParameterDeref(ParameterIndex i) { i = index }
|
||||
|
||||
override predicate isParameterDeref(ParameterIndex i, int ind) {
|
||||
i = index and ind = indirectionIndex
|
||||
this.isParameterDeref(i) and ind = 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,8 +572,4 @@ class OutReturnValueDeref extends FunctionOutput, TOutReturnValueDeref {
|
||||
override string toString() { result = "OutReturnValueDeref" }
|
||||
|
||||
override predicate isReturnValueDeref() { any() }
|
||||
|
||||
override predicate isReturnValueDeref(int indirectionIndex) {
|
||||
this = TOutReturnValueDeref(indirectionIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@ private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
* `upper` is true, and can be traced back to a guard represented by `reason`.
|
||||
*/
|
||||
predicate bounded(Expr e, Bound b, float delta, boolean upper, Reason reason) {
|
||||
exists(SemanticExprConfig::Expr semExpr | semExpr.getUnconvertedResultExpression() = e |
|
||||
exists(SemanticExprConfig::Expr semExpr |
|
||||
semExpr.getUnconverted().getUnconvertedResultExpression() = e
|
||||
|
|
||||
semBounded(semExpr, b, delta, upper, reason)
|
||||
)
|
||||
}
|
||||
@@ -28,7 +30,9 @@ predicate bounded(Expr e, Bound b, float delta, boolean upper, Reason reason) {
|
||||
* The `Expr` may be a conversion.
|
||||
*/
|
||||
predicate convertedBounded(Expr e, Bound b, float delta, boolean upper, Reason reason) {
|
||||
exists(SemanticExprConfig::Expr semExpr | semExpr.getConvertedResultExpression() = e |
|
||||
exists(SemanticExprConfig::Expr semExpr |
|
||||
semExpr.getConverted().getConvertedResultExpression() = e
|
||||
|
|
||||
semBounded(semExpr, b, delta, upper, reason)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ predicate exprMightOverflowNegatively(Expr expr) {
|
||||
lowerBound(expr) < exprMinVal(expr)
|
||||
or
|
||||
exists(SemanticExprConfig::Expr semExpr |
|
||||
semExpr.getAst() = expr and
|
||||
semExpr.getUnconverted().getAst() = expr and
|
||||
ConstantStage::potentiallyOverflowingExpr(false, semExpr) and
|
||||
not ConstantStage::initialBounded(semExpr, _, _, false, _, _, _)
|
||||
)
|
||||
@@ -126,7 +126,7 @@ predicate exprMightOverflowPositively(Expr expr) {
|
||||
upperBound(expr) > exprMaxVal(expr)
|
||||
or
|
||||
exists(SemanticExprConfig::Expr semExpr |
|
||||
semExpr.getAst() = expr and
|
||||
semExpr.getUnconverted().getAst() = expr and
|
||||
ConstantStage::potentiallyOverflowingExpr(true, semExpr) and
|
||||
not ConstantStage::initialBounded(semExpr, _, _, true, _, _, _)
|
||||
)
|
||||
|
||||
@@ -12,6 +12,9 @@ class SemBasicBlock extends Specific::BasicBlock {
|
||||
/** Holds if this block (transitively) dominates `otherblock`. */
|
||||
final predicate bbDominates(SemBasicBlock otherBlock) { Specific::bbDominates(this, otherBlock) }
|
||||
|
||||
/** Holds if this block has dominance information. */
|
||||
final predicate hasDominanceInformation() { Specific::hasDominanceInformation(this) }
|
||||
|
||||
/** Gets an expression that is evaluated in this basic block. */
|
||||
final SemExpr getAnExpr() { result.getBasicBlock() = this }
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
private import Semantic
|
||||
private import SemanticExprSpecific::SemanticExprConfig as Specific
|
||||
private import SemanticType
|
||||
|
||||
/**
|
||||
* An language-neutral expression.
|
||||
@@ -242,21 +241,8 @@ class SemConvertExpr extends SemUnaryExpr {
|
||||
SemConvertExpr() { opcode instanceof Opcode::Convert }
|
||||
}
|
||||
|
||||
private import semmle.code.cpp.ir.IR as IR
|
||||
|
||||
/** A conversion instruction which is guaranteed to not overflow. */
|
||||
private class SafeConversion extends IR::ConvertInstruction {
|
||||
SafeConversion() {
|
||||
exists(SemType tFrom, SemType tTo |
|
||||
tFrom = getSemanticType(super.getUnary().getResultIRType()) and
|
||||
tTo = getSemanticType(super.getResultIRType()) and
|
||||
conversionCannotOverflow(tFrom, tTo)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class SemCopyValueExpr extends SemUnaryExpr {
|
||||
SemCopyValueExpr() { opcode instanceof Opcode::CopyValue or this instanceof SafeConversion }
|
||||
SemCopyValueExpr() { opcode instanceof Opcode::CopyValue }
|
||||
}
|
||||
|
||||
class SemNegateExpr extends SemUnaryExpr {
|
||||
|
||||
@@ -12,10 +12,87 @@ private import semmle.code.cpp.ir.ValueNumbering
|
||||
module SemanticExprConfig {
|
||||
class Location = Cpp::Location;
|
||||
|
||||
/** A `ConvertInstruction` or a `CopyValueInstruction`. */
|
||||
private class Conversion extends IR::UnaryInstruction {
|
||||
Conversion() {
|
||||
this instanceof IR::CopyValueInstruction
|
||||
or
|
||||
this instanceof IR::ConvertInstruction
|
||||
}
|
||||
|
||||
/** Holds if this instruction converts a value of type `tFrom` to a value of type `tTo`. */
|
||||
predicate converts(SemType tFrom, SemType tTo) {
|
||||
tFrom = getSemanticType(this.getUnary().getResultIRType()) and
|
||||
tTo = getSemanticType(this.getResultIRType())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a conversion-like instruction that consumes `op`, and
|
||||
* which is guaranteed to not overflow.
|
||||
*/
|
||||
private IR::Instruction safeConversion(IR::Operand op) {
|
||||
exists(Conversion conv, SemType tFrom, SemType tTo |
|
||||
conv.converts(tFrom, tTo) and
|
||||
conversionCannotOverflow(tFrom, tTo) and
|
||||
conv.getUnaryOperand() = op and
|
||||
result = conv
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `i1 = i2` or if `i2` is a safe conversion that consumes `i1`. */
|
||||
private predicate idOrSafeConversion(IR::Instruction i1, IR::Instruction i2) {
|
||||
not i1.getResultIRType() instanceof IR::IRVoidType and
|
||||
(
|
||||
i1 = i2
|
||||
or
|
||||
i2 = safeConversion(i1.getAUse()) and
|
||||
i1.getBlock() = i2.getBlock()
|
||||
)
|
||||
}
|
||||
|
||||
module Equiv = QlBuiltins::EquivalenceRelation<IR::Instruction, idOrSafeConversion/2>;
|
||||
|
||||
/**
|
||||
* The expressions on which we perform range analysis.
|
||||
*/
|
||||
class Expr = IR::Instruction;
|
||||
class Expr extends Equiv::EquivalenceClass {
|
||||
/** Gets the n'th instruction in this equivalence class. */
|
||||
private IR::Instruction getInstruction(int n) {
|
||||
result =
|
||||
rank[n + 1](IR::Instruction instr, int i, IR::IRBlock block |
|
||||
this = Equiv::getEquivalenceClass(instr) and block.getInstruction(i) = instr
|
||||
|
|
||||
instr order by i
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = this.getUnconverted().toString() }
|
||||
|
||||
/** Gets the basic block of this expression. */
|
||||
IR::IRBlock getBlock() { result = this.getUnconverted().getBlock() }
|
||||
|
||||
/** Gets the unconverted instruction associated with this expression. */
|
||||
IR::Instruction getUnconverted() { result = this.getInstruction(0) }
|
||||
|
||||
/**
|
||||
* Gets the final instruction associated with this expression. This
|
||||
* represents the result after applying all the safe conversions.
|
||||
*/
|
||||
IR::Instruction getConverted() {
|
||||
exists(int n |
|
||||
result = this.getInstruction(n) and
|
||||
not exists(this.getInstruction(n + 1))
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the type of the result produced by this instruction. */
|
||||
IR::IRType getResultIRType() { result = this.getConverted().getResultIRType() }
|
||||
|
||||
/** Gets the location of the source code for this expression. */
|
||||
Location getLocation() { result = this.getUnconverted().getLocation() }
|
||||
}
|
||||
|
||||
SemBasicBlock getExprBasicBlock(Expr e) { result = getSemanticBasicBlock(e.getBlock()) }
|
||||
|
||||
@@ -62,12 +139,12 @@ module SemanticExprConfig {
|
||||
|
||||
predicate stringLiteral(Expr expr, SemType type, string value) {
|
||||
anyConstantExpr(expr, type, value) and
|
||||
expr instanceof IR::StringConstantInstruction
|
||||
expr.getUnconverted() instanceof IR::StringConstantInstruction
|
||||
}
|
||||
|
||||
predicate binaryExpr(Expr expr, Opcode opcode, SemType type, Expr leftOperand, Expr rightOperand) {
|
||||
exists(IR::BinaryInstruction instr |
|
||||
instr = expr and
|
||||
instr = expr.getUnconverted() and
|
||||
type = getSemanticType(instr.getResultIRType()) and
|
||||
leftOperand = getSemanticExpr(instr.getLeft()) and
|
||||
rightOperand = getSemanticExpr(instr.getRight()) and
|
||||
@@ -77,14 +154,14 @@ module SemanticExprConfig {
|
||||
}
|
||||
|
||||
predicate unaryExpr(Expr expr, Opcode opcode, SemType type, Expr operand) {
|
||||
exists(IR::UnaryInstruction instr | instr = expr |
|
||||
exists(IR::UnaryInstruction instr | instr = expr.getUnconverted() |
|
||||
type = getSemanticType(instr.getResultIRType()) and
|
||||
operand = getSemanticExpr(instr.getUnary()) and
|
||||
// REVIEW: Merge the two operand types.
|
||||
opcode.toString() = instr.getOpcode().toString()
|
||||
)
|
||||
or
|
||||
exists(IR::StoreInstruction instr | instr = expr |
|
||||
exists(IR::StoreInstruction instr | instr = expr.getUnconverted() |
|
||||
type = getSemanticType(instr.getResultIRType()) and
|
||||
operand = getSemanticExpr(instr.getSourceValue()) and
|
||||
opcode instanceof Opcode::Store
|
||||
@@ -93,13 +170,13 @@ module SemanticExprConfig {
|
||||
|
||||
predicate nullaryExpr(Expr expr, Opcode opcode, SemType type) {
|
||||
exists(IR::LoadInstruction load |
|
||||
load = expr and
|
||||
load = expr.getUnconverted() and
|
||||
type = getSemanticType(load.getResultIRType()) and
|
||||
opcode instanceof Opcode::Load
|
||||
)
|
||||
or
|
||||
exists(IR::InitializeParameterInstruction init |
|
||||
init = expr and
|
||||
init = expr.getUnconverted() and
|
||||
type = getSemanticType(init.getResultIRType()) and
|
||||
opcode instanceof Opcode::InitializeParameter
|
||||
)
|
||||
@@ -122,6 +199,8 @@ module SemanticExprConfig {
|
||||
dominator.dominates(dominated)
|
||||
}
|
||||
|
||||
predicate hasDominanceInformation(BasicBlock block) { any() }
|
||||
|
||||
private predicate id(Cpp::Locatable x, Cpp::Locatable y) { x = y }
|
||||
|
||||
private predicate idOf(Cpp::Locatable x, int y) = equivalenceRelation(id/2)(x, y)
|
||||
@@ -130,7 +209,17 @@ module SemanticExprConfig {
|
||||
|
||||
newtype TSsaVariable =
|
||||
TSsaInstruction(IR::Instruction instr) { instr.hasMemoryResult() } or
|
||||
TSsaOperand(IR::PhiInputOperand op) { op.isDefinitionInexact() }
|
||||
TSsaOperand(IR::Operand op) { op.isDefinitionInexact() } or
|
||||
TSsaPointerArithmeticGuard(ValueNumber instr) {
|
||||
exists(Guard g, IR::Operand use |
|
||||
use = instr.getAUse() and use.getIRType() instanceof IR::IRAddressType
|
||||
|
|
||||
g.comparesLt(use, _, _, _, _) or
|
||||
g.comparesLt(_, use, _, _, _) or
|
||||
g.comparesEq(use, _, _, _, _) or
|
||||
g.comparesEq(_, use, _, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
class SsaVariable extends TSsaVariable {
|
||||
string toString() { none() }
|
||||
@@ -139,7 +228,9 @@ module SemanticExprConfig {
|
||||
|
||||
IR::Instruction asInstruction() { none() }
|
||||
|
||||
IR::PhiInputOperand asOperand() { none() }
|
||||
ValueNumber asPointerArithGuard() { none() }
|
||||
|
||||
IR::Operand asOperand() { none() }
|
||||
}
|
||||
|
||||
class SsaInstructionVariable extends SsaVariable, TSsaInstruction {
|
||||
@@ -154,8 +245,20 @@ module SemanticExprConfig {
|
||||
final override IR::Instruction asInstruction() { result = instr }
|
||||
}
|
||||
|
||||
class SsaPointerArithmeticGuard extends SsaVariable, TSsaPointerArithmeticGuard {
|
||||
ValueNumber vn;
|
||||
|
||||
SsaPointerArithmeticGuard() { this = TSsaPointerArithmeticGuard(vn) }
|
||||
|
||||
final override string toString() { result = vn.toString() }
|
||||
|
||||
final override Location getLocation() { result = vn.getLocation() }
|
||||
|
||||
final override ValueNumber asPointerArithGuard() { result = vn }
|
||||
}
|
||||
|
||||
class SsaOperand extends SsaVariable, TSsaOperand {
|
||||
IR::PhiInputOperand op;
|
||||
IR::Operand op;
|
||||
|
||||
SsaOperand() { this = TSsaOperand(op) }
|
||||
|
||||
@@ -163,7 +266,7 @@ module SemanticExprConfig {
|
||||
|
||||
final override Location getLocation() { result = op.getLocation() }
|
||||
|
||||
final override IR::PhiInputOperand asOperand() { result = op }
|
||||
final override IR::Operand asOperand() { result = op }
|
||||
}
|
||||
|
||||
predicate explicitUpdate(SsaVariable v, Expr sourceExpr) {
|
||||
@@ -186,29 +289,97 @@ module SemanticExprConfig {
|
||||
)
|
||||
}
|
||||
|
||||
Expr getAUse(SsaVariable v) { result.(IR::LoadInstruction).getSourceValue() = v.asInstruction() }
|
||||
Expr getAUse(SsaVariable v) {
|
||||
result.getUnconverted().(IR::LoadInstruction).getSourceValue() = v.asInstruction()
|
||||
or
|
||||
result.getUnconverted() = v.asPointerArithGuard().getAnInstruction()
|
||||
}
|
||||
|
||||
SemType getSsaVariableType(SsaVariable v) {
|
||||
result = getSemanticType(v.asInstruction().getResultIRType())
|
||||
or
|
||||
result = getSemanticType(v.asOperand().getUse().getResultIRType())
|
||||
}
|
||||
|
||||
BasicBlock getSsaVariableBasicBlock(SsaVariable v) {
|
||||
result = v.asInstruction().getBlock()
|
||||
or
|
||||
result = v.asOperand().getAnyDef().getBlock()
|
||||
result = v.asOperand().getUse().getBlock()
|
||||
}
|
||||
|
||||
/** Holds if `inp` is an input to the phi node along the edge originating in `bb`. */
|
||||
predicate phiInputFromBlock(SsaVariable phi, SsaVariable inp, BasicBlock bb) {
|
||||
private newtype TReadPosition =
|
||||
TReadPositionBlock(IR::IRBlock block) or
|
||||
TReadPositionPhiInputEdge(IR::IRBlock pred, IR::IRBlock succ) {
|
||||
exists(IR::PhiInputOperand input |
|
||||
pred = input.getPredecessorBlock() and
|
||||
succ = input.getUse().getBlock()
|
||||
)
|
||||
}
|
||||
|
||||
class SsaReadPosition extends TReadPosition {
|
||||
string toString() { none() }
|
||||
|
||||
Location getLocation() { none() }
|
||||
|
||||
predicate hasRead(SsaVariable v) { none() }
|
||||
}
|
||||
|
||||
private class SsaReadPositionBlock extends SsaReadPosition, TReadPositionBlock {
|
||||
IR::IRBlock block;
|
||||
|
||||
SsaReadPositionBlock() { this = TReadPositionBlock(block) }
|
||||
|
||||
final override string toString() { result = block.toString() }
|
||||
|
||||
final override Location getLocation() { result = block.getLocation() }
|
||||
|
||||
final override predicate hasRead(SsaVariable v) {
|
||||
exists(IR::Operand operand |
|
||||
operand.getDef() = v.asInstruction() or
|
||||
operand.getDef() = v.asPointerArithGuard().getAnInstruction()
|
||||
|
|
||||
not operand instanceof IR::PhiInputOperand and
|
||||
operand.getUse().getBlock() = block
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class SsaReadPositionPhiInputEdge extends SsaReadPosition, TReadPositionPhiInputEdge {
|
||||
IR::IRBlock pred;
|
||||
IR::IRBlock succ;
|
||||
|
||||
SsaReadPositionPhiInputEdge() { this = TReadPositionPhiInputEdge(pred, succ) }
|
||||
|
||||
final override string toString() { result = pred.toString() + "->" + succ.toString() }
|
||||
|
||||
final override Location getLocation() { result = succ.getLocation() }
|
||||
|
||||
final override predicate hasRead(SsaVariable v) {
|
||||
exists(IR::PhiInputOperand operand |
|
||||
operand.getDef() = v.asInstruction() or
|
||||
operand.getDef() = v.asPointerArithGuard().getAnInstruction()
|
||||
|
|
||||
operand.getPredecessorBlock() = pred and
|
||||
operand.getUse().getBlock() = succ
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
predicate hasReadOfSsaVariable(SsaReadPosition pos, SsaVariable v) { pos.hasRead(v) }
|
||||
|
||||
predicate readBlock(SsaReadPosition pos, BasicBlock block) { pos = TReadPositionBlock(block) }
|
||||
|
||||
predicate phiInputEdge(SsaReadPosition pos, BasicBlock origBlock, BasicBlock phiBlock) {
|
||||
pos = TReadPositionPhiInputEdge(origBlock, phiBlock)
|
||||
}
|
||||
|
||||
predicate phiInput(SsaReadPosition pos, SsaVariable phi, SsaVariable input) {
|
||||
exists(IR::PhiInputOperand operand |
|
||||
bb = operand.getPredecessorBlock() and
|
||||
pos = TReadPositionPhiInputEdge(operand.getPredecessorBlock(), operand.getUse().getBlock())
|
||||
|
|
||||
phi.asInstruction() = operand.getUse() and
|
||||
(
|
||||
inp.asInstruction() = operand.getDef()
|
||||
input.asInstruction() = operand.getDef()
|
||||
or
|
||||
inp.asOperand() = operand
|
||||
input.asOperand() = operand
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -262,7 +433,7 @@ module SemanticExprConfig {
|
||||
}
|
||||
|
||||
/** Gets the expression associated with `instr`. */
|
||||
SemExpr getSemanticExpr(IR::Instruction instr) { result = instr }
|
||||
SemExpr getSemanticExpr(IR::Instruction instr) { result = Equiv::getEquivalenceClass(instr) }
|
||||
}
|
||||
|
||||
predicate getSemanticExpr = SemanticExprConfig::getSemanticExpr/1;
|
||||
|
||||
@@ -35,4 +35,32 @@ predicate semImplies_v2(SemGuard g1, boolean b1, SemGuard g2, boolean b2) {
|
||||
Specific::implies_v2(g1, b1, g2, b2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `guard` directly controls the position `controlled` with the
|
||||
* value `testIsTrue`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate semGuardDirectlyControlsSsaRead(
|
||||
SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue
|
||||
) {
|
||||
guard.directlyControls(controlled.(SemSsaReadPositionBlock).getBlock(), testIsTrue)
|
||||
or
|
||||
exists(SemSsaReadPositionPhiInputEdge controlledEdge | controlledEdge = controlled |
|
||||
guard.directlyControls(controlledEdge.getOrigBlock(), testIsTrue) or
|
||||
guard.hasBranchEdge(controlledEdge.getOrigBlock(), controlledEdge.getPhiBlock(), testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `guard` controls the position `controlled` with the value `testIsTrue`.
|
||||
*/
|
||||
predicate semGuardControlsSsaRead(SemGuard guard, SemSsaReadPosition controlled, boolean testIsTrue) {
|
||||
semGuardDirectlyControlsSsaRead(guard, controlled, testIsTrue)
|
||||
or
|
||||
exists(SemGuard guard0, boolean testIsTrue0 |
|
||||
semImplies_v2(guard0, testIsTrue0, guard, testIsTrue) and
|
||||
semGuardControlsSsaRead(guard0, controlled, testIsTrue0)
|
||||
)
|
||||
}
|
||||
|
||||
SemGuard semGetComparisonGuard(SemRelationalExpr e) { result = Specific::comparisonGuard(e) }
|
||||
|
||||
@@ -22,15 +22,75 @@ class SemSsaExplicitUpdate extends SemSsaVariable {
|
||||
|
||||
SemSsaExplicitUpdate() { Specific::explicitUpdate(this, sourceExpr) }
|
||||
|
||||
final SemExpr getDefiningExpr() { result = sourceExpr }
|
||||
final SemExpr getSourceExpr() { result = sourceExpr }
|
||||
}
|
||||
|
||||
class SemSsaPhiNode extends SemSsaVariable {
|
||||
SemSsaPhiNode() { Specific::phi(this) }
|
||||
|
||||
final SemSsaVariable getAPhiInput() { result = Specific::getAPhiInput(this) }
|
||||
|
||||
final predicate hasInputFromBlock(SemSsaVariable inp, SemBasicBlock bb) {
|
||||
Specific::phiInputFromBlock(this, inp, bb)
|
||||
}
|
||||
}
|
||||
|
||||
class SemSsaReadPosition instanceof Specific::SsaReadPosition {
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
final Specific::Location getLocation() { result = super.getLocation() }
|
||||
|
||||
final predicate hasReadOfVar(SemSsaVariable var) { Specific::hasReadOfSsaVariable(this, var) }
|
||||
}
|
||||
|
||||
class SemSsaReadPositionPhiInputEdge extends SemSsaReadPosition {
|
||||
SemBasicBlock origBlock;
|
||||
SemBasicBlock phiBlock;
|
||||
|
||||
SemSsaReadPositionPhiInputEdge() { Specific::phiInputEdge(this, origBlock, phiBlock) }
|
||||
|
||||
predicate phiInput(SemSsaPhiNode phi, SemSsaVariable inp) { Specific::phiInput(this, phi, inp) }
|
||||
|
||||
SemBasicBlock getOrigBlock() { result = origBlock }
|
||||
|
||||
SemBasicBlock getPhiBlock() { result = phiBlock }
|
||||
}
|
||||
|
||||
class SemSsaReadPositionBlock extends SemSsaReadPosition {
|
||||
SemBasicBlock block;
|
||||
|
||||
SemSsaReadPositionBlock() { Specific::readBlock(this, block) }
|
||||
|
||||
SemBasicBlock getBlock() { result = block }
|
||||
|
||||
SemExpr getAnExpr() { result = this.getBlock().getAnExpr() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along a back edge.
|
||||
*/
|
||||
predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge) {
|
||||
edge.phiInput(phi, inp) and
|
||||
// Conservatively assume that every edge is a back edge if we don't have dominance information.
|
||||
(
|
||||
phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or
|
||||
irreducibleSccEdge(edge.getOrigBlock(), phi.getBasicBlock()) or
|
||||
not edge.getOrigBlock().hasDominanceInformation()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the edge from b1 to b2 is part of a multiple-entry cycle in an irreducible control flow
|
||||
* graph.
|
||||
*
|
||||
* An ireducible control flow graph is one where the usual dominance-based back edge detection does
|
||||
* not work, because there is a cycle with multiple entry points, meaning there are
|
||||
* mutually-reachable basic blocks where neither dominates the other. For such a graph, we first
|
||||
* remove all detectable back-edges using the normal condition that the predecessor block is
|
||||
* dominated by the successor block, then mark all edges in a cycle in the resulting graph as back
|
||||
* edges.
|
||||
*/
|
||||
private predicate irreducibleSccEdge(SemBasicBlock b1, SemBasicBlock b2) {
|
||||
trimmedEdge(b1, b2) and trimmedEdge+(b2, b1)
|
||||
}
|
||||
|
||||
private predicate trimmedEdge(SemBasicBlock pred, SemBasicBlock succ) {
|
||||
pred.getASuccessor() = succ and
|
||||
not succ.bbDominates(pred)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
/**
|
||||
* Provides inferences of the form: `e` equals `b + v` modulo `m` where `e` is
|
||||
* an expression, `b` is a `Bound` (typically zero or the value of an SSA
|
||||
* variable), and `v` is an integer in the range `[0 .. m-1]`.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The main recursion has base cases in both `ssaModulus` (for guarded reads) and `semExprModulus`
|
||||
* (for constant values). The most interesting recursive case is `phiModulusRankStep`, which
|
||||
* handles phi inputs.
|
||||
*/
|
||||
|
||||
private import ModulusAnalysisSpecific::Private
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticLocation
|
||||
private import ConstantAnalysis
|
||||
private import RangeUtils
|
||||
private import codeql.rangeanalysis.RangeAnalysis
|
||||
private import RangeAnalysisImpl
|
||||
|
||||
module ModulusAnalysis<DeltaSig D, BoundSig<SemLocation, Sem, D> Bounds, UtilSig<Sem, D> U> {
|
||||
pragma[nomagic]
|
||||
private predicate valueFlowStepSsaEqFlowCond(
|
||||
SemSsaReadPosition pos, SemSsaVariable v, SemExpr e, int delta
|
||||
) {
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
guard = U::semEqFlowCond(v, e, D::fromInt(delta), true, testIsTrue) and
|
||||
semGuardDirectlyControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e + delta` equals `v` at `pos`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate valueFlowStepSsa(SemSsaVariable v, SemSsaReadPosition pos, SemExpr e, int delta) {
|
||||
U::semSsaUpdateStep(v, e, D::fromInt(delta)) and pos.hasReadOfVar(v)
|
||||
or
|
||||
pos.hasReadOfVar(v) and
|
||||
valueFlowStepSsaEqFlowCond(pos, v, e, delta)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `add` is the addition of `larg` and `rarg`, neither of which are
|
||||
* `ConstantIntegerExpr`s.
|
||||
*/
|
||||
private predicate nonConstAddition(SemExpr add, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemAddExpr a | a = add |
|
||||
larg = a.getLeftOperand() and
|
||||
rarg = a.getRightOperand()
|
||||
) and
|
||||
not larg instanceof SemConstantIntegerExpr and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sub` is the subtraction of `larg` and `rarg`, where `rarg` is not
|
||||
* a `ConstantIntegerExpr`.
|
||||
*/
|
||||
private predicate nonConstSubtraction(SemExpr sub, SemExpr larg, SemExpr rarg) {
|
||||
exists(SemSubExpr s | s = sub |
|
||||
larg = s.getLeftOperand() and
|
||||
rarg = s.getRightOperand()
|
||||
) and
|
||||
not rarg instanceof SemConstantIntegerExpr
|
||||
}
|
||||
|
||||
/** Gets an expression that is the remainder modulo `mod` of `arg`. */
|
||||
private SemExpr modExpr(SemExpr arg, int mod) {
|
||||
exists(SemRemExpr rem |
|
||||
result = rem and
|
||||
arg = rem.getLeftOperand() and
|
||||
rem.getRightOperand().(SemConstantIntegerExpr).getIntValue() = mod and
|
||||
mod >= 2
|
||||
)
|
||||
or
|
||||
exists(SemConstantIntegerExpr c |
|
||||
mod = 2.pow([1 .. 30]) and
|
||||
c.getIntValue() = mod - 1 and
|
||||
result.(SemBitAndExpr).hasOperands(arg, c)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a guard that tests whether `v` is congruent with `val` modulo `mod` on
|
||||
* its `testIsTrue` branch.
|
||||
*/
|
||||
private SemGuard moduloCheck(SemSsaVariable v, int val, int mod, boolean testIsTrue) {
|
||||
exists(SemExpr rem, SemConstantIntegerExpr c, int r, boolean polarity |
|
||||
result.isEquality(rem, c, polarity) and
|
||||
c.getIntValue() = r and
|
||||
rem = modExpr(v.getAUse(), mod) and
|
||||
(
|
||||
testIsTrue = polarity and val = r
|
||||
or
|
||||
testIsTrue = polarity.booleanNot() and
|
||||
mod = 2 and
|
||||
val = 1 - r and
|
||||
(r = 0 or r = 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a guard ensures that `v` at `pos` is congruent with `val` modulo `mod`.
|
||||
*/
|
||||
private predicate moduloGuardedRead(SemSsaVariable v, SemSsaReadPosition pos, int val, int mod) {
|
||||
exists(SemGuard guard, boolean testIsTrue |
|
||||
pos.hasReadOfVar(v) and
|
||||
guard = moduloCheck(v, val, mod, testIsTrue) and
|
||||
semGuardControlsSsaRead(guard, pos, testIsTrue)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `factor` is a power of 2 that divides `mask`. */
|
||||
bindingset[mask]
|
||||
private predicate andmaskFactor(int mask, int factor) {
|
||||
mask % factor = 0 and
|
||||
factor = 2.pow([1 .. 30])
|
||||
}
|
||||
|
||||
/** Holds if `e` is evenly divisible by `factor`. */
|
||||
private predicate evenlyDivisibleExpr(SemExpr e, int factor) {
|
||||
exists(SemConstantIntegerExpr c, int k | k = c.getIntValue() |
|
||||
e.(SemMulExpr).getAnOperand() = c and factor = k.abs() and factor >= 2
|
||||
or
|
||||
e.(SemShiftLeftExpr).getRightOperand() = c and factor = 2.pow(k) and k > 0
|
||||
or
|
||||
e.(SemBitAndExpr).getAnOperand() = c and factor = max(int f | andmaskFactor(k, f))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remainder of `val` modulo `mod`.
|
||||
*
|
||||
* For `mod = 0` the result equals `val` and for `mod > 1` the result is within
|
||||
* the range `[0 .. mod-1]`.
|
||||
*/
|
||||
bindingset[val, mod]
|
||||
private int remainder(int val, int mod) {
|
||||
mod = 0 and result = val
|
||||
or
|
||||
mod > 1 and result = ((val % mod) + mod) % mod
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` and equals `phi` modulo `mod` along `edge`.
|
||||
*/
|
||||
private predicate phiSelfModulus(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int mod
|
||||
) {
|
||||
exists(Bounds::SemSsaBound phibound, int v, int m |
|
||||
edge.phiInput(phi, inp) and
|
||||
phibound.getAVariable() = phi and
|
||||
ssaModulus(inp, edge, phibound, v, m) and
|
||||
mod = m.gcd(v) and
|
||||
mod != 1
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `b + val` modulo `mod` is a candidate congruence class for `phi`.
|
||||
*/
|
||||
private predicate phiModulusInit(SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(phi, inp) and
|
||||
ssaModulus(inp, edge, b, val, mod)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all inputs to `phi` numbered `1` to `rix` are equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate phiModulusRankStep(
|
||||
SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod, int rix
|
||||
) {
|
||||
/*
|
||||
* base case. If any phi input is equal to `b + val` modulo `mod`, that's a potential congruence
|
||||
* class for the phi node.
|
||||
*/
|
||||
|
||||
rix = 0 and
|
||||
phiModulusInit(phi, b, val, mod)
|
||||
or
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int v1, int m1 |
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
|
|
||||
/*
|
||||
* Recursive case. If `inp` = `b + v2` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be the congruence class of `v1` modulo
|
||||
* the greatest common denominator of `m1`, `m2`, and `v1 - v2`.
|
||||
*/
|
||||
|
||||
exists(int v2, int m2 |
|
||||
rankedPhiInput(pragma[only_bind_out](phi), inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
ssaModulus(inp, edge, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2)
|
||||
)
|
||||
or
|
||||
/*
|
||||
* Recursive case. If `inp` = `phi` mod `m2`, we combine that with the preceding potential
|
||||
* congruence class `b + v1` mod `m1`. The result will be a congruence class modulo the greatest
|
||||
* common denominator of `m1` and `m2`.
|
||||
*/
|
||||
|
||||
exists(int m2 |
|
||||
rankedPhiInput(phi, inp, edge, rix) and
|
||||
phiModulusRankStep(phi, b, v1, m1, rix - 1) and
|
||||
phiSelfModulus(phi, inp, edge, m2) and
|
||||
mod = m1.gcd(m2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `phi` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate phiModulus(SemSsaPhiNode phi, Bounds::SemBound b, int val, int mod) {
|
||||
exists(int r |
|
||||
maxPhiInputRank(phi, r) and
|
||||
phiModulusRankStep(phi, b, val, mod, r)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `v` at `pos` is equal to `b + val` modulo `mod`.
|
||||
*/
|
||||
private predicate ssaModulus(
|
||||
SemSsaVariable v, SemSsaReadPosition pos, Bounds::SemBound b, int val, int mod
|
||||
) {
|
||||
phiModulus(v, b, val, mod) and pos.hasReadOfVar(v)
|
||||
or
|
||||
b.(Bounds::SemSsaBound).getAVariable() = v and pos.hasReadOfVar(v) and val = 0 and mod = 0
|
||||
or
|
||||
exists(SemExpr e, int val0, int delta |
|
||||
semExprModulus(e, b, val0, mod) and
|
||||
valueFlowStepSsa(v, pos, e, delta) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
moduloGuardedRead(v, pos, val, mod) and b instanceof Bounds::SemZeroBound
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `e` is equal to `b + val` modulo `mod`.
|
||||
*
|
||||
* There are two cases for the modulus:
|
||||
* - `mod = 0`: The equality `e = b + val` is an ordinary equality.
|
||||
* - `mod > 1`: `val` lies within the range `[0 .. mod-1]`.
|
||||
*/
|
||||
cached
|
||||
predicate semExprModulus(SemExpr e, Bounds::SemBound b, int val, int mod) {
|
||||
not ignoreExprModulus(e) and
|
||||
(
|
||||
e = b.getExpr(D::fromInt(val)) and mod = 0
|
||||
or
|
||||
evenlyDivisibleExpr(e, mod) and
|
||||
val = 0 and
|
||||
b instanceof Bounds::SemZeroBound
|
||||
or
|
||||
exists(SemSsaVariable v, SemSsaReadPositionBlock bb |
|
||||
ssaModulus(v, bb, b, val, mod) and
|
||||
e = v.getAUse() and
|
||||
bb.getAnExpr() = e
|
||||
)
|
||||
or
|
||||
exists(SemExpr mid, int val0, int delta |
|
||||
semExprModulus(mid, b, val0, mod) and
|
||||
U::semValueFlowStep(e, mid, D::fromInt(delta)) and
|
||||
val = remainder(val0 + delta, mod)
|
||||
)
|
||||
or
|
||||
exists(SemConditionalExpr cond, int v1, int v2, int m1, int m2 |
|
||||
cond = e and
|
||||
condExprBranchModulus(cond, true, b, v1, m1) and
|
||||
condExprBranchModulus(cond, false, b, v2, m2) and
|
||||
mod = m1.gcd(m2).gcd(v1 - v2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1, mod)
|
||||
)
|
||||
or
|
||||
exists(Bounds::SemBound b1, Bounds::SemBound b2, int v1, int v2, int m1, int m2 |
|
||||
addModulus(e, true, b1, v1, m1) and
|
||||
addModulus(e, false, b2, v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 + v2, mod)
|
||||
|
|
||||
b = b1 and b2 instanceof Bounds::SemZeroBound
|
||||
or
|
||||
b = b2 and b1 instanceof Bounds::SemZeroBound
|
||||
)
|
||||
or
|
||||
exists(int v1, int v2, int m1, int m2 |
|
||||
subModulus(e, true, b, v1, m1) and
|
||||
subModulus(e, false, any(Bounds::SemZeroBound zb), v2, m2) and
|
||||
mod = m1.gcd(m2) and
|
||||
mod != 1 and
|
||||
val = remainder(v1 - v2, mod)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate condExprBranchModulus(
|
||||
SemConditionalExpr cond, boolean branch, Bounds::SemBound b, int val, int mod
|
||||
) {
|
||||
semExprModulus(cond.getBranchExpr(branch), b, val, mod)
|
||||
}
|
||||
|
||||
private predicate addModulus(SemExpr add, boolean isLeft, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstAddition(add, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
|
||||
private predicate subModulus(SemExpr sub, boolean isLeft, Bounds::SemBound b, int val, int mod) {
|
||||
exists(SemExpr larg, SemExpr rarg | nonConstSubtraction(sub, larg, rarg) |
|
||||
semExprModulus(larg, b, val, mod) and isLeft = true
|
||||
or
|
||||
semExprModulus(rarg, b, val, mod) and isLeft = false
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* C++-specific implementation of modulus analysis.
|
||||
*/
|
||||
module Private {
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
|
||||
predicate ignoreExprModulus(SemExpr e) { none() }
|
||||
}
|
||||
@@ -8,6 +8,14 @@ private import RangeAnalysisImpl
|
||||
private import codeql.rangeanalysis.RangeAnalysis
|
||||
|
||||
module CppLangImplConstant implements LangSig<Sem, FloatDelta> {
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadCopy(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore the bound on this expression.
|
||||
*
|
||||
@@ -16,13 +24,70 @@ module CppLangImplConstant implements LangSig<Sem, FloatDelta> {
|
||||
*/
|
||||
predicate ignoreExprBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore any inferred zero lower bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreZeroLowerBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
|
||||
|
||||
/**
|
||||
* Adds additional results to `ssaRead()` that are specific to Java.
|
||||
*
|
||||
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
|
||||
* in exactly the same way as the old Java implementation. Once the new implementation matches the
|
||||
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
|
||||
* or not they come from a post-increment/decrement expression.
|
||||
*/
|
||||
SemExpr specificSsaRead(SemSsaVariable v, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
|
||||
*/
|
||||
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
|
||||
@@ -51,34 +52,20 @@ module Sem implements Semantic {
|
||||
|
||||
class NegateExpr = SemNegateExpr;
|
||||
|
||||
class PreIncExpr = SemAddOneExpr;
|
||||
class AddOneExpr = SemAddOneExpr;
|
||||
|
||||
class PreDecExpr = SemSubOneExpr;
|
||||
|
||||
class PostIncExpr extends SemUnaryExpr {
|
||||
PostIncExpr() { none() }
|
||||
}
|
||||
|
||||
class PostDecExpr extends SemUnaryExpr {
|
||||
PostDecExpr() { none() }
|
||||
}
|
||||
|
||||
class CopyValueExpr extends SemUnaryExpr {
|
||||
CopyValueExpr() { this instanceof SemCopyValueExpr or this instanceof SemStoreExpr }
|
||||
}
|
||||
class SubOneExpr = SemSubOneExpr;
|
||||
|
||||
class ConditionalExpr = SemConditionalExpr;
|
||||
|
||||
class BasicBlock = SemBasicBlock;
|
||||
|
||||
BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() }
|
||||
|
||||
int getBlockId1(BasicBlock bb) { result = bb.getUniqueId() }
|
||||
|
||||
class Guard = SemGuard;
|
||||
|
||||
predicate implies_v2 = semImplies_v2/4;
|
||||
|
||||
predicate guardDirectlyControlsSsaRead = semGuardDirectlyControlsSsaRead/3;
|
||||
|
||||
class Type = SemType;
|
||||
|
||||
class IntegerType = SemIntegerType;
|
||||
@@ -87,17 +74,19 @@ 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() }
|
||||
class SsaReadPosition = SemSsaReadPosition;
|
||||
|
||||
class SsaReadPositionPhiInputEdge = SemSsaReadPositionPhiInputEdge;
|
||||
|
||||
class SsaReadPositionBlock = SemSsaReadPositionBlock;
|
||||
|
||||
predicate backEdge = semBackEdge/3;
|
||||
|
||||
predicate conversionCannotOverflow(Type fromType, Type toType) {
|
||||
SemanticType::conversionCannotOverflow(fromType, toType)
|
||||
@@ -106,7 +95,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> {
|
||||
@@ -127,7 +116,7 @@ module ConstantBounds implements BoundSig<SemLocation, Sem, FloatDelta> {
|
||||
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
|
||||
|
||||
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
|
||||
SemSsaVariable getVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +134,7 @@ module RelativeBounds implements BoundSig<SemLocation, Sem, FloatDelta> {
|
||||
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
|
||||
|
||||
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
|
||||
SemSsaVariable getVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,24 +150,26 @@ module AllBounds implements BoundSig<SemLocation, Sem, FloatDelta> {
|
||||
class SemZeroBound extends SemBound instanceof SemanticBound::SemZeroBound { }
|
||||
|
||||
class SemSsaBound extends SemBound instanceof SemanticBound::SemSsaBound {
|
||||
SemSsaVariable getVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
SemSsaVariable getAVariable() { result = this.(SemanticBound::SemSsaBound).getAVariable() }
|
||||
}
|
||||
}
|
||||
|
||||
private module ModulusAnalysisInstantiated implements ModulusAnalysisSig<Sem> {
|
||||
class ModBound = AllBounds::SemBound;
|
||||
|
||||
private import codeql.rangeanalysis.ModulusAnalysis as MA
|
||||
import MA::ModulusAnalysis<SemLocation, Sem, FloatDelta, AllBounds>
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.ModulusAnalysis as MA
|
||||
import MA::ModulusAnalysis<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
|
||||
|
||||
@@ -9,6 +9,14 @@ private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
private import codeql.rangeanalysis.RangeAnalysis
|
||||
|
||||
module CppLangImplRelative implements LangSig<Sem, FloatDelta> {
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadCopy(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Ignore the bound on this expression.
|
||||
*
|
||||
@@ -48,13 +56,70 @@ module CppLangImplRelative implements LangSig<Sem, FloatDelta> {
|
||||
t instanceof SemFloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore any inferred zero lower bound on this expression.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreZeroLowerBound(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadArithmeticExpr(SemExpr e) { none() }
|
||||
|
||||
/**
|
||||
* Holds if the specified variable should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
* This predicate is to keep the results identical to the original Java implementation. It should be
|
||||
* removed once we have the new implementation matching the old results exactly.
|
||||
*/
|
||||
predicate ignoreSsaReadAssignment(SemSsaVariable v) { none() }
|
||||
|
||||
/**
|
||||
* Adds additional results to `ssaRead()` that are specific to Java.
|
||||
*
|
||||
* This predicate handles propagation of offsets for post-increment and post-decrement expressions
|
||||
* in exactly the same way as the old Java implementation. Once the new implementation matches the
|
||||
* old one, we should remove this predicate and propagate deltas for all similar patterns, whether
|
||||
* or not they come from a post-increment/decrement expression.
|
||||
*/
|
||||
SemExpr specificSsaRead(SemSsaVariable v, float delta) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e >= bound` (if `upper = false`) or `e <= bound` (if `upper = true`).
|
||||
*/
|
||||
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,171 @@
|
||||
/**
|
||||
* 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`.
|
||||
*/
|
||||
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()) and
|
||||
not Lang::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
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()) and
|
||||
not Lang::ignoreSsaReadArithmeticExpr(result)
|
||||
)
|
||||
or
|
||||
result = v.(SemSsaExplicitUpdate).getSourceExpr() and
|
||||
delta = D::fromFloat(0) and
|
||||
not Lang::ignoreSsaReadAssignment(v)
|
||||
or
|
||||
result = Lang::specificSsaRead(v, delta)
|
||||
or
|
||||
result.(SemCopyValueExpr).getOperand() = semSsaRead(v, delta) and
|
||||
not Lang::ignoreSsaReadCopy(result)
|
||||
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()
|
||||
}
|
||||
|
||||
import Ranking
|
||||
}
|
||||
|
||||
import Ranking
|
||||
|
||||
module Ranking {
|
||||
/**
|
||||
* Holds if `rix` is the number of input edges to `phi`.
|
||||
*/
|
||||
predicate maxPhiInputRank(SemSsaPhiNode phi, int rix) {
|
||||
rix = max(int r | rankedPhiInput(phi, _, _, r))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
|
||||
* in an arbitrary 1-based numbering of the input edges to `phi`.
|
||||
*/
|
||||
predicate rankedPhiInput(
|
||||
SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge, int r
|
||||
) {
|
||||
edge.phiInput(phi, inp) and
|
||||
edge =
|
||||
rank[r](SemSsaReadPositionPhiInputEdge e |
|
||||
e.phiInput(phi, _)
|
||||
|
|
||||
e order by e.getOrigBlock().getUniqueId()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -11,11 +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> {
|
||||
private import codeql.rangeanalysis.internal.RangeUtils::MakeUtils<Sem, D>
|
||||
|
||||
module SignAnalysis<DeltaSig D, UtilSig<Sem, D> Utils> {
|
||||
/**
|
||||
* An SSA definition for which the analysis can compute the sign.
|
||||
*
|
||||
@@ -38,13 +37,13 @@ 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. */
|
||||
private class PhiSignDef extends FlowSignDef instanceof SemSsaPhiNode {
|
||||
final override Sign getSign() {
|
||||
exists(SemSsaVariable inp, SsaReadPositionPhiInputEdge edge |
|
||||
exists(SemSsaVariable inp, SemSsaReadPositionPhiInputEdge edge |
|
||||
edge.phiInput(this, inp) and
|
||||
result = semSsaSign(inp, edge)
|
||||
)
|
||||
@@ -147,7 +146,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)
|
||||
@@ -169,11 +168,11 @@ module SignAnalysis<DeltaSig D> {
|
||||
override Sign getSignRestriction() {
|
||||
// Propagate via SSA
|
||||
// Propagate the sign from the def of `v`, incorporating any inference from guards.
|
||||
result = semSsaSign(v, any(SsaReadPositionBlock bb | bb.getBlock().getAnExpr() = this))
|
||||
result = semSsaSign(v, any(SemSsaReadPositionBlock bb | bb.getAnExpr() = this))
|
||||
or
|
||||
// No block for this read. Just use the sign of the def.
|
||||
// REVIEW: How can this happen?
|
||||
not exists(SsaReadPositionBlock bb | bb.getBlock().getAnExpr() = this) and
|
||||
not exists(SemSsaReadPositionBlock bb | bb.getAnExpr() = this) and
|
||||
result = semSsaDefSign(v)
|
||||
}
|
||||
}
|
||||
@@ -202,7 +201,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 +274,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
|
||||
)
|
||||
@@ -289,21 +288,21 @@ module SignAnalysis<DeltaSig D> {
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate lowerBound(
|
||||
SemExpr lowerbound, SemSsaVariable v, SsaReadPosition pos, boolean isStrict
|
||||
SemExpr lowerbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
guardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(lowerbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getLesserOperand() = lowerbound and
|
||||
comp.getGreaterOperand() = ssaRead(v, D::fromInt(0)) and
|
||||
comp.getGreaterOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getGreaterOperand() = lowerbound and
|
||||
comp.getLesserOperand() = ssaRead(v, D::fromInt(0)) and
|
||||
comp.getLesserOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
@@ -313,21 +312,21 @@ module SignAnalysis<DeltaSig D> {
|
||||
* to only include bounds for which we might determine a sign.
|
||||
*/
|
||||
private predicate upperBound(
|
||||
SemExpr upperbound, SemSsaVariable v, SsaReadPosition pos, boolean isStrict
|
||||
SemExpr upperbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isStrict
|
||||
) {
|
||||
exists(boolean testIsTrue, SemRelationalExpr comp |
|
||||
pos.hasReadOfVar(v) and
|
||||
guardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
semGuardControlsSsaRead(semGetComparisonGuard(comp), pos, testIsTrue) and
|
||||
not unknownSign(upperbound)
|
||||
|
|
||||
testIsTrue = true and
|
||||
comp.getGreaterOperand() = upperbound and
|
||||
comp.getLesserOperand() = ssaRead(v, D::fromInt(0)) and
|
||||
comp.getLesserOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = true else isStrict = false)
|
||||
or
|
||||
testIsTrue = false and
|
||||
comp.getLesserOperand() = upperbound and
|
||||
comp.getGreaterOperand() = ssaRead(v, D::fromInt(0)) and
|
||||
comp.getGreaterOperand() = Utils::semSsaRead(v, D::fromInt(0)) and
|
||||
(if comp.isStrict() then isStrict = false else isStrict = true)
|
||||
)
|
||||
}
|
||||
@@ -339,11 +338,11 @@ module SignAnalysis<DeltaSig D> {
|
||||
* - `isEq = true` : `v = eqbound`
|
||||
* - `isEq = false` : `v != eqbound`
|
||||
*/
|
||||
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SsaReadPosition pos, boolean isEq) {
|
||||
private predicate eqBound(SemExpr eqbound, SemSsaVariable v, SemSsaReadPosition pos, boolean isEq) {
|
||||
exists(SemGuard guard, boolean testIsTrue, boolean polarity, SemExpr e |
|
||||
pos.hasReadOfVar(pragma[only_bind_into](v)) and
|
||||
guardControlsSsaRead(guard, pragma[only_bind_into](pos), testIsTrue) and
|
||||
e = ssaRead(pragma[only_bind_into](v), D::fromInt(0)) and
|
||||
semGuardControlsSsaRead(guard, pragma[only_bind_into](pos), testIsTrue) and
|
||||
e = Utils::semSsaRead(pragma[only_bind_into](v), D::fromInt(0)) and
|
||||
guard.isEquality(eqbound, e, polarity) and
|
||||
isEq = polarity.booleanXor(testIsTrue).booleanNot() and
|
||||
not unknownSign(eqbound)
|
||||
@@ -354,7 +353,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be positive in
|
||||
* order for `v` to be positive.
|
||||
*/
|
||||
private predicate posBound(SemExpr bound, SemSsaVariable v, SsaReadPosition pos) {
|
||||
private predicate posBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
@@ -363,7 +362,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
* Holds if `bound` is a bound for `v` at `pos` that needs to be negative in
|
||||
* order for `v` to be negative.
|
||||
*/
|
||||
private predicate negBound(SemExpr bound, SemSsaVariable v, SsaReadPosition pos) {
|
||||
private predicate negBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, true)
|
||||
}
|
||||
@@ -372,24 +371,24 @@ module SignAnalysis<DeltaSig D> {
|
||||
* Holds if `bound` is a bound for `v` at `pos` that can restrict whether `v`
|
||||
* can be zero.
|
||||
*/
|
||||
private predicate zeroBound(SemExpr bound, SemSsaVariable v, SsaReadPosition pos) {
|
||||
private predicate zeroBound(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) or
|
||||
upperBound(bound, v, pos, _) or
|
||||
eqBound(bound, v, pos, _)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be positive at `pos`. */
|
||||
private predicate posBoundOk(SemExpr bound, SemSsaVariable v, SsaReadPosition pos) {
|
||||
private predicate posBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
posBound(bound, v, pos) and TPos() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be negative at `pos`. */
|
||||
private predicate negBoundOk(SemExpr bound, SemSsaVariable v, SsaReadPosition pos) {
|
||||
private predicate negBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
negBound(bound, v, pos) and TNeg() = semExprSign(bound)
|
||||
}
|
||||
|
||||
/** Holds if `bound` allows `v` to be zero at `pos`. */
|
||||
private predicate zeroBoundOk(SemExpr bound, SemSsaVariable v, SsaReadPosition pos) {
|
||||
private predicate zeroBoundOk(SemExpr bound, SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
lowerBound(bound, v, pos, _) and TNeg() = semExprSign(bound)
|
||||
or
|
||||
lowerBound(bound, v, pos, false) and TZero() = semExprSign(bound)
|
||||
@@ -407,7 +406,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
* Holds if there is a bound that might restrict whether `v` has the sign `s`
|
||||
* at `pos`.
|
||||
*/
|
||||
private predicate hasGuard(SemSsaVariable v, SsaReadPosition pos, Sign s) {
|
||||
private predicate hasGuard(SemSsaVariable v, SemSsaReadPosition pos, Sign s) {
|
||||
s = TPos() and posBound(_, v, pos)
|
||||
or
|
||||
s = TNeg() and negBound(_, v, pos)
|
||||
@@ -420,7 +419,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
* might be ruled out by a guard.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign guardedSsaSign(SemSsaVariable v, SsaReadPosition pos) {
|
||||
private Sign guardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
hasGuard(v, pos, result)
|
||||
@@ -431,7 +430,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
* can rule it out.
|
||||
*/
|
||||
pragma[noinline]
|
||||
private Sign unguardedSsaSign(SemSsaVariable v, SsaReadPosition pos) {
|
||||
private Sign unguardedSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = semSsaDefSign(v) and
|
||||
pos.hasReadOfVar(v) and
|
||||
not hasGuard(v, pos, result)
|
||||
@@ -442,7 +441,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
* ruled out the sign but does not.
|
||||
* This does not check that the definition of `v` also allows the sign.
|
||||
*/
|
||||
private Sign guardedSsaSignOk(SemSsaVariable v, SsaReadPosition pos) {
|
||||
private Sign guardedSsaSignOk(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = TPos() and
|
||||
forex(SemExpr bound | posBound(bound, v, pos) | posBoundOk(bound, v, pos))
|
||||
or
|
||||
@@ -454,7 +453,7 @@ module SignAnalysis<DeltaSig D> {
|
||||
}
|
||||
|
||||
/** Gets a possible sign for `v` at `pos`. */
|
||||
private Sign semSsaSign(SemSsaVariable v, SsaReadPosition pos) {
|
||||
private Sign semSsaSign(SemSsaVariable v, SemSsaReadPosition pos) {
|
||||
result = unguardedSsaSign(v, pos)
|
||||
or
|
||||
result = guardedSsaSign(v, pos) and
|
||||
@@ -470,7 +469,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()
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
* configuration (see `InvalidPointerToDerefConfig`).
|
||||
*
|
||||
* The dataflow traversal defines the set of sources as any dataflow node `n` such that there exists a pointer-arithmetic
|
||||
* instruction `pai` found by `AllocationToInvalidPointer.qll` and a `n.asInstruction() = pai`.
|
||||
* instruction `pai` found by `AllocationToInvalidPointer.qll` and a `n.asInstruction() >= pai + deltaDerefSourceAndPai`.
|
||||
* Here, `deltaDerefSourceAndPai` is the constant difference between the source we track for finding a dereference and the
|
||||
* pointer-arithmetic instruction.
|
||||
*
|
||||
* The set of sinks is defined as any dataflow node `n` such that `addr <= n.asInstruction() + deltaDerefSinkAndDerefAddress`
|
||||
* for some address operand `addr` and constant difference `deltaDerefSinkAndDerefAddress`. Since an address operand is
|
||||
@@ -35,8 +37,9 @@
|
||||
* `deltaDerefSinkAndDerefAddress >= 0`. The load attached to `*p` is the "operation". To ensure that the path makes
|
||||
* intuitive sense, we only pick operations that are control-flow reachable from the dereference sink.
|
||||
*
|
||||
* We use the `deltaDerefSinkAndDerefAddress` to compute how many elements the dereference is beyond the end position of
|
||||
* the allocation. This is done in the `operationIsOffBy` predicate (which is the only predicate exposed by this file).
|
||||
* To compute how many elements the dereference is beyond the end position of the allocation, we sum the two deltas
|
||||
* `deltaDerefSourceAndPai` and `deltaDerefSinkAndDerefAddress`. This is done in the `operationIsOffBy` predicate
|
||||
* (which is the only predicate exposed by this file).
|
||||
*
|
||||
* Handling false positives:
|
||||
*
|
||||
@@ -93,7 +96,7 @@ int invalidPointerToDereferenceFieldFlowBranchLimit() { result = 0 }
|
||||
private module InvalidPointerToDerefBarrier {
|
||||
private module BarrierConfig implements DataFlow::ConfigSig {
|
||||
additional predicate isSource(DataFlow::Node source, PointerArithmeticInstruction pai) {
|
||||
invalidPointerToDerefSource(_, pai, _) and
|
||||
invalidPointerToDerefSource(_, pai, _, _) and
|
||||
// source <= pai
|
||||
bounded2(source.asInstruction(), pai, any(int d | d <= 0))
|
||||
}
|
||||
@@ -166,11 +169,11 @@ private module InvalidPointerToDerefBarrier {
|
||||
*/
|
||||
private module InvalidPointerToDerefConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState extends PointerArithmeticInstruction {
|
||||
FlowState() { invalidPointerToDerefSource(_, this, _) }
|
||||
FlowState() { invalidPointerToDerefSource(_, this, _, _) }
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node source, FlowState pai) {
|
||||
invalidPointerToDerefSource(_, pai, source)
|
||||
invalidPointerToDerefSource(_, pai, source, _)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
@@ -195,17 +198,24 @@ private import DataFlow::GlobalWithState<InvalidPointerToDerefConfig>
|
||||
|
||||
/**
|
||||
* Holds if `allocSource` is dataflow node that represents an allocation that flows to the
|
||||
* left-hand side of the pointer-arithmetic instruction represented by `derefSource`.
|
||||
* left-hand side of the pointer-arithmetic `pai`, and `derefSource <= pai + derefSourcePaiDelta`.
|
||||
*
|
||||
* For example, if `pai` is a pointer-arithmetic operation `p + size` in an expression such
|
||||
* as `(p + size) + 1` and `derefSource` is the node representing `(p + size) + 1`. In this
|
||||
* case `derefSourcePaiDelta` is 1.
|
||||
*/
|
||||
private predicate invalidPointerToDerefSource(
|
||||
DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource
|
||||
DataFlow::Node allocSource, PointerArithmeticInstruction pai, DataFlow::Node derefSource,
|
||||
int deltaDerefSourceAndPai
|
||||
) {
|
||||
// Note that `deltaDerefSourceAndPai` is not necessarily equal to `rhsSizeDelta`:
|
||||
// `rhsSizeDelta` is the constant offset added to the size of the allocation, and
|
||||
// `deltaDerefSourceAndPai` is the constant difference between the pointer-arithmetic instruction
|
||||
// and the instruction computing the address for which we will search for a dereference.
|
||||
AllocToInvalidPointer::pointerAddInstructionHasBounds(allocSource, pai, _, _) and
|
||||
derefSource.asInstruction() = pai
|
||||
// derefSource <= pai + deltaDerefSourceAndPai
|
||||
bounded2(derefSource.asInstruction(), pai, deltaDerefSourceAndPai) and
|
||||
deltaDerefSourceAndPai >= 0
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,9 +258,11 @@ private Instruction getASuccessor(Instruction instr) {
|
||||
instr.getBlock().getASuccessor+() = result.getBlock()
|
||||
}
|
||||
|
||||
private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFlow::Node derefSink) {
|
||||
private predicate paiForDereferenceSink(
|
||||
PointerArithmeticInstruction pai, DataFlow::Node derefSink, int deltaDerefSourceAndPai
|
||||
) {
|
||||
exists(DataFlow::Node derefSource |
|
||||
invalidPointerToDerefSource(_, pai, derefSource) and
|
||||
invalidPointerToDerefSource(_, pai, derefSource, deltaDerefSourceAndPai) and
|
||||
flow(derefSource, derefSink)
|
||||
)
|
||||
}
|
||||
@@ -262,10 +274,10 @@ private predicate paiForDereferenceSink(PointerArithmeticInstruction pai, DataFl
|
||||
*/
|
||||
private predicate derefSinkToOperation(
|
||||
DataFlow::Node derefSink, PointerArithmeticInstruction pai, DataFlow::Node operation,
|
||||
string description, int deltaDerefSinkAndDerefAddress
|
||||
string description, int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress
|
||||
) {
|
||||
exists(Instruction operationInstr, AddressOperand addr |
|
||||
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink)) and
|
||||
paiForDereferenceSink(pai, pragma[only_bind_into](derefSink), deltaDerefSourceAndPai) and
|
||||
isInvalidPointerDerefSink(derefSink, addr, operationInstr, description,
|
||||
deltaDerefSinkAndDerefAddress) and
|
||||
operationInstr = getASuccessor(derefSink.asInstruction()) and
|
||||
@@ -286,7 +298,11 @@ predicate operationIsOffBy(
|
||||
DataFlow::Node allocation, PointerArithmeticInstruction pai, DataFlow::Node derefSource,
|
||||
DataFlow::Node derefSink, string description, DataFlow::Node operation, int delta
|
||||
) {
|
||||
invalidPointerToDerefSource(allocation, pai, derefSource) and
|
||||
flow(derefSource, derefSink) and
|
||||
derefSinkToOperation(derefSink, pai, operation, description, delta)
|
||||
exists(int deltaDerefSourceAndPai, int deltaDerefSinkAndDerefAddress |
|
||||
invalidPointerToDerefSource(allocation, pai, derefSource, deltaDerefSourceAndPai) and
|
||||
flow(derefSource, derefSink) and
|
||||
derefSinkToOperation(derefSink, pai, operation, description, deltaDerefSourceAndPai,
|
||||
deltaDerefSinkAndDerefAddress) and
|
||||
delta = deltaDerefSourceAndPai + deltaDerefSinkAndDerefAddress
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -1336,16 +1334,11 @@ funbind(
|
||||
| @assignxorexpr
|
||||
| @assignlshiftexpr
|
||||
| @assignrshiftexpr
|
||||
;
|
||||
|
||||
@assign_pointer_expr = @assignpaddexpr
|
||||
| @assignpaddexpr
|
||||
| @assignpsubexpr
|
||||
;
|
||||
|
||||
@assign_op_expr = @assign_arith_expr
|
||||
| @assign_bitwise_expr
|
||||
| @assign_pointer_expr
|
||||
;
|
||||
@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr
|
||||
|
||||
@assign_expr = @assignexpr | @assign_op_expr | @blockassignexpr
|
||||
|
||||
|
||||
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
|
||||
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: Removed @assignpaddexpr and @assignpsubexpr from @assign_bitwise_expr
|
||||
compatibility: full
|
||||
@@ -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()
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.dataflow.MustFlow
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
|
||||
/**
|
||||
* Auxiliary predicate: Types that don't require initialization
|
||||
@@ -34,6 +33,31 @@ predicate allocatedType(Type t) {
|
||||
allocatedType(t.getUnspecifiedType())
|
||||
}
|
||||
|
||||
/**
|
||||
* A declaration of a local variable that leaves the
|
||||
* variable uninitialized.
|
||||
*/
|
||||
DeclStmt declWithNoInit(LocalVariable v) {
|
||||
result.getADeclaration() = v and
|
||||
not exists(v.getInitializer()) and
|
||||
/* The type of the variable is not stack-allocated. */
|
||||
exists(Type t | t = v.getType() | not allocatedType(t))
|
||||
}
|
||||
|
||||
class UninitialisedLocalReachability extends StackVariableReachability {
|
||||
UninitialisedLocalReachability() { this = "UninitialisedLocal" }
|
||||
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) { node = declWithNoInit(v) }
|
||||
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) { useOfVarActual(v, node) }
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
|
||||
// only report the _first_ possibly uninitialized use
|
||||
useOfVarActual(v, node) or
|
||||
definitionBarrier(v, node)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate containsInlineAssembly(Function f) { exists(AsmStmt s | s.getEnclosingFunction() = f) }
|
||||
|
||||
@@ -58,33 +82,8 @@ VariableAccess commonException() {
|
||||
containsInlineAssembly(result.getEnclosingFunction())
|
||||
}
|
||||
|
||||
predicate isSinkImpl(Instruction sink, VariableAccess va) {
|
||||
exists(LoadInstruction load |
|
||||
va = load.getUnconvertedResultExpression() and
|
||||
not va = commonException() and
|
||||
sink = load.getSourceValue()
|
||||
)
|
||||
}
|
||||
|
||||
class MustFlow extends MustFlowConfiguration {
|
||||
MustFlow() { this = "MustFlow" }
|
||||
|
||||
override predicate isSource(Instruction source) {
|
||||
source instanceof UninitializedInstruction and
|
||||
exists(Type t | t = source.getResultType() | not allocatedType(t))
|
||||
}
|
||||
|
||||
override predicate isSink(Operand sink) { isSinkImpl(sink.getDef(), _) }
|
||||
|
||||
override predicate allowInterproceduralFlow() { none() }
|
||||
|
||||
override predicate isBarrier(Instruction instr) { instr instanceof ChiInstruction }
|
||||
}
|
||||
|
||||
from
|
||||
VariableAccess va, LocalVariable v, MustFlow conf, MustFlowPathNode source, MustFlowPathNode sink
|
||||
from UninitialisedLocalReachability r, LocalVariable v, VariableAccess va
|
||||
where
|
||||
conf.hasFlowPath(source, sink) and
|
||||
isSinkImpl(sink.getInstruction(), va) and
|
||||
v = va.getTarget()
|
||||
r.reaches(_, v, va) and
|
||||
not va = commonException()
|
||||
select va, "The variable $@ may not be initialized at this access.", v, v.getName()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
|
||||
import semmle.code.cpp.security.BufferWrite
|
||||
import semmle.code.cpp.security.FlowSources as FS
|
||||
import semmle.code.cpp.dataflow.new.TaintTracking
|
||||
import semmle.code.cpp.controlflow.IRGuards
|
||||
import Flow::PathGraph
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.ir.dataflow.internal.DefaultTaintTrackingImpl
|
||||
import TaintedWithPath
|
||||
|
||||
/*
|
||||
* --- Summary of CWE-120 alerts ---
|
||||
@@ -48,54 +47,38 @@ predicate isUnboundedWrite(BufferWrite bw) {
|
||||
not exists(bw.getMaxData(_)) // and we can't deduce an upper bound to the amount copied
|
||||
}
|
||||
|
||||
/*
|
||||
* predicate isMaybeUnboundedWrite(BufferWrite bw)
|
||||
* {
|
||||
* not bw.hasExplicitLimit() // has no explicit size limit
|
||||
* and exists(bw.getMaxData()) // and we can deduce an upper bound to the amount copied
|
||||
* and (not exists(getBufferSize(bw.getDest(), _))) // but we can't work out the size of the destination to be sure
|
||||
* }
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 }
|
||||
/*
|
||||
* --- user input reach ---
|
||||
*/
|
||||
|
||||
predicate isSink(DataFlow::Node sink, BufferWrite bw, boolean qualifier) {
|
||||
unboundedWriteSource(sink.asIndirectExpr(), bw, qualifier)
|
||||
or
|
||||
// `gets` and `scanf` reads from stdin so there's no real input.
|
||||
// The `BufferWrite` library models this as the call itself being
|
||||
// the source. In this case we mark the output argument as being
|
||||
// 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)
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element tainted) { unboundedWriteSource(tainted, _) }
|
||||
|
||||
override predicate taintThroughGlobals() { any() }
|
||||
}
|
||||
|
||||
predicate lessThanOrEqual(IRGuardCondition g, Expr e, boolean branch) {
|
||||
exists(Operand left |
|
||||
g.comparesLt(left, _, _, true, branch) or
|
||||
g.comparesEq(left, _, _, true, branch)
|
||||
|
|
||||
left.getDef().getUnconvertedResultExpression() = e
|
||||
)
|
||||
}
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isSource(source, _) }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { isSink(sink, _, _) }
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) { isSink(node, _, false) }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// Block flow if the node is guarded by any <, <= or = operations.
|
||||
node = DataFlow::BarrierGuard<lessThanOrEqual/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
/*
|
||||
* --- put it together ---
|
||||
*/
|
||||
|
||||
/*
|
||||
* An unbounded write is, for example `strcpy(..., tainted)`. We're looking
|
||||
@@ -104,20 +87,17 @@ module Flow = TaintTracking::Global<Config>;
|
||||
*
|
||||
* In the case of `gets` and `scanf`, where the source buffer is implicit, the
|
||||
* `BufferWrite` library reports the source buffer to be the same as the
|
||||
* destination buffer. So to report an alert on a pattern like:
|
||||
* ```
|
||||
* char s[32];
|
||||
* gets(s);
|
||||
* ```
|
||||
* we define the sink as the node corresponding to the output argument of `gets`.
|
||||
* This gives us a path where the source is equal to the sink.
|
||||
* destination buffer. Since those destination-buffer arguments are also
|
||||
* modeled in the taint-tracking library as being _sources_ of taint, they are
|
||||
* in practice reported as being tainted because the `security.TaintTracking`
|
||||
* library does not distinguish between taint going into an argument and out of
|
||||
* an argument. Thus, we get the desired alerts.
|
||||
*/
|
||||
|
||||
from BufferWrite bw, Flow::PathNode source, Flow::PathNode sink, string sourceType
|
||||
from BufferWrite bw, Expr inputSource, Expr tainted, PathNode sourceNode, PathNode sinkNode
|
||||
where
|
||||
Flow::flowPath(source, sink) and
|
||||
isSource(source.getNode(), sourceType) and
|
||||
isSink(sink.getNode(), bw, _)
|
||||
select bw, source, sink,
|
||||
"This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.",
|
||||
source.getNode(), sourceType
|
||||
taintedWithPath(inputSource, tainted, sourceNode, sinkNode) and
|
||||
unboundedWriteSource(tainted, bw)
|
||||
select bw, sourceNode, sinkNode,
|
||||
"This '" + bw.getBWDesc() + "' with input from $@ may overflow the destination.", inputSource,
|
||||
inputSource.toString()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* @id cpp/invalid-pointer-deref
|
||||
* @tags reliability
|
||||
* security
|
||||
* experimental
|
||||
* external/cwe/cwe-119
|
||||
* external/cwe/cwe-125
|
||||
* external/cwe/cwe-193
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
@@ -15,7 +15,6 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import semmle.code.cpp.models.implementations.Memset
|
||||
import ExposedSystemData::PathGraph
|
||||
import SystemData
|
||||
|
||||
@@ -29,10 +28,6 @@ module ExposedSystemDataConfig implements DataFlow::ConfigSig {
|
||||
fc.getArgument(arg).getAChild*() = sink.asIndirectExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.asIndirectArgument() = any(MemsetFunction func).getACallToThisFunction().getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
module ExposedSystemData = TaintTracking::Global<ExposedSystemDataConfig>;
|
||||
|
||||
@@ -28,7 +28,6 @@ import cpp
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import semmle.code.cpp.models.interfaces.FlowSource
|
||||
import semmle.code.cpp.security.OutputWrite
|
||||
import semmle.code.cpp.models.implementations.Memset
|
||||
import PotentiallyExposedSystemData::PathGraph
|
||||
import SystemData
|
||||
|
||||
@@ -50,10 +49,6 @@ module PotentiallyExposedSystemDataConfig implements DataFlow::ConfigSig {
|
||||
else child = sink.asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node.asIndirectArgument() = any(MemsetFunction func).getACallToThisFunction().getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
module PotentiallyExposedSystemData = TaintTracking::Global<PotentiallyExposedSystemDataConfig>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
## 0.8.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* 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
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user