mirror of
https://github.com/github/codeql.git
synced 2026-04-30 19:26:02 +02:00
Merge branch 'main' into js-insecure-http-parser
This commit is contained in:
102
.github/workflows/atm-check-query-suite.yml
vendored
102
.github/workflows/atm-check-query-suite.yml
vendored
@@ -1,102 +0,0 @@
|
||||
name: "ATM - Check query suite"
|
||||
|
||||
env:
|
||||
QUERY_PACK: javascript/ql/experimental/adaptivethreatmodeling/src
|
||||
QUERY_SUITE: codeql-suites/javascript-atm-code-scanning.qls
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/atm-check-query-suite.yml"
|
||||
- "javascript/ql/experimental/adaptivethreatmodeling/**"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
atm-check-query-suite:
|
||||
runs-on: ubuntu-latest-xl
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup CodeQL
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
with:
|
||||
channel: release
|
||||
|
||||
- name: Cache compilation cache
|
||||
id: query-cache
|
||||
uses: ./.github/actions/cache-query-compilation
|
||||
with:
|
||||
key: atm-suite
|
||||
|
||||
- name: Install ATM model
|
||||
run: |
|
||||
set -exu
|
||||
|
||||
# Install dependencies of ATM query pack, i.e. the ATM model
|
||||
codeql pack install "${QUERY_PACK}"
|
||||
|
||||
# Retrieve model checksum
|
||||
model_checksum=$(codeql resolve extensions "${QUERY_PACK}/${QUERY_SUITE}" | jq -r '.models[0].checksum')
|
||||
|
||||
# Trust the model so that we can use it in the ATM boosted queries
|
||||
mkdir -p "$HOME/.config/codeql"
|
||||
echo "--insecurely-execute-ml-model-checksums ${model_checksum}" >> "$HOME/.config/codeql/config"
|
||||
|
||||
- name: Create test DB
|
||||
run: |
|
||||
DB_PATH="${RUNNER_TEMP}/db"
|
||||
echo "DB_PATH=${DB_PATH}" >> "${GITHUB_ENV}"
|
||||
|
||||
codeql database create "${DB_PATH}" --source-root config/atm --language javascript
|
||||
|
||||
- name: Run ATM query suite
|
||||
run: |
|
||||
SARIF_PATH="${RUNNER_TEMP}/sarif.json"
|
||||
echo "SARIF_PATH=${SARIF_PATH}" >> "${GITHUB_ENV}"
|
||||
|
||||
codeql database analyze \
|
||||
--threads=0 \
|
||||
--ram 50000 \
|
||||
--format sarif-latest \
|
||||
--output "${SARIF_PATH}" \
|
||||
--sarif-group-rules-by-pack \
|
||||
-vv \
|
||||
--compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" \
|
||||
-- \
|
||||
"${DB_PATH}" \
|
||||
"${QUERY_PACK}/${QUERY_SUITE}"
|
||||
|
||||
- name: Upload SARIF
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: javascript-ml-powered-queries.sarif
|
||||
path: "${{ env.SARIF_PATH }}"
|
||||
retention-days: 5
|
||||
|
||||
- name: Check results
|
||||
run: |
|
||||
# We should run at least the ML-powered queries in `expected_rules`.
|
||||
expected_rules="js/ml-powered/nosql-injection js/ml-powered/path-injection js/ml-powered/sql-injection js/ml-powered/xss"
|
||||
|
||||
for rule in ${expected_rules}; do
|
||||
found_rule=$(jq --arg rule "${rule}" '[.runs[0].tool.extensions[].rules | select(. != null) |
|
||||
flatten | .[].id] | any(. == $rule)' "${SARIF_PATH}")
|
||||
if [[ "${found_rule}" != "true" ]]; then
|
||||
echo "Expected SARIF output to contain rule '${rule}', but found no such rule."
|
||||
exit 1
|
||||
else
|
||||
echo "Found rule '${rule}'."
|
||||
fi
|
||||
done
|
||||
|
||||
# We should have at least one alert from an ML-powered query.
|
||||
num_alerts=$(jq '[.runs[0].results[] |
|
||||
select(.properties.score != null and (.rule.id | startswith("js/ml-powered/")))] | length' \
|
||||
"${SARIF_PATH}")
|
||||
if [[ "${num_alerts}" -eq 0 ]]; then
|
||||
echo "Expected to find at least one alert from an ML-powered query but found ${num_alerts}."
|
||||
exit 1
|
||||
else
|
||||
echo "Found ${num_alerts} alerts from ML-powered queries.";
|
||||
fi
|
||||
@@ -1,12 +0,0 @@
|
||||
name: ATM Model Integration Tests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
hello-world:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: foo
|
||||
run: echo "Hello world"
|
||||
@@ -123,6 +123,10 @@
|
||||
"java/ql/src/utils/modelgenerator/internal/CaptureModels.qll",
|
||||
"csharp/ql/src/utils/modelgenerator/internal/CaptureModels.qll"
|
||||
],
|
||||
"Model as Data Generation Java/C# - CaptureModelsPrinting": [
|
||||
"java/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll",
|
||||
"csharp/ql/src/utils/modelgenerator/internal/CaptureModelsPrinting.qll"
|
||||
],
|
||||
"Sign Java/C#": [
|
||||
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll",
|
||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll"
|
||||
@@ -596,4 +600,4 @@
|
||||
"python/ql/lib/semmle/python/security/internal/EncryptionKeySizes.qll",
|
||||
"java/ql/lib/semmle/code/java/security/internal/EncryptionKeySizes.qll"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.6.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.6.0
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
3
cpp/ql/lib/change-notes/released/0.6.1.md
Normal file
3
cpp/ql/lib/change-notes/released/0.6.1.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.6.1
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.6.0
|
||||
lastReleaseVersion: 0.6.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.6.1-dev
|
||||
version: 0.7.0-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -79,3 +79,13 @@ class ArgumentPosition extends int {
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
/**
|
||||
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
|
||||
*
|
||||
* This is a temporary hook to support technical debt in the Go language; do not use.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate golangSpecificParamArgFilter(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
any()
|
||||
}
|
||||
|
||||
@@ -425,7 +425,8 @@ private module Cached {
|
||||
exists(ParameterPosition ppos |
|
||||
viableParam(call, ppos, p) and
|
||||
argumentPositionMatch(call, arg, ppos) and
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p))
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
|
||||
golangSpecificParamArgFilter(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -271,3 +271,13 @@ Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) {
|
||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
||||
pragma[inline]
|
||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
||||
|
||||
/**
|
||||
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
|
||||
*
|
||||
* This is a temporary hook to support technical debt in the Go language; do not use.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate golangSpecificParamArgFilter(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
any()
|
||||
}
|
||||
|
||||
@@ -425,7 +425,8 @@ private module Cached {
|
||||
exists(ParameterPosition ppos |
|
||||
viableParam(call, ppos, p) and
|
||||
argumentPositionMatch(call, arg, ppos) and
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p))
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
|
||||
golangSpecificParamArgFilter(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -897,23 +897,6 @@ private class MyConsistencyConfiguration extends Consistency::ConsistencyConfigu
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the basic block of `node`.
|
||||
*/
|
||||
IRBlock getBasicBlock(Node node) {
|
||||
node.asInstruction().getBlock() = result
|
||||
or
|
||||
node.asOperand().getUse().getBlock() = result
|
||||
or
|
||||
node.(SsaPhiNode).getPhiNode().getBasicBlock() = result
|
||||
or
|
||||
node.(RawIndirectOperand).getOperand().getUse().getBlock() = result
|
||||
or
|
||||
node.(RawIndirectInstruction).getInstruction().getBlock() = result
|
||||
or
|
||||
result = getBasicBlock(node.(PostUpdateNode).getPreUpdateNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* A local flow relation that includes both local steps, read steps and
|
||||
* argument-to-return flow through summarized functions.
|
||||
@@ -999,7 +982,8 @@ private int countNumberOfBranchesUsingParameter(SwitchInstruction switch, Parame
|
||||
// we pick the one with the highest edge count.
|
||||
result =
|
||||
max(SsaPhiNode phi |
|
||||
switch.getSuccessor(caseOrDefaultEdge()).getBlock().dominanceFrontier() = getBasicBlock(phi) and
|
||||
switch.getSuccessor(caseOrDefaultEdge()).getBlock().dominanceFrontier() =
|
||||
phi.getBasicBlock() and
|
||||
phi.getSourceVariable() = sv
|
||||
|
|
||||
strictcount(phi.getAnInput())
|
||||
|
||||
@@ -160,6 +160,28 @@ class Node extends TIRDataFlowNode {
|
||||
/** Gets the operands corresponding to this node, if any. */
|
||||
Operand asOperand() { result = this.(OperandNode).getOperand() }
|
||||
|
||||
/**
|
||||
* Holds if this node is at index `i` in basic block `block`.
|
||||
*
|
||||
* Note: Phi nodes are considered to be at index `-1`.
|
||||
*/
|
||||
final predicate hasIndexInBlock(IRBlock block, int i) {
|
||||
this.asInstruction() = block.getInstruction(i)
|
||||
or
|
||||
this.asOperand().getUse() = block.getInstruction(i)
|
||||
or
|
||||
this.(SsaPhiNode).getPhiNode().getBasicBlock() = block and i = -1
|
||||
or
|
||||
this.(RawIndirectOperand).getOperand().getUse() = block.getInstruction(i)
|
||||
or
|
||||
this.(RawIndirectInstruction).getInstruction() = block.getInstruction(i)
|
||||
or
|
||||
this.(PostUpdateNode).getPreUpdateNode().hasIndexInBlock(block, i)
|
||||
}
|
||||
|
||||
/** Gets the basic block of this node, if any. */
|
||||
final IRBlock getBasicBlock() { this.hasIndexInBlock(result, _) }
|
||||
|
||||
/**
|
||||
* Gets the non-conversion expression corresponding to this node, if any.
|
||||
* This predicate only has a result on nodes that represent the value of
|
||||
@@ -530,7 +552,7 @@ class SsaPhiNode extends Node, TSsaPhiNode {
|
||||
*/
|
||||
final Node getAnInput(boolean fromBackEdge) {
|
||||
localFlowStep(result, this) and
|
||||
if phi.getBasicBlock().dominates(getBasicBlock(result))
|
||||
if phi.getBasicBlock().dominates(result.getBasicBlock())
|
||||
then fromBackEdge = true
|
||||
else fromBackEdge = false
|
||||
}
|
||||
@@ -1887,7 +1909,7 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
e = value.getAnInstruction().getConvertedResultExpression() and
|
||||
result.getConvertedExpr() = e and
|
||||
guardChecks(g, value.getAnInstruction().getConvertedResultExpression(), edge) and
|
||||
g.controls(getBasicBlock(result), edge)
|
||||
g.controls(result.getBasicBlock(), edge)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ private import semmle.code.cpp.ir.IR
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticBound
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysis
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysisImpl
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
/**
|
||||
* Gets the lower bound of the expression.
|
||||
@@ -22,8 +24,10 @@ private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.Rang
|
||||
* `lowerBound(expr.getFullyConverted())`
|
||||
*/
|
||||
float lowerBound(Expr expr) {
|
||||
exists(Instruction i, SemBound b | i.getAst() = expr and b instanceof SemZeroBound |
|
||||
semBounded(getSemanticExpr(i), b, result, false, _)
|
||||
exists(Instruction i, ConstantBounds::SemBound b |
|
||||
i.getAst() = expr and b instanceof ConstantBounds::SemZeroBound
|
||||
|
|
||||
ConstantStage::semBounded(getSemanticExpr(i), b, result, false, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -40,8 +44,10 @@ float lowerBound(Expr expr) {
|
||||
* `upperBound(expr.getFullyConverted())`
|
||||
*/
|
||||
float upperBound(Expr expr) {
|
||||
exists(Instruction i, SemBound b | i.getAst() = expr and b instanceof SemZeroBound |
|
||||
semBounded(getSemanticExpr(i), b, result, true, _)
|
||||
exists(Instruction i, ConstantBounds::SemBound b |
|
||||
i.getAst() = expr and b instanceof ConstantBounds::SemZeroBound
|
||||
|
|
||||
ConstantStage::semBounded(getSemanticExpr(i), b, result, true, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,7 +96,15 @@ predicate defMightOverflow(RangeSsaDefinition def, StackVariable v) {
|
||||
* does not consider the possibility that the expression might overflow
|
||||
* due to a conversion.
|
||||
*/
|
||||
predicate exprMightOverflowNegatively(Expr expr) { none() }
|
||||
predicate exprMightOverflowNegatively(Expr expr) {
|
||||
lowerBound(expr) < exprMinVal(expr)
|
||||
or
|
||||
exists(SemanticExprConfig::Expr semExpr |
|
||||
semExpr.getUnconverted().getAst() = expr and
|
||||
ConstantStage::potentiallyOverflowingExpr(false, semExpr) and
|
||||
not ConstantStage::initialBounded(semExpr, _, _, false, _, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow negatively. Conversions
|
||||
@@ -108,7 +122,15 @@ predicate convertedExprMightOverflowNegatively(Expr expr) {
|
||||
* does not consider the possibility that the expression might overflow
|
||||
* due to a conversion.
|
||||
*/
|
||||
predicate exprMightOverflowPositively(Expr expr) { none() }
|
||||
predicate exprMightOverflowPositively(Expr expr) {
|
||||
upperBound(expr) > exprMaxVal(expr)
|
||||
or
|
||||
exists(SemanticExprConfig::Expr semExpr |
|
||||
semExpr.getUnconverted().getAst() = expr and
|
||||
ConstantStage::potentiallyOverflowingExpr(true, semExpr) and
|
||||
not ConstantStage::initialBounded(semExpr, _, _, true, _, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the expression might overflow positively. Conversions
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
private import RangeAnalysisStage
|
||||
private import RangeAnalysisImpl
|
||||
|
||||
module FloatDelta implements DeltaSig {
|
||||
class Delta = float;
|
||||
@@ -18,3 +19,36 @@ module FloatDelta implements DeltaSig {
|
||||
bindingset[f]
|
||||
Delta fromFloat(float f) { result = f }
|
||||
}
|
||||
|
||||
module FloatOverflow implements OverflowSig<FloatDelta> {
|
||||
predicate semExprDoesNotOverflow(boolean positively, SemExpr expr) {
|
||||
exists(float lb, float ub, float delta |
|
||||
typeBounds(expr.getSemType(), lb, ub) and
|
||||
ConstantStage::initialBounded(expr, any(ConstantBounds::SemZeroBound b), delta, positively, _,
|
||||
_, _)
|
||||
|
|
||||
positively = true and delta < ub
|
||||
or
|
||||
positively = false and delta > lb
|
||||
)
|
||||
}
|
||||
|
||||
additional predicate typeBounds(SemType t, float lb, float ub) {
|
||||
exists(SemIntegerType integralType, float limit |
|
||||
integralType = t and limit = 2.pow(8 * integralType.getByteSize())
|
||||
|
|
||||
if integralType instanceof SemBooleanType
|
||||
then lb = 0 and ub = 1
|
||||
else
|
||||
if integralType.isSigned()
|
||||
then (
|
||||
lb = -(limit / 2) and ub = (limit / 2) - 1
|
||||
) else (
|
||||
lb = 0 and ub = limit - 1
|
||||
)
|
||||
)
|
||||
or
|
||||
// This covers all floating point types. The range is (-Inf, +Inf).
|
||||
t instanceof SemFloatingPointType and lb = -(1.0 / 0.0) and ub = 1.0 / 0.0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
import RangeAnalysisImpl
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticBound
|
||||
private import RangeAnalysisImpl as Impl
|
||||
import Impl::Public
|
||||
|
||||
@@ -6,7 +6,7 @@ private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import RangeAnalysisStage
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
|
||||
|
||||
module CppLangImpl implements LangSig<FloatDelta> {
|
||||
module CppLangImplConstant implements LangSig<FloatDelta> {
|
||||
/**
|
||||
* Holds if the specified expression should be excluded from the result of `ssaRead()`.
|
||||
*
|
||||
@@ -1,5 +1,6 @@
|
||||
private import RangeAnalysisStage
|
||||
private import RangeAnalysisSpecific
|
||||
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.SemanticBound as SemanticBound
|
||||
@@ -28,7 +29,7 @@ module ConstantBounds implements BoundSig<FloatDelta> {
|
||||
}
|
||||
}
|
||||
|
||||
private module RelativeBounds implements BoundSig<FloatDelta> {
|
||||
module RelativeBounds implements BoundSig<FloatDelta> {
|
||||
class SemBound instanceof SemanticBound::SemBound {
|
||||
SemBound() { not this instanceof SemanticBound::SemZeroBound }
|
||||
|
||||
@@ -46,11 +47,13 @@ private module RelativeBounds implements BoundSig<FloatDelta> {
|
||||
}
|
||||
}
|
||||
|
||||
private module ConstantStage =
|
||||
RangeStage<FloatDelta, ConstantBounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
module ConstantStage =
|
||||
RangeStage<FloatDelta, ConstantBounds, FloatOverflow, CppLangImplConstant,
|
||||
RangeUtil<FloatDelta, CppLangImplConstant>>;
|
||||
|
||||
private module RelativeStage =
|
||||
RangeStage<FloatDelta, RelativeBounds, CppLangImpl, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
module RelativeStage =
|
||||
RangeStage<FloatDelta, RelativeBounds, FloatOverflow, CppLangImplRelative,
|
||||
RangeUtil<FloatDelta, CppLangImplRelative>>;
|
||||
|
||||
private newtype TSemReason =
|
||||
TSemNoReason() or
|
||||
@@ -60,48 +63,52 @@ private newtype TSemReason =
|
||||
guard = any(RelativeStage::SemCondReason reason).getCond()
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound. This can either be `CondReason` if the bound
|
||||
* is due to a specific condition, or `NoReason` if the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
abstract class SemReason extends TSemReason {
|
||||
/** Gets a textual representation of this reason. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
class SemNoReason extends SemReason, TSemNoReason {
|
||||
override string toString() { result = "NoReason" }
|
||||
}
|
||||
|
||||
/** A reason for an inferred bound pointing to a condition. */
|
||||
class SemCondReason extends SemReason, TSemCondReason {
|
||||
/** Gets the condition that is the reason for the bound. */
|
||||
SemGuard getCond() { this = TSemCondReason(result) }
|
||||
|
||||
override string toString() { result = getCond().toString() }
|
||||
}
|
||||
|
||||
private ConstantStage::SemReason constantReason(SemReason reason) {
|
||||
ConstantStage::SemReason constantReason(SemReason reason) {
|
||||
result instanceof ConstantStage::SemNoReason and reason instanceof SemNoReason
|
||||
or
|
||||
result.(ConstantStage::SemCondReason).getCond() = reason.(SemCondReason).getCond()
|
||||
}
|
||||
|
||||
private RelativeStage::SemReason relativeReason(SemReason reason) {
|
||||
RelativeStage::SemReason relativeReason(SemReason reason) {
|
||||
result instanceof RelativeStage::SemNoReason and reason instanceof SemNoReason
|
||||
or
|
||||
result.(RelativeStage::SemCondReason).getCond() = reason.(SemCondReason).getCond()
|
||||
}
|
||||
|
||||
predicate semBounded(
|
||||
SemExpr e, SemanticBound::SemBound b, float delta, boolean upper, SemReason reason
|
||||
) {
|
||||
ConstantStage::semBounded(e, b, delta, upper, constantReason(reason))
|
||||
or
|
||||
RelativeStage::semBounded(e, b, delta, upper, relativeReason(reason))
|
||||
import Public
|
||||
|
||||
module Public {
|
||||
predicate semBounded(
|
||||
SemExpr e, SemanticBound::SemBound b, float delta, boolean upper, SemReason reason
|
||||
) {
|
||||
ConstantStage::semBounded(e, b, delta, upper, constantReason(reason))
|
||||
or
|
||||
RelativeStage::semBounded(e, b, delta, upper, relativeReason(reason))
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound. This can either be `CondReason` if the bound
|
||||
* is due to a specific condition, or `NoReason` if the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
abstract class SemReason extends TSemReason {
|
||||
/** Gets a textual representation of this reason. */
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A reason for an inferred bound that indicates that the bound is inferred
|
||||
* without going through a bounding condition.
|
||||
*/
|
||||
class SemNoReason extends SemReason, TSemNoReason {
|
||||
override string toString() { result = "NoReason" }
|
||||
}
|
||||
|
||||
/** A reason for an inferred bound pointing to a condition. */
|
||||
class SemCondReason extends SemReason, TSemCondReason {
|
||||
/** Gets the condition that is the reason for the bound. */
|
||||
SemGuard getCond() { this = TSemCondReason(result) }
|
||||
|
||||
override string toString() { result = getCond().toString() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* C++-specific implementation of range analysis.
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import RangeAnalysisStage
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.IntDelta
|
||||
private import RangeAnalysisImpl
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
module CppLangImplRelative implements LangSig<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.
|
||||
*
|
||||
* 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 ignoreExprBound(SemExpr e) {
|
||||
exists(boolean upper, float delta, ConstantBounds::SemZeroBound b, float lb, float ub |
|
||||
ConstantStage::semBounded(e, b, delta, upper, _) and
|
||||
typeBounds(e.getSemType(), lb, ub) and
|
||||
(
|
||||
upper = false and
|
||||
delta < lb
|
||||
or
|
||||
upper = true and
|
||||
delta > ub
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate typeBounds(SemType t, float lb, float ub) {
|
||||
exists(SemIntegerType integralType, float limit |
|
||||
integralType = t and limit = 2.pow(8 * integralType.getByteSize())
|
||||
|
|
||||
if integralType instanceof SemBooleanType
|
||||
then lb = 0 and ub = 1
|
||||
else
|
||||
if integralType.isSigned()
|
||||
then (
|
||||
lb = -(limit / 2) and ub = (limit / 2) - 1
|
||||
) else (
|
||||
lb = 0 and ub = limit - 1
|
||||
)
|
||||
)
|
||||
or
|
||||
// This covers all floating point types. The range is (-Inf, +Inf).
|
||||
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 `e >= bound + delta` (if `upper = false`) or `e <= bound + delta` (if `upper = true`).
|
||||
*/
|
||||
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() }
|
||||
}
|
||||
@@ -73,6 +73,7 @@ import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticCFG
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticType
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticOpcode
|
||||
private import ConstantAnalysis
|
||||
private import Sign
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticLocation
|
||||
|
||||
/**
|
||||
@@ -240,11 +241,19 @@ signature module BoundSig<DeltaSig D> {
|
||||
}
|
||||
}
|
||||
|
||||
module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<D> UtilParam> {
|
||||
signature module OverflowSig<DeltaSig D> {
|
||||
predicate semExprDoesNotOverflow(boolean positively, SemExpr expr);
|
||||
}
|
||||
|
||||
module RangeStage<
|
||||
DeltaSig D, BoundSig<D> Bounds, OverflowSig<D> OverflowParam, LangSig<D> LangParam,
|
||||
UtilSig<D> UtilParam>
|
||||
{
|
||||
private import Bounds
|
||||
private import LangParam
|
||||
private import UtilParam
|
||||
private import D
|
||||
private import OverflowParam
|
||||
|
||||
/**
|
||||
* An expression that does conversion, boxing, or unboxing
|
||||
@@ -920,6 +929,81 @@ module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<
|
||||
bounded(cast.getOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
|
||||
}
|
||||
|
||||
predicate bounded(
|
||||
SemExpr e, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge, D::Delta origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
initialBounded(e, b, delta, upper, fromBackEdge, origdelta, reason) and
|
||||
(
|
||||
semExprDoesNotOverflow(upper.booleanNot(), e)
|
||||
or
|
||||
not potentiallyOverflowingExpr(upper.booleanNot(), e)
|
||||
or
|
||||
exists(D::Delta otherDelta |
|
||||
initialBounded(e, _, otherDelta, upper.booleanNot(), _, _, _) and
|
||||
(
|
||||
upper = true and D::toFloat(otherDelta) >= 0
|
||||
or
|
||||
upper = false and D::toFloat(otherDelta) <= 0
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate potentiallyOverflowingExpr(boolean positively, SemExpr expr) {
|
||||
(
|
||||
expr.getOpcode() instanceof Opcode::Add or
|
||||
expr.getOpcode() instanceof Opcode::PointerAdd
|
||||
) and
|
||||
(
|
||||
positively = true and
|
||||
(
|
||||
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getLeftOperand())) = TPos() and
|
||||
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getRightOperand())) = TPos()
|
||||
)
|
||||
or
|
||||
positively = false and
|
||||
(
|
||||
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getLeftOperand())) = TNeg() and
|
||||
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getRightOperand())) = TNeg()
|
||||
)
|
||||
)
|
||||
or
|
||||
(
|
||||
expr.getOpcode() instanceof Opcode::Sub or
|
||||
expr.getOpcode() instanceof Opcode::PointerSub
|
||||
) and
|
||||
(
|
||||
positively = true and
|
||||
(
|
||||
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getLeftOperand())) = TPos() and
|
||||
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getRightOperand())) = TNeg()
|
||||
)
|
||||
or
|
||||
positively = false and
|
||||
(
|
||||
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getLeftOperand())) = TNeg() and
|
||||
pragma[only_bind_out](semExprSign(expr.(SemBinaryExpr).getRightOperand())) = TPos()
|
||||
)
|
||||
)
|
||||
or
|
||||
positively in [true, false] and
|
||||
(
|
||||
expr.getOpcode() instanceof Opcode::Mul or
|
||||
expr.getOpcode() instanceof Opcode::ShiftLeft
|
||||
)
|
||||
or
|
||||
positively = false and
|
||||
(
|
||||
expr.getOpcode() instanceof Opcode::Negate or
|
||||
expr.getOpcode() instanceof Opcode::SubOne or
|
||||
expr.(SemDivExpr).getSemType() instanceof SemFloatingPointType
|
||||
)
|
||||
or
|
||||
positively = true and
|
||||
expr.getOpcode() instanceof Opcode::AddOne
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a normal form of `x` where -0.0 has changed to +0.0. This can be
|
||||
* needed on the lesser side of a floating-point comparison or on both sides of
|
||||
@@ -934,7 +1018,7 @@ module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<
|
||||
* - `upper = true` : `e <= b + delta`
|
||||
* - `upper = false` : `e >= b + delta`
|
||||
*/
|
||||
private predicate bounded(
|
||||
predicate initialBounded(
|
||||
SemExpr e, SemBound b, D::Delta delta, boolean upper, boolean fromBackEdge, D::Delta origdelta,
|
||||
SemReason reason
|
||||
) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
private import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
private import RangeAnalysisSpecific
|
||||
private import RangeAnalysisRelativeSpecific
|
||||
private import RangeAnalysisStage as Range
|
||||
private import ConstantAnalysis
|
||||
|
||||
|
||||
@@ -314,9 +314,8 @@ class FreadBA extends BufferAccess {
|
||||
* but not:
|
||||
* &buffer[ix]
|
||||
*/
|
||||
class ArrayExprBA extends BufferAccess {
|
||||
class ArrayExprBA extends BufferAccess, ArrayExpr {
|
||||
ArrayExprBA() {
|
||||
exists(this.(ArrayExpr).getArrayOffset().getValue().toInt()) and
|
||||
not exists(AddressOfExpr aoe | aoe.getAChild() = this) and
|
||||
// exclude accesses in macro implementation of `strcmp`,
|
||||
// which are carefully controlled but can look dangerous.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.5.6
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.5
|
||||
|
||||
### Deprecated Queries
|
||||
|
||||
10
cpp/ql/src/Critical/DoubleFree.cpp
Normal file
10
cpp/ql/src/Critical/DoubleFree.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
int* f() {
|
||||
int *buff = malloc(SIZE*sizeof(int));
|
||||
do_stuff(buff);
|
||||
free(buff);
|
||||
int *new_buffer = malloc(SIZE*sizeof(int));
|
||||
free(buff); // BAD: If new_buffer is assigned the same address as buff,
|
||||
// the memory allocator will free the new buffer memory region,
|
||||
// leading to use-after-free problems and memory corruption.
|
||||
return new_buffer;
|
||||
}
|
||||
33
cpp/ql/src/Critical/DoubleFree.qhelp
Normal file
33
cpp/ql/src/Critical/DoubleFree.qhelp
Normal file
@@ -0,0 +1,33 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>
|
||||
Deallocating memory more than once can lead to a double-free vulnerability. This can be exploited to
|
||||
corrupt the allocator's internal data structures, which can lead to denial-of-service attacks by crashing
|
||||
the program, or security vulnerabilities, by allowing an attacker to overwrite arbitrary memory locations.
|
||||
</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
Ensure that all execution paths deallocate the allocated memory at most once. If possible, reassign
|
||||
the pointer to a null value after deallocating it. This will prevent double-free vulnerabilities since
|
||||
most deallocation functions will perform a null-pointer check before attempting to deallocate the memory.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example><sample src="DoubleFree.cpp" />
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
OWASP:
|
||||
<a href="https://owasp.org/www-community/vulnerabilities/Doubly_freeing_memory">Doubly freeing memory</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
49
cpp/ql/src/Critical/DoubleFree.ql
Normal file
49
cpp/ql/src/Critical/DoubleFree.ql
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* @name Potential double free
|
||||
* @description Freeing a resource more than once can lead to undefined behavior and cause memory corruption.
|
||||
* @kind path-problem
|
||||
* @precision medium
|
||||
* @id cpp/double-free
|
||||
* @problem.severity warning
|
||||
* @security-severity 9.3
|
||||
* @tags reliability
|
||||
* security
|
||||
* external/cwe/cwe-415
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import FlowAfterFree
|
||||
import DoubleFree::PathGraph
|
||||
|
||||
predicate isFree(DataFlow::Node n, Expr e) { isFree(n, e, _) }
|
||||
|
||||
/**
|
||||
* `dealloc1` is a deallocation expression and `e` is an expression such
|
||||
* that is deallocated by a deallocation expression, and the `(dealloc1, e)` pair
|
||||
* should be excluded by the `FlowFromFree` library.
|
||||
*
|
||||
* Note that `e` is not necessarily the expression deallocated by `dealloc1`. It will
|
||||
* be bound to the second deallocation as identified by the `FlowFromFree` library.
|
||||
*/
|
||||
bindingset[dealloc1, e]
|
||||
predicate isExcludeFreePair(DeallocationExpr dealloc1, Expr e) {
|
||||
exists(DeallocationExpr dealloc2 | isFree(_, e, dealloc2) |
|
||||
dealloc1.(FunctionCall).getTarget().hasGlobalName("MmFreePagesFromMdl") and
|
||||
// From https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-mmfreepagesfrommdl:
|
||||
// "After calling MmFreePagesFromMdl, the caller must also call ExFreePool
|
||||
// to release the memory that was allocated for the MDL structure."
|
||||
isExFreePoolCall(dealloc2, _)
|
||||
)
|
||||
}
|
||||
|
||||
module DoubleFree = FlowFromFree<isFree/2, isExcludeFreePair/2>;
|
||||
|
||||
from DoubleFree::PathNode source, DoubleFree::PathNode sink, DeallocationExpr dealloc, Expr e2
|
||||
where
|
||||
DoubleFree::flowPath(source, sink) and
|
||||
isFree(source.getNode(), _, dealloc) and
|
||||
isFree(sink.getNode(), e2)
|
||||
select sink.getNode(), source, sink,
|
||||
"Memory pointed to by '" + e2.toString() + "' may already have been freed by $@.", dealloc,
|
||||
dealloc.toString()
|
||||
129
cpp/ql/src/Critical/FlowAfterFree.qll
Normal file
129
cpp/ql/src/Critical/FlowAfterFree.qll
Normal file
@@ -0,0 +1,129 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
private import semmle.code.cpp.ir.IR
|
||||
|
||||
/**
|
||||
* Signature for a predicate that holds if `n.asExpr() = e` and `n` is a sink in
|
||||
* the `FlowFromFreeConfig` module.
|
||||
*/
|
||||
private signature predicate isSinkSig(DataFlow::Node n, Expr e);
|
||||
|
||||
/**
|
||||
* Holds if `dealloc` is a deallocation expression and `e` is an expression such
|
||||
* that `isFree(_, e)` holds for some `isFree` predicate satisfying `isSinkSig`,
|
||||
* and this source-sink pair should be excluded from the analysis.
|
||||
*/
|
||||
bindingset[dealloc, e]
|
||||
private signature predicate isExcludedSig(DeallocationExpr dealloc, Expr e);
|
||||
|
||||
/**
|
||||
* Holds if `(b1, i1)` strictly post-dominates `(b2, i2)`
|
||||
*/
|
||||
bindingset[i1, i2]
|
||||
predicate strictlyPostDominates(IRBlock b1, int i1, IRBlock b2, int i2) {
|
||||
b1 = b2 and
|
||||
i1 > i2
|
||||
or
|
||||
b1.strictlyPostDominates(b2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `(b1, i1)` strictly dominates `(b2, i2)`
|
||||
*/
|
||||
bindingset[i1, i2]
|
||||
predicate strictlyDominates(IRBlock b1, int i1, IRBlock b2, int i2) {
|
||||
b1 = b2 and
|
||||
i1 < i2
|
||||
or
|
||||
b1.strictlyDominates(b2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a `FlowFromFreeConfig` module that can be used to find flow between
|
||||
* a pointer being freed by some deallocation function, and a user-specified sink.
|
||||
*
|
||||
* In order to reduce false positives, the set of sinks is restricted to only those
|
||||
* that satisfy at least one of the following two criteria:
|
||||
* 1. The source dominates the sink, or
|
||||
* 2. The sink post-dominates the source.
|
||||
*/
|
||||
module FlowFromFree<isSinkSig/2 isASink, isExcludedSig/2 isExcluded> {
|
||||
module FlowFromFreeConfig implements DataFlow::StateConfigSig {
|
||||
class FlowState instanceof Expr {
|
||||
FlowState() { isFree(_, this, _) }
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
}
|
||||
|
||||
predicate isSource(DataFlow::Node node, FlowState state) { isFree(node, state, _) }
|
||||
|
||||
pragma[inline]
|
||||
predicate isSink(DataFlow::Node sink, FlowState state) {
|
||||
exists(
|
||||
Expr e, DataFlow::Node source, IRBlock b1, int i1, IRBlock b2, int i2,
|
||||
DeallocationExpr dealloc
|
||||
|
|
||||
isASink(sink, e) and
|
||||
isFree(source, state, dealloc) and
|
||||
e != state and
|
||||
source.hasIndexInBlock(b1, i1) and
|
||||
sink.hasIndexInBlock(b2, i2) and
|
||||
not isExcluded(dealloc, e)
|
||||
|
|
||||
strictlyDominates(b1, i1, b2, i2)
|
||||
or
|
||||
strictlyPostDominates(b2, i2, b1, i1)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrierIn(DataFlow::Node n) {
|
||||
n.asIndirectExpr() = any(AddressOfExpr aoe)
|
||||
or
|
||||
n.asIndirectExpr() = any(Call call).getAnArgument()
|
||||
or
|
||||
exists(Expr e |
|
||||
n.asIndirectExpr() = e.(PointerDereferenceExpr).getOperand() or
|
||||
n.asIndirectExpr() = e.(ArrayExpr).getArrayBase()
|
||||
|
|
||||
e = any(StoreInstruction store).getDestinationAddress().getUnconvertedResultExpression()
|
||||
)
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node n, FlowState state) { none() }
|
||||
|
||||
predicate isAdditionalFlowStep(
|
||||
DataFlow::Node n1, FlowState state1, DataFlow::Node n2, FlowState state2
|
||||
) {
|
||||
none()
|
||||
}
|
||||
}
|
||||
|
||||
import DataFlow::GlobalWithState<FlowFromFreeConfig>
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` is a dataflow node such that `n.asExpr() = e` and `e`
|
||||
* is being freed by a deallocation expression `dealloc`.
|
||||
*/
|
||||
predicate isFree(DataFlow::Node n, Expr e, DeallocationExpr dealloc) {
|
||||
e = dealloc.getFreedExpr() and
|
||||
e = n.asExpr() and
|
||||
// Ignore realloc functions
|
||||
not exists(dealloc.(FunctionCall).getTarget().(AllocationFunction).getReallocPtrArg())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `fc` is a function call that is the result of expanding
|
||||
* the `ExFreePool` macro.
|
||||
*/
|
||||
predicate isExFreePoolCall(FunctionCall fc, Expr e) {
|
||||
e = fc.getArgument(0) and
|
||||
(
|
||||
exists(MacroInvocation mi |
|
||||
mi.getMacroName() = "ExFreePool" and
|
||||
mi.getExpr() = fc
|
||||
)
|
||||
or
|
||||
fc.getTarget().hasGlobalName("ExFreePool")
|
||||
)
|
||||
}
|
||||
@@ -16,160 +16,133 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.commons.Scanf
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
import semmle.code.cpp.ir.dataflow.DataFlow
|
||||
import semmle.code.cpp.dataflow.new.DataFlow::DataFlow
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
/**
|
||||
* Holds if `call` is a `scanf`-like function that may write to `output` at index `index`.
|
||||
*
|
||||
* Furthermore, `instr` is the instruction that defines the address of the `index`'th argument
|
||||
* of `call`, and `vn` is the value number of `instr.`
|
||||
*/
|
||||
predicate isSource(ScanfFunctionCall call, int index, Instruction instr, ValueNumber vn, Expr output) {
|
||||
output = call.getOutputArgument(index).getFullyConverted() and
|
||||
instr.getConvertedResultExpression() = output and
|
||||
vn.getAnInstruction() = instr
|
||||
/** Holds if `n` reaches an argument to a call to a `scanf`-like function. */
|
||||
pragma[nomagic]
|
||||
predicate revFlow0(Node n) {
|
||||
isSink(_, _, n, _)
|
||||
or
|
||||
exists(Node succ | revFlow0(succ) | localFlowStep(n, succ))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is control-flow reachable in 0 or more steps from
|
||||
* a call to a `scanf`-like function.
|
||||
* Holds if `n` represents an uninitialized stack-allocated variable, or a
|
||||
* newly (and presumed uninitialized) heap allocation.
|
||||
*/
|
||||
predicate isUninitialized(Node n) {
|
||||
exists(n.asUninitialized()) or
|
||||
n.asIndirectExpr(1) instanceof AllocationExpr
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate fwdFlow0(Instruction instr) {
|
||||
isSource(_, _, instr, _, _)
|
||||
or
|
||||
exists(Instruction prev |
|
||||
fwdFlow0(prev) and
|
||||
prev.getASuccessor() = instr
|
||||
predicate fwdFlow0(Node n) {
|
||||
revFlow0(n) and
|
||||
(
|
||||
isUninitialized(n)
|
||||
or
|
||||
exists(Node prev |
|
||||
fwdFlow0(prev) and
|
||||
localFlowStep(prev, n)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of the IR translation of `access` that
|
||||
* is not an expression being deallocated, and `instr` has value
|
||||
* number `vn`.
|
||||
*/
|
||||
predicate isSink(Instruction instr, Access access, ValueNumber vn) {
|
||||
instr.getAst() = access and
|
||||
not any(DeallocationExpr dealloc).getFreedExpr() = access and
|
||||
vn.getAnInstruction() = instr
|
||||
predicate isSink(ScanfFunctionCall call, int index, Node n, Expr input) {
|
||||
input = call.getOutputArgument(index) and
|
||||
n.asIndirectExpr() = input
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a path from a call to a `scanf`-like function
|
||||
* Holds if `call` is a `scanf`-like call and `output` is the `index`'th
|
||||
* argument that has not been previously initialized.
|
||||
*/
|
||||
predicate isRelevantScanfCall(ScanfFunctionCall call, int index, Expr output) {
|
||||
exists(Node n | fwdFlow0(n) and isSink(call, index, n, output))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` is a `scanf`-like function that may write to `output` at
|
||||
* index `index` and `n` is the dataflow node that represents the data after
|
||||
* it has been written to by `call`.
|
||||
*/
|
||||
predicate isSource(ScanfFunctionCall call, int index, Node n, Expr output) {
|
||||
isRelevantScanfCall(call, index, output) and
|
||||
output = call.getOutputArgument(index) and
|
||||
n.asDefiningArgument() = output
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` is reachable from an output argument of a relevant call to
|
||||
* a `scanf`-like function.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate fwdFlow(Node n) {
|
||||
isSource(_, _, n, _)
|
||||
or
|
||||
exists(Node prev |
|
||||
fwdFlow(prev) and
|
||||
localFlowStep(prev, n) and
|
||||
not isSanitizerOut(prev)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n` should not have outgoing flow. */
|
||||
predicate isSanitizerOut(Node n) {
|
||||
// We disable flow out of sinks to reduce result duplication
|
||||
isSink(n, _)
|
||||
or
|
||||
// If the node is being passed to a function it may be
|
||||
// modified, and thus it's safe to later read the value.
|
||||
exists(n.asIndirectArgument())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` is a node such that `n.asExpr() = e` and `e` is not an
|
||||
* argument of a deallocation expression.
|
||||
*/
|
||||
predicate isSink(Node n, Expr e) {
|
||||
n.asExpr() = e and
|
||||
not any(DeallocationExpr dealloc).getFreedExpr() = e
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` is part of a path from a call to a `scanf`-like function
|
||||
* to a use of the written variable.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate revFlow0(Instruction instr) {
|
||||
fwdFlow0(instr) and
|
||||
predicate revFlow(Node n) {
|
||||
fwdFlow(n) and
|
||||
(
|
||||
isSink(instr, _, _)
|
||||
isSink(n, _)
|
||||
or
|
||||
exists(Instruction succ | revFlow0(succ) | instr.getASuccessor() = succ)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a path from a call to a `scanf`-like function
|
||||
* that writes to a variable with value number `vn`, without passing through
|
||||
* redefinitions of the variable.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate fwdFlow(Instruction instr, ValueNumber vn) {
|
||||
revFlow0(instr) and
|
||||
(
|
||||
isSource(_, _, instr, vn, _)
|
||||
or
|
||||
exists(Instruction prev |
|
||||
fwdFlow(prev, vn) and
|
||||
prev.getASuccessor() = instr and
|
||||
not isBarrier(instr, vn)
|
||||
exists(Node succ |
|
||||
revFlow(succ) and
|
||||
localFlowStep(n, succ) and
|
||||
not isSanitizerOut(n)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is part of a path from a call to a `scanf`-like function
|
||||
* that writes to a variable with value number `vn`, without passing through
|
||||
* redefinitions of the variable.
|
||||
*
|
||||
* Note: This predicate only holds for the `(intr, vn)` pairs that are also
|
||||
* control-flow reachable from an argument to a `scanf`-like function call.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate revFlow(Instruction instr, ValueNumber vn) {
|
||||
fwdFlow(instr, pragma[only_bind_out](vn)) and
|
||||
(
|
||||
isSink(instr, _, vn)
|
||||
or
|
||||
exists(Instruction succ | revFlow(succ, vn) |
|
||||
instr.getASuccessor() = succ and
|
||||
not isBarrier(succ, vn)
|
||||
)
|
||||
)
|
||||
/** A local flow step, restricted to relevant dataflow nodes. */
|
||||
private predicate step(Node n1, Node n2) {
|
||||
revFlow(n1) and
|
||||
revFlow(n2) and
|
||||
localFlowStep(n1, n2)
|
||||
}
|
||||
|
||||
/**
|
||||
* A type that bundles together a reachable instruction with the appropriate
|
||||
* value number (i.e., the value number that's transferred from the source
|
||||
* to the sink).
|
||||
*/
|
||||
newtype TNode = MkNode(Instruction instr, ValueNumber vn) { revFlow(instr, vn) }
|
||||
|
||||
class Node extends MkNode {
|
||||
ValueNumber vn;
|
||||
Instruction instr;
|
||||
|
||||
Node() { this = MkNode(instr, vn) }
|
||||
|
||||
final string toString() { result = instr.toString() }
|
||||
|
||||
final Node getASuccessor() { result = MkNode(pragma[only_bind_out](instr.getASuccessor()), vn) }
|
||||
|
||||
final Location getLocation() { result = instr.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `instr` is an instruction with value number `vn` that is
|
||||
* used in a store operation, or is overwritten by another call to
|
||||
* a `scanf`-like function.
|
||||
*/
|
||||
private predicate isBarrier(Instruction instr, ValueNumber vn) {
|
||||
// We only need to compute barriers for instructions that we
|
||||
// managed to hit during the initial flow stage.
|
||||
revFlow0(pragma[only_bind_into](instr)) and
|
||||
valueNumber(instr) = vn and
|
||||
exists(Expr e | instr.getAst() = e |
|
||||
instr = any(StoreInstruction s).getDestinationAddress()
|
||||
or
|
||||
isSource(_, _, _, _, [e, e.getParent().(AddressOfExpr)])
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `n1` steps to `n2` in a single step. */
|
||||
predicate isSuccessor(Node n1, Node n2) { n1.getASuccessor() = n2 }
|
||||
|
||||
predicate hasFlow(Node n1, Node n2) = fastTC(isSuccessor/2)(n1, n2)
|
||||
|
||||
Node getNode(Instruction instr, ValueNumber vn) { result = MkNode(instr, vn) }
|
||||
predicate hasFlow(Node n1, Node n2) = fastTC(step/2)(n1, n2)
|
||||
|
||||
/**
|
||||
* Holds if `source` is the `index`'th argument to the `scanf`-like call `call`, and `sink` is
|
||||
* an instruction that is part of the translation of `access` which is a transitive
|
||||
* control-flow successor of `call`.
|
||||
*
|
||||
* Furthermore, `source` and `sink` have identical global value numbers.
|
||||
* a dataflow node that represents the expression `e`.
|
||||
*/
|
||||
predicate hasFlow(
|
||||
Instruction source, ScanfFunctionCall call, int index, Instruction sink, Access access
|
||||
) {
|
||||
exists(ValueNumber vn |
|
||||
isSource(call, index, source, vn, _) and
|
||||
hasFlow(getNode(source, pragma[only_bind_into](vn)), getNode(sink, pragma[only_bind_into](vn))) and
|
||||
isSink(sink, access, vn)
|
||||
)
|
||||
predicate hasFlow(Node source, ScanfFunctionCall call, int index, Node sink, Expr e) {
|
||||
isSource(call, index, source, _) and
|
||||
hasFlow(source, sink) and
|
||||
isSink(sink, e)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,7 +150,7 @@ predicate hasFlow(
|
||||
* success in writing the output argument at index `index`.
|
||||
*/
|
||||
int getMinimumGuardConstant(ScanfFunctionCall call, int index) {
|
||||
isSource(call, index, _, _, _) and
|
||||
isSource(call, index, _, _) and
|
||||
result =
|
||||
index + 1 -
|
||||
count(ScanfFormatLiteral f, int n |
|
||||
@@ -191,7 +164,7 @@ int getMinimumGuardConstant(ScanfFunctionCall call, int index) {
|
||||
* Holds the access to `e` isn't guarded by a check that ensures that `call` returned
|
||||
* at least `minGuard`.
|
||||
*/
|
||||
predicate hasNonGuardedAccess(ScanfFunctionCall call, Access e, int minGuard) {
|
||||
predicate hasNonGuardedAccess(ScanfFunctionCall call, Expr e, int minGuard) {
|
||||
exists(int index |
|
||||
hasFlow(_, call, index, _, e) and
|
||||
minGuard = getMinimumGuardConstant(call, index)
|
||||
@@ -211,7 +184,7 @@ BasicBlock blockGuardedBy(int value, string op, ScanfFunctionCall call) {
|
||||
exists(GuardCondition g, Expr left, Expr right |
|
||||
right = g.getAChild() and
|
||||
value = left.getValue().toInt() and
|
||||
DataFlow::localExprFlow(call, right)
|
||||
localExprFlow(call, right)
|
||||
|
|
||||
g.ensuresEq(left, right, 0, result, true) and op = "=="
|
||||
or
|
||||
@@ -221,9 +194,9 @@ BasicBlock blockGuardedBy(int value, string op, ScanfFunctionCall call) {
|
||||
)
|
||||
}
|
||||
|
||||
from ScanfFunctionCall call, Access access, int minGuard
|
||||
where hasNonGuardedAccess(call, access, minGuard)
|
||||
select access,
|
||||
from ScanfFunctionCall call, Expr e, int minGuard
|
||||
where hasNonGuardedAccess(call, e, minGuard)
|
||||
select e,
|
||||
"This variable is read, but may not have been written. " +
|
||||
"It should be guarded by a check that the $@ returns at least " + minGuard + ".", call,
|
||||
call.toString()
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* @name Potential use after free
|
||||
* @description An allocated memory block is used after it has been freed. Behavior in such cases is undefined and can cause memory corruption.
|
||||
* @kind problem
|
||||
* @kind path-problem
|
||||
* @precision medium
|
||||
* @id cpp/use-after-free
|
||||
* @problem.severity warning
|
||||
* @security-severity 9.3
|
||||
@@ -11,56 +12,158 @@
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.StackVariableReachability
|
||||
import semmle.code.cpp.dataflow.new.DataFlow
|
||||
import semmle.code.cpp.ir.IR
|
||||
import FlowAfterFree
|
||||
import UseAfterFree::PathGraph
|
||||
|
||||
/** `e` is an expression that frees the memory pointed to by `v`. */
|
||||
predicate isFreeExpr(Expr e, StackVariable v) {
|
||||
exists(VariableAccess va | va.getTarget() = v |
|
||||
exists(FunctionCall fc | fc = e |
|
||||
fc.getTarget().hasGlobalOrStdName("free") and
|
||||
va = fc.getArgument(0)
|
||||
)
|
||||
or
|
||||
e.(DeleteExpr).getExpr() = va
|
||||
or
|
||||
e.(DeleteArrayExpr).getExpr() = va
|
||||
/**
|
||||
* Holds if `call` is a call to a function that obviously
|
||||
* doesn't dereference its `i`'th argument.
|
||||
*/
|
||||
private predicate externalCallNeverDereferences(FormattingFunctionCall call, int arg) {
|
||||
exists(int formatArg |
|
||||
pragma[only_bind_out](call.getFormatArgument(formatArg)) =
|
||||
pragma[only_bind_out](call.getArgument(arg)) and
|
||||
call.getFormat().(FormatLiteral).getConvSpec(formatArg) != "%s"
|
||||
)
|
||||
}
|
||||
|
||||
/** `e` is an expression that (may) dereference `v`. */
|
||||
predicate isDerefExpr(Expr e, StackVariable v) {
|
||||
v.getAnAccess() = e and dereferenced(e)
|
||||
or
|
||||
isDerefByCallExpr(_, _, e, v)
|
||||
predicate isUse0(DataFlow::Node n, Expr e) {
|
||||
e = n.asExpr() and
|
||||
not isFree(_, e, _) and
|
||||
(
|
||||
e = any(PointerDereferenceExpr pde).getOperand()
|
||||
or
|
||||
e = any(PointerFieldAccess pfa).getQualifier()
|
||||
or
|
||||
e = any(ArrayExpr ae).getArrayBase()
|
||||
or
|
||||
e = any(Call call).getQualifier()
|
||||
or
|
||||
// Assume any function without a body will dereference the pointer
|
||||
exists(int i, Call call, Function f |
|
||||
n.asExpr() = call.getArgument(i) and
|
||||
f = call.getTarget() and
|
||||
not f.hasEntryPoint() and
|
||||
// Exclude known functions we know won't dereference the pointer.
|
||||
// For example, a call such as `printf("%p", myPointer)`.
|
||||
not externalCallNeverDereferences(call, i)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* `va` is passed by value as (part of) the `i`th argument in
|
||||
* call `c`. The target function is either a library function
|
||||
* or a source code function that dereferences the relevant
|
||||
* parameter.
|
||||
*/
|
||||
predicate isDerefByCallExpr(Call c, int i, VariableAccess va, StackVariable v) {
|
||||
v.getAnAccess() = va and
|
||||
va = c.getAnArgumentSubExpr(i) and
|
||||
not c.passesByReference(i, va) and
|
||||
(c.getTarget().hasEntryPoint() implies isDerefExpr(_, c.getTarget().getParameter(i)))
|
||||
}
|
||||
module ParameterSinks {
|
||||
import semmle.code.cpp.ir.ValueNumbering
|
||||
|
||||
class UseAfterFreeReachability extends StackVariableReachability {
|
||||
UseAfterFreeReachability() { this = "UseAfterFree" }
|
||||
predicate flowsToUse(DataFlow::Node n) {
|
||||
isUse0(n, _)
|
||||
or
|
||||
exists(DataFlow::Node succ |
|
||||
flowsToUse(succ) and
|
||||
DataFlow::localFlowStep(n, succ)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSource(ControlFlowNode node, StackVariable v) { isFreeExpr(node, v) }
|
||||
private predicate flowsFromParam(DataFlow::Node n) {
|
||||
flowsToUse(n) and
|
||||
(
|
||||
n.asParameter().getUnspecifiedType() instanceof PointerType
|
||||
or
|
||||
exists(DataFlow::Node prev |
|
||||
flowsFromParam(prev) and
|
||||
DataFlow::localFlowStep(prev, n)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(ControlFlowNode node, StackVariable v) { isDerefExpr(node, v) }
|
||||
private predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
flowsFromParam(n1) and
|
||||
flowsFromParam(n2) and
|
||||
DataFlow::localFlowStep(n1, n2)
|
||||
}
|
||||
|
||||
override predicate isBarrier(ControlFlowNode node, StackVariable v) {
|
||||
definitionBarrier(v, node) or
|
||||
isFreeExpr(node, v)
|
||||
private predicate paramToUse(DataFlow::Node n1, DataFlow::Node n2) = fastTC(step/2)(n1, n2)
|
||||
|
||||
private predicate hasFlow(
|
||||
DataFlow::Node source, InitializeParameterInstruction init, DataFlow::Node sink
|
||||
) {
|
||||
pragma[only_bind_out](source.asParameter()) = pragma[only_bind_out](init.getParameter()) and
|
||||
paramToUse(source, sink) and
|
||||
isUse0(sink, _)
|
||||
}
|
||||
|
||||
private InitializeParameterInstruction getAnAlwaysDereferencedParameter0() {
|
||||
exists(DataFlow::Node source, DataFlow::Node sink, IRBlock b1, int i1, IRBlock b2, int i2 |
|
||||
hasFlow(pragma[only_bind_into](source), result, pragma[only_bind_into](sink)) and
|
||||
source.hasIndexInBlock(b1, pragma[only_bind_into](i1)) and
|
||||
sink.hasIndexInBlock(b2, pragma[only_bind_into](i2)) and
|
||||
strictlyPostDominates(b2, i2, b1, i1)
|
||||
)
|
||||
}
|
||||
|
||||
private CallInstruction getAnAlwaysReachedCallInstruction(IRFunction f) {
|
||||
result.getBlock().postDominates(f.getEntryBlock())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate callHasTargetAndArgument(Function f, int i, CallInstruction call, Instruction argument) {
|
||||
call.getStaticCallTarget() = f and
|
||||
call.getArgument(i) = argument
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate initializeParameterInFunction(Function f, int i, InitializeParameterInstruction init) {
|
||||
pragma[only_bind_out](init.getEnclosingFunction()) = f and
|
||||
init.hasIndex(i)
|
||||
}
|
||||
|
||||
InitializeParameterInstruction getAnAlwaysDereferencedParameter() {
|
||||
result = getAnAlwaysDereferencedParameter0()
|
||||
or
|
||||
exists(
|
||||
CallInstruction call, int i, InitializeParameterInstruction p, Instruction argument,
|
||||
Function f
|
||||
|
|
||||
callHasTargetAndArgument(f, i, call, argument) and
|
||||
initializeParameterInFunction(f, i, p) and
|
||||
p = getAnAlwaysDereferencedParameter() and
|
||||
result = pragma[only_bind_out](valueNumber(argument).getAnInstruction()) and
|
||||
call = getAnAlwaysReachedCallInstruction(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from UseAfterFreeReachability r, StackVariable v, Expr free, Expr e
|
||||
where r.reaches(free, v, e)
|
||||
select e, "Memory pointed to by '" + v.getName().toString() + "' may have $@.", free,
|
||||
"been previously freed"
|
||||
predicate isUse(DataFlow::Node n, Expr e) {
|
||||
isUse0(n, e)
|
||||
or
|
||||
exists(CallInstruction call, int i, InitializeParameterInstruction init |
|
||||
n.asOperand().getDef().getUnconvertedResultExpression() = e and
|
||||
init = ParameterSinks::getAnAlwaysDereferencedParameter() and
|
||||
call.getArgumentOperand(i) = n.asOperand() and
|
||||
init.hasIndex(i) and
|
||||
init.getEnclosingFunction() = call.getStaticCallTarget()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* `dealloc1` is a deallocation expression, `e` is an expression that dereferences a
|
||||
* pointer, and the `(dealloc1, e)` pair should be excluded by the `FlowFromFree` library.
|
||||
*/
|
||||
bindingset[dealloc1, e]
|
||||
predicate isExcludeFreeUsePair(DeallocationExpr dealloc1, Expr e) {
|
||||
// From https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-mmfreepagesfrommdl:
|
||||
// "After calling MmFreePagesFromMdl, the caller must also call ExFreePool
|
||||
// to release the memory that was allocated for the MDL structure."
|
||||
dealloc1.(FunctionCall).getTarget().hasGlobalName("MmFreePagesFromMdl") and
|
||||
isExFreePoolCall(_, e)
|
||||
}
|
||||
|
||||
module UseAfterFree = FlowFromFree<isUse/2, isExcludeFreeUsePair/2>;
|
||||
|
||||
from UseAfterFree::PathNode source, UseAfterFree::PathNode sink, DeallocationExpr dealloc
|
||||
where
|
||||
UseAfterFree::flowPath(source, sink) and
|
||||
isFree(source.getNode(), _, dealloc)
|
||||
select sink.getNode(), source, sink, "Memory may have been previously freed by $@.", dealloc,
|
||||
dealloc.toString()
|
||||
|
||||
@@ -3,23 +3,21 @@
|
||||
* @description Checking a pointer for nullness after dereferencing it is
|
||||
* likely to be a sign that either the check can be removed, or
|
||||
* it should be moved before the dereference.
|
||||
* @kind problem
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id cpp/redundant-null-check-simple
|
||||
* @tags reliability
|
||||
* correctness
|
||||
* external/cwe/cwe-476
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note: this query is not assigned a precision yet because we don't want it
|
||||
* to be included in query suites until its performance is well understood.
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.ir.IR
|
||||
import semmle.code.cpp.ir.ValueNumbering
|
||||
import PathGraph
|
||||
|
||||
/** An instruction that represents a null pointer. */
|
||||
class NullInstruction extends ConstantValueInstruction {
|
||||
NullInstruction() {
|
||||
this.getValue() = "0" and
|
||||
@@ -27,6 +25,10 @@ class NullInstruction extends ConstantValueInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `checked` is an instruction that is checked against a null value,
|
||||
* and `bool` is the instruction that represents the result of the comparison
|
||||
*/
|
||||
predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
|
||||
bool =
|
||||
any(CompareInstruction cmp |
|
||||
@@ -49,7 +51,7 @@ predicate explicitNullTestOfInstruction(Instruction checked, Instruction bool) {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
pragma[nomagic]
|
||||
predicate candidateResult(LoadInstruction checked, ValueNumber value, IRBlock dominator) {
|
||||
explicitNullTestOfInstruction(checked, _) and
|
||||
not checked.getAst().isInMacroExpansion() and
|
||||
@@ -57,6 +59,147 @@ predicate candidateResult(LoadInstruction checked, ValueNumber value, IRBlock do
|
||||
dominator.dominates(checked.getBlock())
|
||||
}
|
||||
|
||||
/**
|
||||
* This module constructs a pretty edges relation out of the results produced by
|
||||
* the `candidateResult` predicate: We create a path using the instruction successor-
|
||||
* relation from the dereference to the null check. To avoid generating very long paths,
|
||||
* we compact the edges relation so that `edges(i1, i2)` only holds when `i2` is the first
|
||||
* instruction that is control-flow reachable from `i1` with the same global value number
|
||||
* as `i1`.
|
||||
*/
|
||||
module PathGraph {
|
||||
/**
|
||||
* Holds if `deref` is a load instruction that loads a value
|
||||
* from the address `address`. This predicate is restricted to
|
||||
* those pairs for which we will end up reporting a result.
|
||||
*/
|
||||
private predicate isSource(Instruction address, LoadInstruction deref) {
|
||||
exists(ValueNumber sourceValue |
|
||||
candidateResult(_, sourceValue, deref.getBlock()) and
|
||||
sourceValue.getAnInstruction() = address and
|
||||
deref.getSourceAddress() = address
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `checked` has global value number `vn` and is an instruction that is
|
||||
* used in a check against a null value.
|
||||
*/
|
||||
private predicate isSink(LoadInstruction checked, ValueNumber vn) {
|
||||
candidateResult(checked, vn, _)
|
||||
}
|
||||
|
||||
/** Holds if `i` is control-flow reachable from a relevant `LoadInstruction`. */
|
||||
private predicate fwdFlow(Instruction i) {
|
||||
isSource(i, _)
|
||||
or
|
||||
exists(Instruction mid |
|
||||
fwdFlow(mid) and
|
||||
mid.getASuccessor() = i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `i` is part of a path from a relevant `LoadInstruction` to a
|
||||
* check against a null value that compares a value against an instruction
|
||||
* with global value number `vn`.
|
||||
*/
|
||||
private predicate revFlow(Instruction i, ValueNumber vn) {
|
||||
fwdFlow(i) and
|
||||
(
|
||||
isSink(i, vn)
|
||||
or
|
||||
exists(Instruction mid |
|
||||
revFlow(mid, vn) and
|
||||
i.getASuccessor() = mid
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a first control-flow successor of `i` that has the same
|
||||
* global value number as `i`.
|
||||
*/
|
||||
private Instruction getASuccessor(Instruction i) {
|
||||
exists(ValueNumber vn |
|
||||
vn.getAnInstruction() = i and
|
||||
result = getASuccessorWithValueNumber(i, vn)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a first control-flow successor of `i` that has the same
|
||||
* global value number as `i`. Furthermore, `i` has global value
|
||||
* number `vn`.
|
||||
*/
|
||||
private Instruction getASuccessorWithValueNumber(Instruction i, ValueNumber vn) {
|
||||
revFlow(i, vn) and
|
||||
result = getASuccessorWithValueNumber0(vn, i.getASuccessor()) and
|
||||
vn.getAnInstruction() = i
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private Instruction getASuccessorWithValueNumber0(ValueNumber vn, Instruction i) {
|
||||
result = getASuccessorIfDifferentValueNumberTC(vn, i) and
|
||||
vn.getAnInstruction() = result
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the reflexive transitive closure of `getASuccessorIfDifferentValueNumber`.
|
||||
*/
|
||||
private Instruction getASuccessorIfDifferentValueNumberTC(ValueNumber vn, Instruction i) {
|
||||
revFlow(i, vn) and
|
||||
(
|
||||
i = result and
|
||||
vn.getAnInstruction() = i
|
||||
or
|
||||
exists(Instruction mid |
|
||||
mid = getASuccessorIfDifferentValueNumber(vn, i) and
|
||||
result = getASuccessorIfDifferentValueNumberTC(vn, mid)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instruction that is a control-flow successor of `i` and which is not assigned
|
||||
* the global value number `vn`.
|
||||
*/
|
||||
private Instruction getASuccessorIfDifferentValueNumber(ValueNumber vn, Instruction i) {
|
||||
revFlow(i, vn) and
|
||||
revFlow(result, vn) and
|
||||
not vn.getAnInstruction() = i and
|
||||
pragma[only_bind_into](result) = pragma[only_bind_into](i).getASuccessor()
|
||||
}
|
||||
|
||||
query predicate nodes(Instruction i, string key, string val) {
|
||||
revFlow(i, _) and
|
||||
key = "semmle.label" and
|
||||
val = i.getAst().toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* The control-flow successor relation, compacted by stepping
|
||||
* over instruction that don't preserve the global value number.
|
||||
*
|
||||
* There is one exception to the above preservation rule: The
|
||||
* initial step from the `LoadInstruction` (that is, the sink)
|
||||
* steps to the first control-flow reachable instruction that
|
||||
* has the same value number as the load instruction's address
|
||||
* operand.
|
||||
*/
|
||||
query predicate edges(Instruction i1, Instruction i2) {
|
||||
getASuccessor(i1) = i2
|
||||
or
|
||||
// We could write `isSource(i2, i1)` here, but that would
|
||||
// include a not-very-informative step from `*p` to `p`.
|
||||
// So we collapse `*p` -> `p` -> `q` to `*p` -> `q`.
|
||||
exists(Instruction mid |
|
||||
isSource(mid, i1) and
|
||||
getASuccessor(mid) = i2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from LoadInstruction checked, LoadInstruction deref, ValueNumber sourceValue, IRBlock dominator
|
||||
where
|
||||
candidateResult(checked, sourceValue, dominator) and
|
||||
@@ -67,5 +210,5 @@ where
|
||||
// the pointer was null. To follow this idea to its full generality, we
|
||||
// should also give an alert when `check` post-dominates `deref`.
|
||||
deref.getBlock() = dominator
|
||||
select checked, "This null check is redundant because $@ in any case.", deref,
|
||||
select checked, deref, checked, "This null check is redundant because $@ in any case.", deref,
|
||||
"the value is dereferenced"
|
||||
|
||||
4
cpp/ql/src/change-notes/2023-04-11-double-free.md
Normal file
4
cpp/ql/src/change-notes/2023-04-11-double-free.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* A new query `cpp/double-free` has been added. The query finds possible cases of deallocating the same pointer twice. The precision of the query has been set to "medium".
|
||||
4
cpp/ql/src/change-notes/2023-04-11-use-after-free.md
Normal file
4
cpp/ql/src/change-notes/2023-04-11-use-after-free.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* The query `cpp/use-after-free` has been modernized and assigned the precision "medium". The query finds cases of where a pointer is dereferenced after its memory has been deallocated.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* The query `cpp/redundant-null-check-simple` has been promoted to Code Scanning. The query finds cases where a pointer is compared to null after it has already been dereferenced. Such comparisons likely indicate a bug at the place where the pointer is dereferenced, or where the pointer is compared to null.
|
||||
3
cpp/ql/src/change-notes/released/0.5.6.md
Normal file
3
cpp/ql/src/change-notes/released/0.5.6.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.6
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.5
|
||||
lastReleaseVersion: 0.5.6
|
||||
|
||||
@@ -25,16 +25,23 @@ Instruction getABoundIn(SemBound b, IRFunction func) {
|
||||
/**
|
||||
* Holds if `i <= b + delta`.
|
||||
*/
|
||||
bindingset[i]
|
||||
pragma[inline_late]
|
||||
predicate bounded(Instruction i, Instruction b, int delta) {
|
||||
pragma[inline]
|
||||
predicate boundedImpl(Instruction i, Instruction b, int delta) {
|
||||
exists(SemBound bound, IRFunction func |
|
||||
semBounded(getSemanticExpr(i), bound, delta, true, _) and
|
||||
b = getABoundIn(bound, func) and
|
||||
i.getEnclosingIRFunction() = func
|
||||
pragma[only_bind_out](i.getEnclosingIRFunction()) = func
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[i]
|
||||
pragma[inline_late]
|
||||
predicate bounded1(Instruction i, Instruction b, int delta) { boundedImpl(i, b, delta) }
|
||||
|
||||
bindingset[b]
|
||||
pragma[inline_late]
|
||||
predicate bounded2(Instruction i, Instruction b, int delta) { boundedImpl(i, b, delta) }
|
||||
|
||||
module FieldAddressToPointerArithmeticConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { isFieldAddressSource(_, source) }
|
||||
|
||||
@@ -50,23 +57,39 @@ predicate isFieldAddressSource(Field f, DataFlow::Node source) {
|
||||
source.asInstruction().(FieldAddressInstruction).getField() = f
|
||||
}
|
||||
|
||||
bindingset[delta]
|
||||
predicate isInvalidPointerDerefSinkImpl(
|
||||
int delta, Instruction i, AddressOperand addr, string operation
|
||||
) {
|
||||
delta >= 0 and
|
||||
i.getAnOperand() = addr and
|
||||
(
|
||||
i instanceof StoreInstruction and
|
||||
operation = "write"
|
||||
or
|
||||
i instanceof LoadInstruction and
|
||||
operation = "read"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a sink for `InvalidPointerToDerefConf` and `i` is a `StoreInstruction` that
|
||||
* writes to an address that non-strictly upper-bounds `sink`, or `i` is a `LoadInstruction` that
|
||||
* reads from an address that non-strictly upper-bounds `sink`.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string operation) {
|
||||
predicate isInvalidPointerDerefSink1(DataFlow::Node sink, Instruction i, string operation) {
|
||||
exists(AddressOperand addr, int delta |
|
||||
bounded(addr.getDef(), sink.asInstruction(), delta) and
|
||||
delta >= 0 and
|
||||
i.getAnOperand() = addr
|
||||
|
|
||||
i instanceof StoreInstruction and
|
||||
operation = "write"
|
||||
or
|
||||
i instanceof LoadInstruction and
|
||||
operation = "read"
|
||||
bounded1(addr.getDef(), sink.asInstruction(), delta) and
|
||||
isInvalidPointerDerefSinkImpl(delta, i, addr, operation)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate isInvalidPointerDerefSink2(DataFlow::Node sink, Instruction i, string operation) {
|
||||
exists(AddressOperand addr, int delta |
|
||||
bounded2(addr.getDef(), sink.asInstruction(), delta) and
|
||||
isInvalidPointerDerefSinkImpl(delta, i, addr, operation)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -90,7 +113,7 @@ module PointerArithmeticToDerefConfig implements DataFlow::ConfigSig {
|
||||
}
|
||||
|
||||
pragma[inline]
|
||||
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _) }
|
||||
predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink1(sink, _, _) }
|
||||
}
|
||||
|
||||
module PointerArithmeticToDerefFlow = DataFlow::Global<PointerArithmeticToDerefConfig>;
|
||||
@@ -100,7 +123,7 @@ from
|
||||
PointerArithmeticToDerefFlow::PathNode sink, Instruction deref, string operation, int delta
|
||||
where
|
||||
PointerArithmeticToDerefFlow::flowPath(source, sink) and
|
||||
isInvalidPointerDerefSink(sink.getNode(), deref, operation) and
|
||||
isInvalidPointerDerefSink2(sink.getNode(), deref, operation) and
|
||||
isConstantSizeOverflowSource(f, source.getNode().asInstruction(), delta)
|
||||
select source, source, sink,
|
||||
"This pointer arithmetic may have an off-by-" + (delta + 1) +
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @name Errors When Double Free
|
||||
* @description Freeing a previously allocated resource twice can lead to various vulnerabilities in the program.
|
||||
* @kind problem
|
||||
* @id cpp/double-free
|
||||
* @id cpp/experimental-double-free
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags security
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.5.6-dev
|
||||
version: 0.6.0-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -3,14 +3,14 @@ import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.ModulusAnaly
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeUtils
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysisSpecific
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysisRelativeSpecific
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysisImpl
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific
|
||||
import semmle.code.cpp.ir.IR as IR
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
module ModulusAnalysisInstantiated =
|
||||
ModulusAnalysis<FloatDelta, ConstantBounds, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
ModulusAnalysis<FloatDelta, ConstantBounds, RangeUtil<FloatDelta, CppLangImplRelative>>;
|
||||
|
||||
class ModulusAnalysisTest extends InlineExpectationsTest {
|
||||
ModulusAnalysisTest() { this = "ModulusAnalysisTest" }
|
||||
|
||||
23
cpp/ql/test/library-tests/ir/range-analysis/Overflow.ql
Normal file
23
cpp/ql/test/library-tests/ir/range-analysis/Overflow.ql
Normal file
@@ -0,0 +1,23 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.new.SimpleRangeAnalysis
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class RangeAnalysisTest extends InlineExpectationsTest {
|
||||
RangeAnalysisTest() { this = "RangeAnalysisTest" }
|
||||
|
||||
override string getARelevantTag() { result = "overflow" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Expr e |
|
||||
tag = "overflow" and
|
||||
element = e.toString() and
|
||||
location = e.getLocation() and
|
||||
value =
|
||||
strictconcat(string s |
|
||||
s = "+" and exprMightOverflowPositively(e)
|
||||
or
|
||||
s = "-" and exprMightOverflowNegatively(e)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ int test1(struct List* p) {
|
||||
int count = 0;
|
||||
for (; p; p = p->next) {
|
||||
count = count+1;
|
||||
range(count); // $ range===count:p+1 range=>=1
|
||||
range(count); // $ range===count:p+1
|
||||
}
|
||||
range(count); // $ range=>=0
|
||||
range(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -40,13 +40,13 @@ int test4() {
|
||||
int total = 0;
|
||||
for (i = 0; i < 2; i = i+1) {
|
||||
range(i); // $ range=<=1 range=>=0
|
||||
range(total); // $ range=>=0
|
||||
range(total);
|
||||
total += i;
|
||||
range(total); // $ range=<=i+1 range=<=i+1 range=>=0 range=>=i+0
|
||||
range(total); // $ range=<=i+1 range=<=i+1 MISSING: range=>=0 range=>=i+0
|
||||
}
|
||||
range(total); // $ range=>=0
|
||||
range(total); // $ MISSING: range=>=0
|
||||
range(i); // $ range===2
|
||||
range(total + i); // $ range===i+2 range=>=2 range=>=i+0
|
||||
range(total + i); // $ range=<=i+2 MISSING: range===i+2 range=>=2 range=>=i+0
|
||||
return total + i;
|
||||
}
|
||||
|
||||
@@ -55,13 +55,13 @@ int test5() {
|
||||
int total = 0;
|
||||
for (i = 0; i < 2; i++) {
|
||||
range(i); // $ range=<=1 range=>=0
|
||||
range(total); // $ range=>=0
|
||||
range(total); // $ MISSING: range=>=0
|
||||
total += i;
|
||||
range(total); // $ range=<=i+1 range=>=0 range=>=i+0
|
||||
range(total); // $ range=<=i+1 MISSING: range=>=0 range=>=i+0
|
||||
}
|
||||
range(total); // $ range=>=0
|
||||
range(total); // $ MISSING: range=>=0
|
||||
range(i); // $ range===2
|
||||
range(total + i); // $ range===i+2 range=>=2 range=>=i+0
|
||||
range(total + i); // $ range=<=i+2 MISSING: range===i+2 range=>=2 range=>=i+0
|
||||
return total + i;
|
||||
}
|
||||
|
||||
@@ -70,9 +70,9 @@ int test6() {
|
||||
int total = 0;
|
||||
for (i = 0; i+2 < 4; i = i+1) {
|
||||
range(i); // $ range=<=1 range=>=0
|
||||
range(total); // $ range=>=0
|
||||
range(total); // $ MISSING: range=>=0
|
||||
total += i;
|
||||
range(total); // $ range=<=i+1 range=>=0 range=>=i+0
|
||||
range(total); // $ range=<=i+1 MISSING: range=>=0 range=>=i+0
|
||||
}
|
||||
return total + i;
|
||||
}
|
||||
@@ -168,19 +168,19 @@ typedef unsigned long long size_type;
|
||||
|
||||
size_type test12_helper() {
|
||||
static size_type n = 0;
|
||||
return n++;
|
||||
return n++; // $ overflow=+
|
||||
}
|
||||
|
||||
int test12() {
|
||||
size_type Start = 0;
|
||||
while (Start <= test12_helper()-1)
|
||||
{
|
||||
range(Start);
|
||||
range(Start); // $ MISSING:range=>=0
|
||||
const size_type Length = test12_helper();
|
||||
Start += Length + 1;
|
||||
range(Start);
|
||||
Start += Length + 1; // $ overflow=+
|
||||
range(Start); // $ MISSING:range=>=1 MISSING:range=>=Start+1 MISSING:range=">=call to test12_helper+1"
|
||||
}
|
||||
range(Start);
|
||||
range(Start); // $ MISSING:range=>=0
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -190,13 +190,13 @@ int test13(char c, int i) {
|
||||
unsigned char uc = c;
|
||||
range(uc);
|
||||
unsigned int x = 0;
|
||||
unsigned int y = x-1;
|
||||
range(y); // $ range===-1
|
||||
int z = i+1;
|
||||
unsigned int y = x-1; // $ overflow=-
|
||||
range(y); // $ range===-1 overflow=-
|
||||
int z = i+1; // $ overflow=+
|
||||
range(z); // $ range===i+1
|
||||
range(c + i + uc + x + y + z);
|
||||
range((double)(c + i + uc + x + y + z));
|
||||
return (double)(c + i + uc + x + y + z);
|
||||
range(c + i + uc + x + y + z); // $ overflow=+- overflow=+ overflow=- MISSING: range=>=1
|
||||
range((double)(c + i + uc + x + y + z)); // $ overflow=+ overflow=+- overflow=- MISSING: range=>=1
|
||||
return (double)(c + i + uc + x + y + z); // $ overflow=+- overflow=+ overflow=-
|
||||
}
|
||||
|
||||
// Regression test for ODASA-6013.
|
||||
@@ -213,8 +213,8 @@ int test14(int x) {
|
||||
range(c0);
|
||||
unsigned short s0 = x;
|
||||
range(s0);
|
||||
range(x0 + x1 + x2 + x3 + c0 + s0);
|
||||
return x0 + x1 + x2 + x3 + c0 + s0;
|
||||
range(x0 + x1 + x2 + x3 + c0 + s0); // $ overflow=+ overflow=+-
|
||||
return x0 + x1 + x2 + x3 + c0 + s0; // $ overflow=+ overflow=+-
|
||||
}
|
||||
|
||||
long long test15(long long x) {
|
||||
@@ -243,7 +243,7 @@ int test_unary(int a) {
|
||||
range(b); // $ range=<=11 range=>=0
|
||||
int c = -a;
|
||||
range(c); // $ range=<=0 range=>=-11
|
||||
range(b+c); // $ range=<=11 range=>=-11
|
||||
range(b+c); // $ range=<=11 range=>=-11 MISSING:range=">=- ...+0"
|
||||
total += b+c;
|
||||
range(total); // $ range=<=0+11 range=<=19 range=>=0-11 range=>=-19
|
||||
}
|
||||
@@ -273,7 +273,7 @@ int test_unary(int a) {
|
||||
range(b); // $ range=<=0 range=>=-7
|
||||
int c = -a;
|
||||
range(c); // $ range=<=7 range=>=0
|
||||
range(b+c); // $ range=>=-7 range=<=7
|
||||
range(b+c); // $ range=>=-7 range=<=7 MISSING:range="<=- ...+0"
|
||||
total += b+c;
|
||||
range(total); // $ range="<=- ...+7" range="<=- ...+15" range="<=- ...+33" range=">=- ...-7" range=">=- ...-15" range=">=- ...-33" range=<=0+44 range=<=52 range=>=0-44 range=>=-52
|
||||
}
|
||||
@@ -315,9 +315,9 @@ int test_mult01(int a, int b) {
|
||||
if (3 <= a && a <= 11 && -13 <= b && b <= 23) {
|
||||
range(a); // $ range=<=11 range=>=3
|
||||
range(b); // $ range=<=23 range=>=-13
|
||||
int r = a*b; // -143 .. 253
|
||||
int r = a*b; // $ overflow=+- -143 .. 253
|
||||
range(r);
|
||||
total += r;
|
||||
total += r; // $ overflow=+
|
||||
range(total); // $ MISSING: range=">=... * ...+0"
|
||||
}
|
||||
if (3 <= a && a <= 11 && -13 <= b && b <= 0) {
|
||||
@@ -326,7 +326,7 @@ int test_mult01(int a, int b) {
|
||||
int r = a*b; // -143 .. 0
|
||||
range(r); // $ range=<=0 range=>=-143
|
||||
total += r;
|
||||
range(total); // $ range=<=3+0 range=>=3-143
|
||||
range(total); // $ range=>=3-143
|
||||
}
|
||||
if (3 <= a && a <= 11 && -13 <= b && b <= -7) {
|
||||
range(a); // $ range=<=11 range=>=3
|
||||
@@ -334,9 +334,9 @@ int test_mult01(int a, int b) {
|
||||
int r = a*b; // -143 .. -21
|
||||
range(r); // $ range=<=-21 range=>=-143
|
||||
total += r;
|
||||
range(total); // $ range=<=3-21 range=>=3-143 range=>=3-286
|
||||
range(total); // $ range=>=3-143 range=>=3-286
|
||||
}
|
||||
range(total); // $ range=<=3+0 range=>=3-143 range=>=3-286
|
||||
range(total); // $ range=>=3-143 range=>=3-286
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -363,9 +363,9 @@ int test_mult02(int a, int b) {
|
||||
if (0 <= a && a <= 11 && -13 <= b && b <= 23) {
|
||||
range(a); // $ range=<=11 range=>=0
|
||||
range(b); // $ range=<=23 range=>=-13
|
||||
int r = a*b; // -143 .. 253
|
||||
int r = a*b; // $ overflow=+- -143 .. 253
|
||||
range(r);
|
||||
total += r;
|
||||
total += r; // $ overflow=+
|
||||
range(total); // $ MISSING: range=">=... * ...+0"
|
||||
}
|
||||
if (0 <= a && a <= 11 && -13 <= b && b <= 0) {
|
||||
@@ -374,7 +374,7 @@ int test_mult02(int a, int b) {
|
||||
int r = a*b; // -143 .. 0
|
||||
range(r); // $ range=<=0 range=>=-143
|
||||
total += r;
|
||||
range(total); // $ range=<=0+0 range=>=0-143
|
||||
range(total); // $ range=>=0-143
|
||||
}
|
||||
if (0 <= a && a <= 11 && -13 <= b && b <= -7) {
|
||||
range(a); // $ range=<=11 range=>=0
|
||||
@@ -382,9 +382,9 @@ int test_mult02(int a, int b) {
|
||||
int r = a*b; // -143 .. 0
|
||||
range(r); // $ range=<=0 range=>=-143
|
||||
total += r;
|
||||
range(total); // $ range=<=0+0 range=>=0-143 range=>=0-286
|
||||
range(total); // $ range=>=0-143 range=>=0-286
|
||||
}
|
||||
range(total); // $ range=<=0+0 range=>=0-143 range=>=0-286
|
||||
range(total); // $range=>=0-143 range=>=0-286
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -395,7 +395,7 @@ int test_mult03(int a, int b) {
|
||||
if (-17 <= a && a <= 11 && 5 <= b && b <= 23) {
|
||||
range(a); // $ range=<=11 range=>=-17
|
||||
range(b); // $ range=<=23 range=>=5
|
||||
int r = a*b; // -391 .. 253
|
||||
int r = a*b; // $ overflow=+- -391 .. 253
|
||||
range(r);
|
||||
total += r;
|
||||
range(total);
|
||||
@@ -403,33 +403,33 @@ int test_mult03(int a, int b) {
|
||||
if (-17 <= a && a <= 11 && 0 <= b && b <= 23) {
|
||||
range(a); // $ range=<=11 range=>=-17
|
||||
range(b); // $ range=<=23 range=>=0
|
||||
int r = a*b; // -391 .. 253
|
||||
int r = a*b; // $ overflow=+- -391 .. 253
|
||||
range(r);
|
||||
total += r;
|
||||
total += r; // $ overflow=+-
|
||||
range(total);
|
||||
}
|
||||
if (-17 <= a && a <= 11 && -13 <= b && b <= 23) {
|
||||
range(a); // $ range=<=11 range=>=-17
|
||||
range(b); // $ range=<=23 range=>=-13
|
||||
int r = a*b; // -391 .. 253
|
||||
int r = a*b; // $ overflow=+- -391 .. 25
|
||||
range(r);
|
||||
total += r;
|
||||
total += r; // $ overflow=+-
|
||||
range(total);
|
||||
}
|
||||
if (-17 <= a && a <= 11 && -13 <= b && b <= 0) {
|
||||
range(a); // $ range=<=11 range=>=-17
|
||||
range(b); // $ range=<=0 range=>=-13
|
||||
int r = a*b; // -143 .. 221
|
||||
int r = a*b; // $ overflow=+- -143 .. 221
|
||||
range(r);
|
||||
total += r;
|
||||
total += r; // $ overflow=+-
|
||||
range(total);
|
||||
}
|
||||
if (-17 <= a && a <= 11 && -13 <= b && b <= -7) {
|
||||
range(a); // $ range=<=11 range=>=-17
|
||||
range(b); // $ range=<=-7 range=>=-13
|
||||
int r = a*b; // -143 .. 221
|
||||
int r = a*b; // $ overflow=+- -143 .. 221
|
||||
range(r);
|
||||
total += r;
|
||||
total += r; // $ overflow=+-
|
||||
range(total);
|
||||
}
|
||||
range(total);
|
||||
@@ -458,9 +458,9 @@ int test_mult04(int a, int b) {
|
||||
if (-17 <= a && a <= 0 && -13 <= b && b <= 23) {
|
||||
range(a); // $ range=<=0 range=>=-17
|
||||
range(b); // $ range=<=23 range=>=-13
|
||||
int r = a*b; // -391 .. 221
|
||||
int r = a*b; // $ overflow=+- -391 .. 221
|
||||
range(r);
|
||||
total += r;
|
||||
total += r; // $ overflow=-
|
||||
range(total); // $ MISSING: range="<=... * ...+0"
|
||||
}
|
||||
if (-17 <= a && a <= 0 && -13 <= b && b <= 0) {
|
||||
@@ -469,7 +469,7 @@ int test_mult04(int a, int b) {
|
||||
int r = a*b; // 0 .. 221
|
||||
range(r); // $ range=<=221 range=>=0
|
||||
total += r;
|
||||
range(total); // $ range="<=- ...+221" range=">=- ...+0"
|
||||
range(total); // $ range="<=- ...+221"
|
||||
}
|
||||
if (-17 <= a && a <= 0 && -13 <= b && b <= -7) {
|
||||
range(a); // $ range=<=0 range=>=-17
|
||||
@@ -477,9 +477,9 @@ int test_mult04(int a, int b) {
|
||||
int r = a*b; // 0 .. 221
|
||||
range(r); // $ range=<=221 range=>=0
|
||||
total += r;
|
||||
range(total); // $ range=">=- ...+0" range="<=- ...+221" range="<=- ...+442"
|
||||
range(total); // $ range="<=- ...+221" range="<=- ...+442"
|
||||
}
|
||||
range(total); // $ range=">=- ...+0" range="<=- ...+221" range="<=- ...+442"
|
||||
range(total); // $ range="<=- ...+221" range="<=- ...+442"
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -506,9 +506,9 @@ int test_mult05(int a, int b) {
|
||||
if (-17 <= a && a <= -2 && -13 <= b && b <= 23) {
|
||||
range(a); // $ range=<=-2 range=>=-17
|
||||
range(b); // $ range=<=23 range=>=-13
|
||||
int r = a*b; // -391 .. 221
|
||||
int r = a*b; // $ overflow=+- -391 .. 221
|
||||
range(r);
|
||||
total += r;
|
||||
total += r; // $ overflow=-
|
||||
range(total); // $ MISSING: range="<=... * ...+0"
|
||||
}
|
||||
if (-17 <= a && a <= -2 && -13 <= b && b <= 0) {
|
||||
@@ -517,7 +517,7 @@ int test_mult05(int a, int b) {
|
||||
int r = a*b; // 0 .. 221
|
||||
range(r); // $ range=<=221 range=>=0
|
||||
total += r;
|
||||
range(total); // $ range="<=- ...+221" range=">=- ...+0"
|
||||
range(total); // $ range="<=- ...+221"
|
||||
}
|
||||
if (-17 <= a && a <= -2 && -13 <= b && b <= -7) {
|
||||
range(a); // $ range=<=-2 range=>=-17
|
||||
@@ -525,9 +525,9 @@ int test_mult05(int a, int b) {
|
||||
int r = a*b; // 14 .. 221
|
||||
range(r); // $ range=<=221 range=>=14
|
||||
total += r;
|
||||
range(total); // $ range="<=- ...+221" range="<=- ...+442" range=">=- ...+14"
|
||||
range(total); // $ range="<=- ...+221" range="<=- ...+442"
|
||||
}
|
||||
range(total); // $ range=">=- ...+0" range="<=- ...+221" range="<=- ...+442"
|
||||
range(total); // $ range="<=- ...+221" range="<=- ...+442"
|
||||
return total;
|
||||
}
|
||||
|
||||
@@ -541,7 +541,7 @@ int test16(int x) {
|
||||
while (i < 3) {
|
||||
range(i); // $ range=<=2 range=>=0
|
||||
i++;
|
||||
range(i); // $ range="==... = ...:i+1" range=<=3 range=>=1
|
||||
range(i); // $ range=<=3 range=>=1 range="==... = ...:i+1" SPURIOUS:range="==... = ...:i+1"
|
||||
}
|
||||
range(d);
|
||||
d = i;
|
||||
@@ -586,7 +586,7 @@ unsigned int test_ternary01(unsigned int x) {
|
||||
(range(x), 500);
|
||||
range(y4); // $ range=<=500
|
||||
y5 = (x+1) ?:
|
||||
(range(x), 500); // $ range===-1
|
||||
(range(x), 500); // $ overflow=- range===-1
|
||||
range(y5); // $ range=<=500
|
||||
y6 = ((unsigned char)(x+1)) ?:
|
||||
(range(x), 5); // $ range=<=299
|
||||
@@ -598,8 +598,8 @@ unsigned int test_ternary01(unsigned int x) {
|
||||
(range(x), 500); // $ range=<=299
|
||||
range(y8); // y8 <= 300
|
||||
}
|
||||
range(y1 + y2 + y3 + y4 + y5 + y6 + y7 + y8); // $ MISSING: range=">=... = ...:... ? ... : ...+0" range=">=call to range+0"
|
||||
return y1 + y2 + y3 + y4 + y5 + y6 + y7 + y8;
|
||||
range(y1 + y2 + y3 + y4 + y5 + y6 + y7 + y8); // $ overflow=+ MISSING: range=">=... = ...:... ? ... : ...+0" range=">=call to range+0"
|
||||
return y1 + y2 + y3 + y4 + y5 + y6 + y7 + y8; // $ overflow=+
|
||||
}
|
||||
|
||||
// Test ternary expression lower bounds.
|
||||
@@ -628,8 +628,8 @@ unsigned int test_ternary02(unsigned int x) {
|
||||
(range(x), 5); // $ range=>=300
|
||||
range(y5); // y6 >= 0
|
||||
}
|
||||
range(y1 + y2 + y3 + y4 + y5); // $ range=">=call to range+207" MISSING: range=">=... = ...:... ? ... : ...+0" range=">=call to range+0"
|
||||
return y1 + y2 + y3 + y4 + y5;
|
||||
range(y1 + y2 + y3 + y4 + y5); // $ overflow=+ MISSING: range=">=call to range+207" range=">=... = ...:... ? ... : ...+0" range=">=call to range+0"
|
||||
return y1 + y2 + y3 + y4 + y5; // $ overflow=+
|
||||
}
|
||||
|
||||
// Test the comma expression.
|
||||
@@ -691,9 +691,9 @@ int test_unsigned_mult01(unsigned int a, unsigned b) {
|
||||
range(a); // $ range=<=11 range=>=3
|
||||
range(b); // $ range=<=23 range=>=0
|
||||
int r = a*b; // 0 .. 253
|
||||
range(r); // $ range=>=0 range=<=253
|
||||
range(r);// $ range=>=0 range=<=253
|
||||
total += r;
|
||||
range(total); // $ range=>=0 range=<=506 range=">=(unsigned int)...+0" range="<=(unsigned int)...+253"
|
||||
range(total); // $ range=">=(unsigned int)...+0" range=>=0 range=<=506 range="<=(unsigned int)...+253"
|
||||
}
|
||||
if (3 <= a && a <= 11 && 13 <= b && b <= 23) {
|
||||
range(a); // $ range=<=11 range=>=3
|
||||
@@ -701,7 +701,7 @@ int test_unsigned_mult01(unsigned int a, unsigned b) {
|
||||
int r = a*b; // 39 .. 253
|
||||
range(r); // $ range=>=39 range=<=253
|
||||
total += r;
|
||||
range(total); // $ range=>=39 range=<=759 range=">=(unsigned int)...+39" range="<=(unsigned int)...+506" range="<=(unsigned int)...+253"
|
||||
range(total); // $ range=>=39 range=<=759 range="<=(unsigned int)...+253" range="<=(unsigned int)...+506" range=">=(unsigned int)...+39"
|
||||
}
|
||||
range(total); // $ range=>=0 range=<=759 range=">=(unsigned int)...+0" range="<=(unsigned int)...+506" range="<=(unsigned int)...+253"
|
||||
return total;
|
||||
@@ -722,14 +722,14 @@ int test_unsigned_mult02(unsigned b) {
|
||||
int r = 11*b; // 0 .. 253
|
||||
range(r); // $ range=>=0 range=<=253
|
||||
total += r;
|
||||
range(total); // $ range=>=0 range=<=506 range=">=(unsigned int)...+0" range="<=(unsigned int)...+253"
|
||||
range(total); // $ range=">=(unsigned int)...+0" range=>=0 range="<=(unsigned int)...+253" range=<=506
|
||||
}
|
||||
if (13 <= b && b <= 23) {
|
||||
range(b); // $ range=<=23 range=>=13
|
||||
int r = 11*b; // 143 .. 253
|
||||
range(r); // $ range=>=143 range=<=253
|
||||
total += r;
|
||||
range(total); // $ range=>=143 range=<=759 range=">=(unsigned int)...+143" range="<=(unsigned int)...+506" range="<=(unsigned int)...+253"
|
||||
range(total); // $ range="<=(unsigned int)...+253" range="<=(unsigned int)...+506" range=">=(unsigned int)...+143" range=>=143 range=<=759
|
||||
}
|
||||
range(total); // $ range=>=0 range=<=759 range=">=(unsigned int)...+0" range="<=(unsigned int)...+506" range="<=(unsigned int)...+253"
|
||||
return total;
|
||||
@@ -751,7 +751,7 @@ unsigned long mult_overflow() {
|
||||
range(x); // $ range===274177
|
||||
y = 67280421310721UL;
|
||||
range(y);
|
||||
xy = x * y;
|
||||
xy = x * y; // $ overflow=+-
|
||||
range(xy);
|
||||
return xy; // BUG: upper bound should be >= 18446744073709551617UL
|
||||
}
|
||||
@@ -760,14 +760,14 @@ unsigned long mult_lower_bound(unsigned int ui, unsigned long ul) {
|
||||
if (ui >= 10) {
|
||||
range(ui); // $ range=>=10
|
||||
range((unsigned long)ui); // $ range=>=10
|
||||
unsigned long result = (unsigned long)ui * ui;
|
||||
range(result); // $ range=>=100 range=>=100
|
||||
unsigned long result = (unsigned long)ui * ui; // $ overflow=+
|
||||
range(result); // $ MISSING: range=>=100
|
||||
return result; // BUG: upper bound should be >= 18446744065119617025
|
||||
}
|
||||
if (ul >= 10) {
|
||||
range(ul); // $ range=>=10
|
||||
unsigned long result = ul * ul;
|
||||
range(result); // $ range=>=100
|
||||
unsigned long result = ul * ul; // $ overflow=+
|
||||
range(result); // $ MISSING: range=>=100
|
||||
return result; // BUG: lower bound should be 0 (overflow is possible)
|
||||
}
|
||||
return 0;
|
||||
@@ -800,13 +800,13 @@ int mul_by_constant(int i, int j) {
|
||||
i = 5 * i;
|
||||
range(i); // $ range=<=10 range=>=-5
|
||||
|
||||
i = i * -3;
|
||||
i = i * -3; // $ overflow=+-
|
||||
range(i); // -30 .. 15
|
||||
|
||||
i *= 7;
|
||||
i *= 7; // $ overflow=+-
|
||||
range(i); // -210 .. 105
|
||||
|
||||
i *= -11;
|
||||
i *= -11; // $ overflow=+-
|
||||
range(i); // -1155 .. 2310
|
||||
}
|
||||
if (i == -1) {
|
||||
@@ -815,7 +815,7 @@ int mul_by_constant(int i, int j) {
|
||||
i = i * (int)0xffFFffFF; // fully converted literal is -1
|
||||
range(i); // $ range===1
|
||||
}
|
||||
i = i * -1;
|
||||
i = i * -1; // $ overflow=+-
|
||||
range( i); // -2^31 .. 2^31-1
|
||||
|
||||
signed char sc = 1;
|
||||
@@ -873,11 +873,11 @@ void notequal_refinement(short n) {
|
||||
}
|
||||
|
||||
while (n != 0) {
|
||||
range(n); // $ range=<=n+0
|
||||
range(n); // $ MISSING:range=<=n+0
|
||||
n--; // 1 ..
|
||||
}
|
||||
|
||||
range(n); // $ range=<=n+0 // 0 .. 0
|
||||
range(n); // $ MISSING:range=<=n+0 // 0 .. 0
|
||||
}
|
||||
|
||||
void notequal_variations(short n, float f) {
|
||||
@@ -888,7 +888,7 @@ void notequal_variations(short n, float f) {
|
||||
}
|
||||
|
||||
if (n >= 5) {
|
||||
if (2 * n - 10 == 0) { // Same as `n == 10/2` (modulo overflow)
|
||||
if (2 * n - 10 == 0) { // $ overflow=+
|
||||
range(n); // $ range=>=5 MISSING: range===5
|
||||
return;
|
||||
}
|
||||
@@ -921,7 +921,7 @@ void two_bounds_from_one_test(short ss, unsigned short us) {
|
||||
}
|
||||
|
||||
if (ss < 0x8001) { // Lower bound removed in `getDefLowerBounds`
|
||||
range(ss); // $ range=<=32768 MISSING: range=>=-32768
|
||||
range(ss); // $ overflow=+ range=<=32768 MISSING: range=>=-32768
|
||||
}
|
||||
|
||||
if ((short)us >= 0) {
|
||||
@@ -936,7 +936,7 @@ void two_bounds_from_one_test(short ss, unsigned short us) {
|
||||
range(ss); // -32768 .. 32767
|
||||
}
|
||||
|
||||
if (ss + 1 < sizeof(int)) {
|
||||
if (ss + 1 < sizeof(int)) { // $ overflow=+
|
||||
range(ss); // -1 .. 2
|
||||
}
|
||||
}
|
||||
@@ -1020,7 +1020,7 @@ void test_overflow() {
|
||||
|
||||
void test_negate_unsigned(unsigned u) {
|
||||
if(10 < u && u < 20) {
|
||||
range<unsigned>(-u); // underflows
|
||||
range<unsigned>(-u); // $ overflow=-
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
return x;
|
||||
}
|
||||
|
||||
if (y - 2 == x && y > 300) {
|
||||
if (y - 2 == x && y > 300) { // $ overflow=-
|
||||
range(x + y); // $ range=<=802 range=>=600
|
||||
return x + y;
|
||||
}
|
||||
|
||||
if (x != y + 1) {
|
||||
if (x != y + 1) { // $ overflow=+
|
||||
range(x); // $ range=<=400
|
||||
int sum = x + y;
|
||||
int sum = x + y; // $ overflow=+-
|
||||
} else {
|
||||
if (y > 300) {
|
||||
range(x); // $ range=>=302 range=<=400 range===y+1
|
||||
range(x); // $ range=>=302 range=<=400 range=<=y+1 MISSING: range===y+1
|
||||
range(y); // $ range=>=301 range=<=399 range===x-1
|
||||
int sum = x + y;
|
||||
}
|
||||
@@ -38,10 +38,10 @@
|
||||
return x;
|
||||
}
|
||||
|
||||
if (y == x - 1 && y > 300 && y + 2 == z && z == 350) {
|
||||
if (y == x - 1 && y > 300 && y + 2 == z && z == 350) { // $ overflow=+ overflow=-
|
||||
range(x); // $ range===349 range===y+1 range===z-1
|
||||
range(y); // $ range===348 range===x-1 range===z-2
|
||||
range(z); // $ range===350 range===x+1 range===y+2
|
||||
range(y); // $ range===348 range=>=x-1 range===z-2 MISSING: range===x-1
|
||||
range(z); // $ range===350 range=<=y+2 MISSING: range===x+1 range===y+2
|
||||
return x + y + z;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@ import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.SignAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.Semantic
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeUtils
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.FloatDelta
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysisSpecific
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysisRelativeSpecific
|
||||
import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific
|
||||
import semmle.code.cpp.ir.IR as IR
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
module SignAnalysisInstantiated = SignAnalysis<FloatDelta, RangeUtil<FloatDelta, CppLangImpl>>;
|
||||
module SignAnalysisInstantiated =
|
||||
SignAnalysis<FloatDelta, RangeUtil<FloatDelta, CppLangImplRelative>>;
|
||||
|
||||
class SignAnalysisTest extends InlineExpectationsTest {
|
||||
SignAnalysisTest() { this = "SignAnalysisTest" }
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
edges
|
||||
| test_free.cpp:11:10:11:10 | a | test_free.cpp:14:10:14:10 | a |
|
||||
| test_free.cpp:11:10:11:10 | a | test_free.cpp:14:10:14:10 | a |
|
||||
| test_free.cpp:11:10:11:10 | a | test_free.cpp:14:10:14:10 | a |
|
||||
| test_free.cpp:11:10:11:10 | a | test_free.cpp:14:10:14:10 | a |
|
||||
| test_free.cpp:30:10:30:10 | a | test_free.cpp:31:27:31:27 | a |
|
||||
| test_free.cpp:35:10:35:10 | a | test_free.cpp:37:27:37:27 | a |
|
||||
| test_free.cpp:42:27:42:27 | a | test_free.cpp:46:10:46:10 | a |
|
||||
| test_free.cpp:42:27:42:27 | a | test_free.cpp:46:10:46:10 | a |
|
||||
| test_free.cpp:42:27:42:27 | a | test_free.cpp:46:10:46:10 | a |
|
||||
| test_free.cpp:42:27:42:27 | a | test_free.cpp:46:10:46:10 | a |
|
||||
| test_free.cpp:44:27:44:27 | a | test_free.cpp:46:10:46:10 | a |
|
||||
| test_free.cpp:44:27:44:27 | a | test_free.cpp:46:10:46:10 | a |
|
||||
| test_free.cpp:44:27:44:27 | a | test_free.cpp:46:10:46:10 | a |
|
||||
| test_free.cpp:44:27:44:27 | a | test_free.cpp:46:10:46:10 | a |
|
||||
| test_free.cpp:50:27:50:27 | a | test_free.cpp:51:10:51:10 | a |
|
||||
| test_free.cpp:69:10:69:10 | a | test_free.cpp:72:14:72:14 | a |
|
||||
| test_free.cpp:69:10:69:10 | a | test_free.cpp:72:14:72:14 | a |
|
||||
| test_free.cpp:69:10:69:10 | a | test_free.cpp:72:14:72:14 | a |
|
||||
| test_free.cpp:69:10:69:10 | a | test_free.cpp:72:14:72:14 | a |
|
||||
| test_free.cpp:101:10:101:10 | a | test_free.cpp:103:10:103:10 | a |
|
||||
| test_free.cpp:128:10:128:11 | * ... | test_free.cpp:129:10:129:11 | * ... |
|
||||
| test_free.cpp:152:27:152:27 | a | test_free.cpp:154:10:154:10 | a |
|
||||
| test_free.cpp:152:27:152:27 | a | test_free.cpp:154:10:154:10 | a |
|
||||
| test_free.cpp:152:27:152:27 | a | test_free.cpp:154:10:154:10 | a |
|
||||
| test_free.cpp:152:27:152:27 | a | test_free.cpp:154:10:154:10 | a |
|
||||
| test_free.cpp:207:10:207:10 | a | test_free.cpp:209:10:209:10 | a |
|
||||
| test_free.cpp:207:10:207:10 | a | test_free.cpp:209:10:209:10 | a |
|
||||
| test_free.cpp:207:10:207:10 | a | test_free.cpp:209:10:209:10 | a |
|
||||
| test_free.cpp:207:10:207:10 | a | test_free.cpp:209:10:209:10 | a |
|
||||
nodes
|
||||
| test_free.cpp:11:10:11:10 | a | semmle.label | a |
|
||||
| test_free.cpp:11:10:11:10 | a | semmle.label | a |
|
||||
| test_free.cpp:14:10:14:10 | a | semmle.label | a |
|
||||
| test_free.cpp:14:10:14:10 | a | semmle.label | a |
|
||||
| test_free.cpp:30:10:30:10 | a | semmle.label | a |
|
||||
| test_free.cpp:31:27:31:27 | a | semmle.label | a |
|
||||
| test_free.cpp:35:10:35:10 | a | semmle.label | a |
|
||||
| test_free.cpp:37:27:37:27 | a | semmle.label | a |
|
||||
| test_free.cpp:42:27:42:27 | a | semmle.label | a |
|
||||
| test_free.cpp:42:27:42:27 | a | semmle.label | a |
|
||||
| test_free.cpp:44:27:44:27 | a | semmle.label | a |
|
||||
| test_free.cpp:44:27:44:27 | a | semmle.label | a |
|
||||
| test_free.cpp:46:10:46:10 | a | semmle.label | a |
|
||||
| test_free.cpp:46:10:46:10 | a | semmle.label | a |
|
||||
| test_free.cpp:46:10:46:10 | a | semmle.label | a |
|
||||
| test_free.cpp:46:10:46:10 | a | semmle.label | a |
|
||||
| test_free.cpp:50:27:50:27 | a | semmle.label | a |
|
||||
| test_free.cpp:51:10:51:10 | a | semmle.label | a |
|
||||
| test_free.cpp:69:10:69:10 | a | semmle.label | a |
|
||||
| test_free.cpp:69:10:69:10 | a | semmle.label | a |
|
||||
| test_free.cpp:72:14:72:14 | a | semmle.label | a |
|
||||
| test_free.cpp:72:14:72:14 | a | semmle.label | a |
|
||||
| test_free.cpp:101:10:101:10 | a | semmle.label | a |
|
||||
| test_free.cpp:103:10:103:10 | a | semmle.label | a |
|
||||
| test_free.cpp:128:10:128:11 | * ... | semmle.label | * ... |
|
||||
| test_free.cpp:129:10:129:11 | * ... | semmle.label | * ... |
|
||||
| test_free.cpp:152:27:152:27 | a | semmle.label | a |
|
||||
| test_free.cpp:152:27:152:27 | a | semmle.label | a |
|
||||
| test_free.cpp:154:10:154:10 | a | semmle.label | a |
|
||||
| test_free.cpp:154:10:154:10 | a | semmle.label | a |
|
||||
| test_free.cpp:207:10:207:10 | a | semmle.label | a |
|
||||
| test_free.cpp:207:10:207:10 | a | semmle.label | a |
|
||||
| test_free.cpp:209:10:209:10 | a | semmle.label | a |
|
||||
| test_free.cpp:209:10:209:10 | a | semmle.label | a |
|
||||
subpaths
|
||||
#select
|
||||
| test_free.cpp:14:10:14:10 | a | test_free.cpp:11:10:11:10 | a | test_free.cpp:14:10:14:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:11:5:11:8 | call to free | call to free |
|
||||
| test_free.cpp:14:10:14:10 | a | test_free.cpp:11:10:11:10 | a | test_free.cpp:14:10:14:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:11:5:11:8 | call to free | call to free |
|
||||
| test_free.cpp:14:10:14:10 | a | test_free.cpp:11:10:11:10 | a | test_free.cpp:14:10:14:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:11:5:11:8 | call to free | call to free |
|
||||
| test_free.cpp:14:10:14:10 | a | test_free.cpp:11:10:11:10 | a | test_free.cpp:14:10:14:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:11:5:11:8 | call to free | call to free |
|
||||
| test_free.cpp:31:27:31:27 | a | test_free.cpp:30:10:30:10 | a | test_free.cpp:31:27:31:27 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:30:5:30:8 | call to free | call to free |
|
||||
| test_free.cpp:37:27:37:27 | a | test_free.cpp:35:10:35:10 | a | test_free.cpp:37:27:37:27 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:35:5:35:8 | call to free | call to free |
|
||||
| test_free.cpp:46:10:46:10 | a | test_free.cpp:42:27:42:27 | a | test_free.cpp:46:10:46:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:42:22:42:25 | call to free | call to free |
|
||||
| test_free.cpp:46:10:46:10 | a | test_free.cpp:42:27:42:27 | a | test_free.cpp:46:10:46:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:42:22:42:25 | call to free | call to free |
|
||||
| test_free.cpp:46:10:46:10 | a | test_free.cpp:42:27:42:27 | a | test_free.cpp:46:10:46:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:42:22:42:25 | call to free | call to free |
|
||||
| test_free.cpp:46:10:46:10 | a | test_free.cpp:42:27:42:27 | a | test_free.cpp:46:10:46:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:42:22:42:25 | call to free | call to free |
|
||||
| test_free.cpp:46:10:46:10 | a | test_free.cpp:44:27:44:27 | a | test_free.cpp:46:10:46:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:44:22:44:25 | call to free | call to free |
|
||||
| test_free.cpp:46:10:46:10 | a | test_free.cpp:44:27:44:27 | a | test_free.cpp:46:10:46:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:44:22:44:25 | call to free | call to free |
|
||||
| test_free.cpp:46:10:46:10 | a | test_free.cpp:44:27:44:27 | a | test_free.cpp:46:10:46:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:44:22:44:25 | call to free | call to free |
|
||||
| test_free.cpp:46:10:46:10 | a | test_free.cpp:44:27:44:27 | a | test_free.cpp:46:10:46:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:44:22:44:25 | call to free | call to free |
|
||||
| test_free.cpp:51:10:51:10 | a | test_free.cpp:50:27:50:27 | a | test_free.cpp:51:10:51:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:50:22:50:25 | call to free | call to free |
|
||||
| test_free.cpp:72:14:72:14 | a | test_free.cpp:69:10:69:10 | a | test_free.cpp:72:14:72:14 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:69:5:69:8 | call to free | call to free |
|
||||
| test_free.cpp:72:14:72:14 | a | test_free.cpp:69:10:69:10 | a | test_free.cpp:72:14:72:14 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:69:5:69:8 | call to free | call to free |
|
||||
| test_free.cpp:72:14:72:14 | a | test_free.cpp:69:10:69:10 | a | test_free.cpp:72:14:72:14 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:69:5:69:8 | call to free | call to free |
|
||||
| test_free.cpp:72:14:72:14 | a | test_free.cpp:69:10:69:10 | a | test_free.cpp:72:14:72:14 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:69:5:69:8 | call to free | call to free |
|
||||
| test_free.cpp:103:10:103:10 | a | test_free.cpp:101:10:101:10 | a | test_free.cpp:103:10:103:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:101:5:101:8 | call to free | call to free |
|
||||
| test_free.cpp:129:10:129:11 | * ... | test_free.cpp:128:10:128:11 | * ... | test_free.cpp:129:10:129:11 | * ... | Memory pointed to by '* ...' may already have been freed by $@. | test_free.cpp:128:5:128:8 | call to free | call to free |
|
||||
| test_free.cpp:154:10:154:10 | a | test_free.cpp:152:27:152:27 | a | test_free.cpp:154:10:154:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:152:22:152:25 | call to free | call to free |
|
||||
| test_free.cpp:154:10:154:10 | a | test_free.cpp:152:27:152:27 | a | test_free.cpp:154:10:154:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:152:22:152:25 | call to free | call to free |
|
||||
| test_free.cpp:154:10:154:10 | a | test_free.cpp:152:27:152:27 | a | test_free.cpp:154:10:154:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:152:22:152:25 | call to free | call to free |
|
||||
| test_free.cpp:154:10:154:10 | a | test_free.cpp:152:27:152:27 | a | test_free.cpp:154:10:154:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:152:22:152:25 | call to free | call to free |
|
||||
| test_free.cpp:209:10:209:10 | a | test_free.cpp:207:10:207:10 | a | test_free.cpp:209:10:209:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:207:5:207:8 | call to free | call to free |
|
||||
| test_free.cpp:209:10:209:10 | a | test_free.cpp:207:10:207:10 | a | test_free.cpp:209:10:209:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:207:5:207:8 | call to free | call to free |
|
||||
| test_free.cpp:209:10:209:10 | a | test_free.cpp:207:10:207:10 | a | test_free.cpp:209:10:209:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:207:5:207:8 | call to free | call to free |
|
||||
| test_free.cpp:209:10:209:10 | a | test_free.cpp:207:10:207:10 | a | test_free.cpp:209:10:209:10 | a | Memory pointed to by 'a' may already have been freed by $@. | test_free.cpp:207:5:207:8 | call to free | call to free |
|
||||
@@ -0,0 +1 @@
|
||||
Critical/DoubleFree.ql
|
||||
@@ -26,6 +26,69 @@
|
||||
| test.cpp:128:15:128:16 | v4 |
|
||||
| test.cpp:185:10:185:12 | cpy |
|
||||
| test.cpp:199:10:199:12 | cpy |
|
||||
| test_free.cpp:11:10:11:10 | a |
|
||||
| test_free.cpp:14:10:14:10 | a |
|
||||
| test_free.cpp:16:10:16:10 | a |
|
||||
| test_free.cpp:18:18:18:18 | a |
|
||||
| test_free.cpp:23:10:23:10 | a |
|
||||
| test_free.cpp:25:10:25:10 | a |
|
||||
| test_free.cpp:26:10:26:10 | b |
|
||||
| test_free.cpp:30:10:30:10 | a |
|
||||
| test_free.cpp:31:27:31:27 | a |
|
||||
| test_free.cpp:35:10:35:10 | a |
|
||||
| test_free.cpp:37:27:37:27 | a |
|
||||
| test_free.cpp:42:27:42:27 | a |
|
||||
| test_free.cpp:44:27:44:27 | a |
|
||||
| test_free.cpp:46:10:46:10 | a |
|
||||
| test_free.cpp:50:27:50:27 | a |
|
||||
| test_free.cpp:51:10:51:10 | a |
|
||||
| test_free.cpp:55:27:55:27 | a |
|
||||
| test_free.cpp:57:10:57:10 | a |
|
||||
| test_free.cpp:61:10:61:10 | a |
|
||||
| test_free.cpp:63:10:63:10 | b |
|
||||
| test_free.cpp:69:10:69:10 | a |
|
||||
| test_free.cpp:72:14:72:14 | a |
|
||||
| test_free.cpp:83:12:83:12 | a |
|
||||
| test_free.cpp:85:12:85:12 | a |
|
||||
| test_free.cpp:90:10:90:10 | a |
|
||||
| test_free.cpp:95:10:95:10 | a |
|
||||
| test_free.cpp:101:10:101:10 | a |
|
||||
| test_free.cpp:102:23:102:23 | a |
|
||||
| test_free.cpp:103:10:103:10 | a |
|
||||
| test_free.cpp:104:10:104:10 | b |
|
||||
| test_free.cpp:107:23:107:23 | a |
|
||||
| test_free.cpp:112:14:112:14 | a |
|
||||
| test_free.cpp:114:10:114:10 | b |
|
||||
| test_free.cpp:118:23:118:23 | a |
|
||||
| test_free.cpp:119:17:119:17 | b |
|
||||
| test_free.cpp:121:14:121:14 | a |
|
||||
| test_free.cpp:126:10:126:11 | * ... |
|
||||
| test_free.cpp:128:10:128:11 | * ... |
|
||||
| test_free.cpp:129:10:129:11 | * ... |
|
||||
| test_free.cpp:131:10:131:13 | access to array |
|
||||
| test_free.cpp:132:10:132:13 | access to array |
|
||||
| test_free.cpp:143:27:143:30 | data |
|
||||
| test_free.cpp:145:14:145:22 | * ... |
|
||||
| test_free.cpp:148:10:148:17 | list_ptr |
|
||||
| test_free.cpp:152:27:152:27 | a |
|
||||
| test_free.cpp:154:10:154:10 | a |
|
||||
| test_free.cpp:159:14:159:15 | * ... |
|
||||
| test_free.cpp:162:10:162:10 | a |
|
||||
| test_free.cpp:167:23:167:23 | a |
|
||||
| test_free.cpp:173:10:173:10 | a |
|
||||
| test_free.cpp:181:10:181:10 | a |
|
||||
| test_free.cpp:183:10:183:10 | a |
|
||||
| test_free.cpp:185:10:185:10 | a |
|
||||
| test_free.cpp:188:10:188:10 | a |
|
||||
| test_free.cpp:193:20:193:20 | a |
|
||||
| test_free.cpp:199:20:199:20 | a |
|
||||
| test_free.cpp:205:10:205:10 | a |
|
||||
| test_free.cpp:207:10:207:10 | a |
|
||||
| test_free.cpp:209:10:209:10 | a |
|
||||
| test_free.cpp:213:10:213:10 | a |
|
||||
| test_free.cpp:216:10:216:10 | a |
|
||||
| test_free.cpp:220:10:220:10 | a |
|
||||
| test_free.cpp:227:24:227:45 | memory_descriptor_list |
|
||||
| virtual.cpp:18:10:18:10 | a |
|
||||
| virtual.cpp:19:10:19:10 | c |
|
||||
| virtual.cpp:38:10:38:10 | b |
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
| test_free.cpp:36:22:36:35 | ... = ... | This memory allocation may not be released at $@. | test_free.cpp:38:1:38:1 | return ... | this exit point |
|
||||
|
||||
@@ -11,3 +11,4 @@
|
||||
| test.cpp:156:3:156:26 | new | This memory is never freed. |
|
||||
| test.cpp:157:3:157:26 | new[] | This memory is never freed. |
|
||||
| test.cpp:169:14:169:19 | call to strdup | This memory is never freed. |
|
||||
| test_free.cpp:167:15:167:21 | call to realloc | This memory is never freed. |
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
edges
|
||||
| test_free.cpp:11:10:11:10 | a | test_free.cpp:12:5:12:5 | a |
|
||||
| test_free.cpp:11:10:11:10 | a | test_free.cpp:12:5:12:5 | a |
|
||||
| test_free.cpp:11:10:11:10 | a | test_free.cpp:13:6:13:6 | a |
|
||||
| test_free.cpp:11:10:11:10 | a | test_free.cpp:13:6:13:6 | a |
|
||||
| test_free.cpp:42:27:42:27 | a | test_free.cpp:45:5:45:5 | a |
|
||||
| test_free.cpp:42:27:42:27 | a | test_free.cpp:45:5:45:5 | a |
|
||||
| test_free.cpp:44:27:44:27 | a | test_free.cpp:45:5:45:5 | a |
|
||||
| test_free.cpp:44:27:44:27 | a | test_free.cpp:45:5:45:5 | a |
|
||||
| test_free.cpp:69:10:69:10 | a | test_free.cpp:71:9:71:9 | a |
|
||||
| test_free.cpp:69:10:69:10 | a | test_free.cpp:71:9:71:9 | a |
|
||||
| test_free.cpp:90:10:90:10 | a | test_free.cpp:91:5:91:5 | a |
|
||||
| test_free.cpp:90:10:90:10 | a | test_free.cpp:91:5:91:5 | a |
|
||||
| test_free.cpp:95:10:95:10 | a | test_free.cpp:96:9:96:9 | a |
|
||||
| test_free.cpp:101:10:101:10 | a | test_free.cpp:102:23:102:23 | a |
|
||||
| test_free.cpp:152:27:152:27 | a | test_free.cpp:153:5:153:5 | a |
|
||||
| test_free.cpp:152:27:152:27 | a | test_free.cpp:153:5:153:5 | a |
|
||||
nodes
|
||||
| test_free.cpp:11:10:11:10 | a | semmle.label | a |
|
||||
| test_free.cpp:11:10:11:10 | a | semmle.label | a |
|
||||
| test_free.cpp:12:5:12:5 | a | semmle.label | a |
|
||||
| test_free.cpp:13:6:13:6 | a | semmle.label | a |
|
||||
| test_free.cpp:42:27:42:27 | a | semmle.label | a |
|
||||
| test_free.cpp:42:27:42:27 | a | semmle.label | a |
|
||||
| test_free.cpp:44:27:44:27 | a | semmle.label | a |
|
||||
| test_free.cpp:44:27:44:27 | a | semmle.label | a |
|
||||
| test_free.cpp:45:5:45:5 | a | semmle.label | a |
|
||||
| test_free.cpp:45:5:45:5 | a | semmle.label | a |
|
||||
| test_free.cpp:69:10:69:10 | a | semmle.label | a |
|
||||
| test_free.cpp:69:10:69:10 | a | semmle.label | a |
|
||||
| test_free.cpp:71:9:71:9 | a | semmle.label | a |
|
||||
| test_free.cpp:90:10:90:10 | a | semmle.label | a |
|
||||
| test_free.cpp:90:10:90:10 | a | semmle.label | a |
|
||||
| test_free.cpp:91:5:91:5 | a | semmle.label | a |
|
||||
| test_free.cpp:95:10:95:10 | a | semmle.label | a |
|
||||
| test_free.cpp:96:9:96:9 | a | semmle.label | a |
|
||||
| test_free.cpp:101:10:101:10 | a | semmle.label | a |
|
||||
| test_free.cpp:102:23:102:23 | a | semmle.label | a |
|
||||
| test_free.cpp:152:27:152:27 | a | semmle.label | a |
|
||||
| test_free.cpp:152:27:152:27 | a | semmle.label | a |
|
||||
| test_free.cpp:153:5:153:5 | a | semmle.label | a |
|
||||
subpaths
|
||||
#select
|
||||
| test_free.cpp:12:5:12:5 | a | test_free.cpp:11:10:11:10 | a | test_free.cpp:12:5:12:5 | a | Memory may have been previously freed by $@. | test_free.cpp:11:5:11:8 | call to free | call to free |
|
||||
| test_free.cpp:12:5:12:5 | a | test_free.cpp:11:10:11:10 | a | test_free.cpp:12:5:12:5 | a | Memory may have been previously freed by $@. | test_free.cpp:11:5:11:8 | call to free | call to free |
|
||||
| test_free.cpp:13:6:13:6 | a | test_free.cpp:11:10:11:10 | a | test_free.cpp:13:6:13:6 | a | Memory may have been previously freed by $@. | test_free.cpp:11:5:11:8 | call to free | call to free |
|
||||
| test_free.cpp:13:6:13:6 | a | test_free.cpp:11:10:11:10 | a | test_free.cpp:13:6:13:6 | a | Memory may have been previously freed by $@. | test_free.cpp:11:5:11:8 | call to free | call to free |
|
||||
| test_free.cpp:45:5:45:5 | a | test_free.cpp:42:27:42:27 | a | test_free.cpp:45:5:45:5 | a | Memory may have been previously freed by $@. | test_free.cpp:42:22:42:25 | call to free | call to free |
|
||||
| test_free.cpp:45:5:45:5 | a | test_free.cpp:42:27:42:27 | a | test_free.cpp:45:5:45:5 | a | Memory may have been previously freed by $@. | test_free.cpp:42:22:42:25 | call to free | call to free |
|
||||
| test_free.cpp:45:5:45:5 | a | test_free.cpp:44:27:44:27 | a | test_free.cpp:45:5:45:5 | a | Memory may have been previously freed by $@. | test_free.cpp:44:22:44:25 | call to free | call to free |
|
||||
| test_free.cpp:45:5:45:5 | a | test_free.cpp:44:27:44:27 | a | test_free.cpp:45:5:45:5 | a | Memory may have been previously freed by $@. | test_free.cpp:44:22:44:25 | call to free | call to free |
|
||||
| test_free.cpp:71:9:71:9 | a | test_free.cpp:69:10:69:10 | a | test_free.cpp:71:9:71:9 | a | Memory may have been previously freed by $@. | test_free.cpp:69:5:69:8 | call to free | call to free |
|
||||
| test_free.cpp:71:9:71:9 | a | test_free.cpp:69:10:69:10 | a | test_free.cpp:71:9:71:9 | a | Memory may have been previously freed by $@. | test_free.cpp:69:5:69:8 | call to free | call to free |
|
||||
| test_free.cpp:91:5:91:5 | a | test_free.cpp:90:10:90:10 | a | test_free.cpp:91:5:91:5 | a | Memory may have been previously freed by $@. | test_free.cpp:90:5:90:8 | call to free | call to free |
|
||||
| test_free.cpp:91:5:91:5 | a | test_free.cpp:90:10:90:10 | a | test_free.cpp:91:5:91:5 | a | Memory may have been previously freed by $@. | test_free.cpp:90:5:90:8 | call to free | call to free |
|
||||
| test_free.cpp:96:9:96:9 | a | test_free.cpp:95:10:95:10 | a | test_free.cpp:96:9:96:9 | a | Memory may have been previously freed by $@. | test_free.cpp:95:5:95:8 | call to free | call to free |
|
||||
| test_free.cpp:102:23:102:23 | a | test_free.cpp:101:10:101:10 | a | test_free.cpp:102:23:102:23 | a | Memory may have been previously freed by $@. | test_free.cpp:101:5:101:8 | call to free | call to free |
|
||||
| test_free.cpp:153:5:153:5 | a | test_free.cpp:152:27:152:27 | a | test_free.cpp:153:5:153:5 | a | Memory may have been previously freed by $@. | test_free.cpp:152:22:152:25 | call to free | call to free |
|
||||
| test_free.cpp:153:5:153:5 | a | test_free.cpp:152:27:152:27 | a | test_free.cpp:153:5:153:5 | a | Memory may have been previously freed by $@. | test_free.cpp:152:22:152:25 | call to free | call to free |
|
||||
@@ -0,0 +1 @@
|
||||
Critical/UseAfterFree.ql
|
||||
229
cpp/ql/test/query-tests/Critical/MemoryFreed/test_free.cpp
Normal file
229
cpp/ql/test/query-tests/Critical/MemoryFreed/test_free.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
void *malloc(int);
|
||||
void free(void *);
|
||||
bool condition();
|
||||
void use(void*);
|
||||
void *realloc(void*, unsigned long);
|
||||
int strlen(char*);
|
||||
int asprintf(char ** strp, const char * fmt, ...);
|
||||
|
||||
|
||||
void* test_double_free1(int *a) {
|
||||
free(a); // GOOD
|
||||
a[5] = 5; // BAD
|
||||
*a = 5; // BAD
|
||||
free(a); // BAD
|
||||
a = (int*) malloc(8);
|
||||
free(a); // GOOD
|
||||
a = (int*) malloc(8);
|
||||
if (!a) free(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void test_double_free_aliasing(void *a, void* b) {
|
||||
free(a); // GOOD
|
||||
a = b;
|
||||
free(a); // GOOD
|
||||
free(b); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_dominance1(void *a) {
|
||||
free(a);
|
||||
if (condition()) free(a); // BAD
|
||||
}
|
||||
|
||||
void test_dominance2(void *a) {
|
||||
free(a);
|
||||
if (condition()) a = malloc(10);
|
||||
if (condition()) free(a); // BAD
|
||||
}
|
||||
|
||||
void test_post_dominance1(int *a)
|
||||
{
|
||||
if (condition()) free(a);
|
||||
if (condition()) a[2] = 5; // BAD [NOT DETECTED]
|
||||
if (condition()) free(a); // BAD [NOT DETECTED]
|
||||
a[2] = 5; // BAD
|
||||
free(a); // BAD
|
||||
}
|
||||
|
||||
void test_post_dominance2(void *a) {
|
||||
if (condition()) free(a);
|
||||
free(a); // BAD
|
||||
}
|
||||
|
||||
void test_post_dominance3(void *a) {
|
||||
if (condition()) free(a);
|
||||
a = malloc(10);
|
||||
free(a); // GOOD
|
||||
}
|
||||
|
||||
void test_use_after_free6(int *a, int *b) {
|
||||
free(a);
|
||||
a=b;
|
||||
free(b);
|
||||
if (condition()) a[0] = 5; // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_use_after_free7(int *a) {
|
||||
a[0] = 42;
|
||||
free(a);
|
||||
|
||||
if (a[3]) { // BAD
|
||||
free(a); // BAD
|
||||
}
|
||||
}
|
||||
|
||||
class A {
|
||||
public:
|
||||
void f();
|
||||
};
|
||||
|
||||
void test_new1() {
|
||||
A *a = new A();
|
||||
delete(a);
|
||||
a->f(); // BAD [NOT DETECTED]
|
||||
delete(a); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test_dereference1(A *a) {
|
||||
a->f(); // GOOD
|
||||
free(a);
|
||||
a->f(); // BAD
|
||||
}
|
||||
|
||||
void* use_after_free(void *a) {
|
||||
free(a);
|
||||
use(a); // BAD
|
||||
return a; // BAD
|
||||
}
|
||||
|
||||
void test_realloc1(void *a) {
|
||||
free(a);
|
||||
void *b = realloc(a, sizeof(a)*3); // BAD [NOT DETECTED by cpp/double-free]
|
||||
free(a); // BAD
|
||||
free(b); // GOOD
|
||||
}
|
||||
void* test_realloc2(char *a) {
|
||||
void *b = realloc(a, strlen(a)+3); // GOOD
|
||||
|
||||
// From the man page on return values from realloc and reallocarray:
|
||||
// "If these functions fail, the original block is left untouched; it is not freed or moved."
|
||||
if (!b) {
|
||||
free(a); // GOOD
|
||||
}
|
||||
free(b); // GOOD
|
||||
}
|
||||
|
||||
void test_realloc3(void *a) {
|
||||
void *b = realloc(a, 100);
|
||||
if (b) free(b); // GOOD
|
||||
if (!b) {
|
||||
free(a); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void test_ptr_deref(void ** a) {
|
||||
free(*a);
|
||||
*a = malloc(10);
|
||||
free(*a); // GOOD
|
||||
free(*a); // BAD [NOT DETECTED]
|
||||
*a = malloc(10);
|
||||
free(a[0]); // GOOD
|
||||
free(a[1]); // GOOD
|
||||
}
|
||||
|
||||
struct list {
|
||||
struct list *next;
|
||||
void* data;
|
||||
};
|
||||
|
||||
void test_loop1(struct list ** list_ptr) {
|
||||
struct list *next;
|
||||
while (*list_ptr) { // GOOD
|
||||
free((*list_ptr)->data); // GOOD
|
||||
next = (*list_ptr)->next; // GOOD
|
||||
free(*list_ptr); // GOOD
|
||||
*list_ptr = next; // GOOD
|
||||
}
|
||||
free(list_ptr); // GOOD
|
||||
}
|
||||
|
||||
void test_use_after_free8(struct list * a) {
|
||||
if (condition()) free(a);
|
||||
a->data = malloc(10); // BAD
|
||||
free(a); // BAD
|
||||
}
|
||||
|
||||
void test_loop2(char ** a) {
|
||||
while (*a) { // GOOD
|
||||
free(*a); // GOOD
|
||||
a++;
|
||||
}
|
||||
free(a); // GOOD
|
||||
}
|
||||
|
||||
void* test_realloc4() {
|
||||
void *a = 0;
|
||||
void *b = realloc(a, 10); // BAD for cpp/memory-never-freed
|
||||
if (!b) { return a; }
|
||||
return b;
|
||||
}
|
||||
|
||||
void test_sizeof(int *a) {
|
||||
free(a);
|
||||
int x = sizeof(a[0]); // GOOD
|
||||
}
|
||||
|
||||
void call_by_reference(char * &a);
|
||||
int custom_alloc_func(char ** a);
|
||||
|
||||
void test_reassign(char *a) {
|
||||
free(a); // GOOD
|
||||
asprintf(&a, "Hello world"); // GOOD
|
||||
free(a); //GOOD
|
||||
call_by_reference(a); // GOOD
|
||||
free(a); // GOOD
|
||||
int v;
|
||||
if (v = custom_alloc_func(&a)) return;
|
||||
free(a); // GOOD
|
||||
}
|
||||
|
||||
char* test_return1(char *a) {
|
||||
int ret = condition();
|
||||
if (!ret) free(a);
|
||||
return (ret ? a : 0);
|
||||
}
|
||||
|
||||
char* test_return2(char *a) {
|
||||
int ret = condition();
|
||||
if (!ret) free(a);
|
||||
if (ret) return a;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void test_condition1(char *a) {
|
||||
free(a);
|
||||
if (asprintf(&a, "Hello world") || condition());
|
||||
free(a); //GOOD
|
||||
if (condition() || asprintf(&a, "Hello world"));
|
||||
free(a); // BAD
|
||||
}
|
||||
|
||||
void test_condition2(char *a) {
|
||||
free(a);
|
||||
if (condition()) a = (char*) malloc(10);
|
||||
else custom_alloc_func(&a);
|
||||
free(a); // GOOD
|
||||
}
|
||||
|
||||
void* test_return1(void *a) {
|
||||
free(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
void MmFreePagesFromMdl(void*);
|
||||
void ExFreePool(void*);
|
||||
void test_ms_free(void * memory_descriptor_list) {
|
||||
MmFreePagesFromMdl(memory_descriptor_list); //GOOD
|
||||
ExFreePool(memory_descriptor_list); // GOOD
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
| test.cpp:35:7:35:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:34:3:34:7 | call to scanf | call to scanf |
|
||||
| test.cpp:51:7:51:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:50:3:50:7 | call to scanf | call to scanf |
|
||||
| test.cpp:68:7:68:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:67:3:67:7 | call to scanf | call to scanf |
|
||||
| test.cpp:80:7:80:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:79:3:79:7 | call to scanf | call to scanf |
|
||||
| test.cpp:90:8:90:8 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:89:3:89:7 | call to scanf | call to scanf |
|
||||
| test.cpp:98:8:98:8 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:97:3:97:7 | call to scanf | call to scanf |
|
||||
| test.cpp:90:7:90:8 | * ... | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:89:3:89:7 | call to scanf | call to scanf |
|
||||
| test.cpp:98:7:98:8 | * ... | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:97:3:97:7 | call to scanf | call to scanf |
|
||||
| test.cpp:108:7:108:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:107:3:107:8 | call to fscanf | call to fscanf |
|
||||
| test.cpp:115:7:115:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:114:3:114:8 | call to sscanf | call to sscanf |
|
||||
| test.cpp:164:8:164:8 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:162:7:162:11 | call to scanf | call to scanf |
|
||||
@@ -12,13 +11,9 @@
|
||||
| test.cpp:224:8:224:8 | j | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 2. | test.cpp:221:7:221:11 | call to scanf | call to scanf |
|
||||
| test.cpp:248:9:248:9 | d | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 2. | test.cpp:246:25:246:29 | call to scanf | call to scanf |
|
||||
| test.cpp:252:9:252:9 | d | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 2. | test.cpp:250:14:250:18 | call to scanf | call to scanf |
|
||||
| test.cpp:264:7:264:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:263:3:263:7 | call to scanf | call to scanf |
|
||||
| test.cpp:272:7:272:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:271:3:271:7 | call to scanf | call to scanf |
|
||||
| test.cpp:280:7:280:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:279:3:279:7 | call to scanf | call to scanf |
|
||||
| test.cpp:292:7:292:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:291:3:291:7 | call to scanf | call to scanf |
|
||||
| test.cpp:302:8:302:12 | ptr_i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:301:3:301:7 | call to scanf | call to scanf |
|
||||
| test.cpp:310:7:310:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:309:3:309:7 | call to scanf | call to scanf |
|
||||
| test.cpp:404:25:404:25 | u | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:403:6:403:11 | call to sscanf | call to sscanf |
|
||||
| test.cpp:416:7:416:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:413:7:413:11 | call to scanf | call to scanf |
|
||||
| test.cpp:423:7:423:7 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:420:7:420:11 | call to scanf | call to scanf |
|
||||
| test.cpp:430:6:430:6 | i | This variable is read, but may not have been written. It should be guarded by a check that the $@ returns at least 1. | test.cpp:429:2:429:6 | call to scanf | call to scanf |
|
||||
|
||||
@@ -48,7 +48,7 @@ int main()
|
||||
int i = 0;
|
||||
|
||||
scanf("%d", &i);
|
||||
use(i); // BAD. Design choice: already initialized variables shouldn't make a difference.
|
||||
use(i); // GOOD. Design choice: already initialized variables are fine.
|
||||
}
|
||||
|
||||
{
|
||||
@@ -261,7 +261,7 @@ int main()
|
||||
i = 0;
|
||||
|
||||
scanf("%d", &i);
|
||||
use(i); // BAD
|
||||
use(i); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
@@ -269,7 +269,7 @@ int main()
|
||||
|
||||
set_by_ref(i);
|
||||
scanf("%d", &i);
|
||||
use(i); // BAD
|
||||
use(i); // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
{
|
||||
@@ -277,7 +277,7 @@ int main()
|
||||
|
||||
set_by_ptr(&i);
|
||||
scanf("%d", &i);
|
||||
use(i); // BAD
|
||||
use(i); // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
{
|
||||
@@ -299,7 +299,7 @@ int main()
|
||||
int *ptr_i = &i;
|
||||
|
||||
scanf("%d", &i);
|
||||
use(*ptr_i); // BAD: may not have written `i`
|
||||
use(*ptr_i); // BAD [NOT DETECTED]: may not have written `i`
|
||||
}
|
||||
|
||||
{
|
||||
@@ -307,7 +307,7 @@ int main()
|
||||
int *ptr_i = &i;
|
||||
|
||||
scanf("%d", ptr_i);
|
||||
use(i); // BAD: may not have written `*ptr_i`
|
||||
use(i); // BAD [NOT DETECTED]: may not have written `*ptr_i`
|
||||
}
|
||||
|
||||
{
|
||||
@@ -427,5 +427,5 @@ void scan_and_write() {
|
||||
void scan_and_static_variable() {
|
||||
static int i;
|
||||
scanf("%d", &i);
|
||||
use(i); // GOOD [FALSE POSITIVE]: static variables are always 0-initialized
|
||||
use(i); // GOOD: static variables are always 0-initialized
|
||||
}
|
||||
|
||||
@@ -1,6 +1,56 @@
|
||||
| RedundantNullCheckSimple.cpp:4:7:4:7 | Load: p | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:3:7:3:8 | Load: * ... | the value is dereferenced |
|
||||
| RedundantNullCheckSimple.cpp:13:8:13:8 | Load: p | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:10:11:10:12 | Load: * ... | the value is dereferenced |
|
||||
| RedundantNullCheckSimple.cpp:20:7:20:8 | Load: * ... | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:19:7:19:9 | Load: * ... | the value is dereferenced |
|
||||
| RedundantNullCheckSimple.cpp:48:12:48:12 | Load: p | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:51:10:51:11 | Load: * ... | the value is dereferenced |
|
||||
| RedundantNullCheckSimple.cpp:79:7:79:9 | Load: * ... | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:78:7:78:10 | Load: * ... | the value is dereferenced |
|
||||
| RedundantNullCheckSimple.cpp:93:13:93:13 | Load: p | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:92:13:92:18 | Load: * ... | the value is dereferenced |
|
||||
nodes
|
||||
| RedundantNullCheckSimple.cpp:3:3:3:3 | VariableAddress: x | semmle.label | x |
|
||||
| RedundantNullCheckSimple.cpp:3:3:3:8 | Store: ... = ... | semmle.label | ... = ... |
|
||||
| RedundantNullCheckSimple.cpp:3:7:3:8 | Load: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:3:8:3:8 | Load: p | semmle.label | p |
|
||||
| RedundantNullCheckSimple.cpp:4:7:4:7 | Load: p | semmle.label | p |
|
||||
| RedundantNullCheckSimple.cpp:4:7:4:7 | VariableAddress: p | semmle.label | p |
|
||||
| RedundantNullCheckSimple.cpp:10:11:10:12 | Load: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:10:11:10:12 | Store: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:10:12:10:12 | Load: p | semmle.label | p |
|
||||
| RedundantNullCheckSimple.cpp:11:7:11:7 | Load: x | semmle.label | x |
|
||||
| RedundantNullCheckSimple.cpp:11:7:11:7 | VariableAddress: x | semmle.label | x |
|
||||
| RedundantNullCheckSimple.cpp:11:7:11:13 | CompareGT: ... > ... | semmle.label | ... > ... |
|
||||
| RedundantNullCheckSimple.cpp:11:7:11:13 | ConditionalBranch: ... > ... | semmle.label | ... > ... |
|
||||
| RedundantNullCheckSimple.cpp:11:11:11:13 | Constant: 100 | semmle.label | 100 |
|
||||
| RedundantNullCheckSimple.cpp:13:8:13:8 | Load: p | semmle.label | p |
|
||||
| RedundantNullCheckSimple.cpp:13:8:13:8 | VariableAddress: p | semmle.label | p |
|
||||
| RedundantNullCheckSimple.cpp:19:3:19:3 | VariableAddress: x | semmle.label | x |
|
||||
| RedundantNullCheckSimple.cpp:19:3:19:9 | Store: ... = ... | semmle.label | ... = ... |
|
||||
| RedundantNullCheckSimple.cpp:19:7:19:9 | Load: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:19:8:19:9 | Load: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:20:7:20:8 | Load: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:20:8:20:8 | Load: p | semmle.label | p |
|
||||
| RedundantNullCheckSimple.cpp:20:8:20:8 | VariableAddress: p | semmle.label | p |
|
||||
| RedundantNullCheckSimple.cpp:78:3:78:3 | VariableAddress: x | semmle.label | x |
|
||||
| RedundantNullCheckSimple.cpp:78:3:78:10 | Store: ... = ... | semmle.label | ... = ... |
|
||||
| RedundantNullCheckSimple.cpp:78:7:78:10 | Load: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:78:8:78:10 | Load: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:79:7:79:9 | Load: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:79:8:79:9 | Load: pp | semmle.label | pp |
|
||||
| RedundantNullCheckSimple.cpp:79:8:79:9 | VariableAddress: pp | semmle.label | pp |
|
||||
| RedundantNullCheckSimple.cpp:92:13:92:18 | Load: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:92:13:92:18 | Store: * ... | semmle.label | * ... |
|
||||
| RedundantNullCheckSimple.cpp:92:18:92:18 | Load: p | semmle.label | p |
|
||||
| RedundantNullCheckSimple.cpp:93:9:93:10 | Load: sp | semmle.label | sp |
|
||||
| RedundantNullCheckSimple.cpp:93:9:93:10 | VariableAddress: sp | semmle.label | sp |
|
||||
| RedundantNullCheckSimple.cpp:93:13:93:13 | FieldAddress: p | semmle.label | p |
|
||||
| RedundantNullCheckSimple.cpp:93:13:93:13 | Load: p | semmle.label | p |
|
||||
edges
|
||||
| RedundantNullCheckSimple.cpp:3:7:3:8 | Load: * ... | RedundantNullCheckSimple.cpp:4:7:4:7 | Load: p |
|
||||
| RedundantNullCheckSimple.cpp:3:8:3:8 | Load: p | RedundantNullCheckSimple.cpp:4:7:4:7 | Load: p |
|
||||
| RedundantNullCheckSimple.cpp:10:11:10:12 | Load: * ... | RedundantNullCheckSimple.cpp:13:8:13:8 | Load: p |
|
||||
| RedundantNullCheckSimple.cpp:10:12:10:12 | Load: p | RedundantNullCheckSimple.cpp:13:8:13:8 | Load: p |
|
||||
| RedundantNullCheckSimple.cpp:19:7:19:9 | Load: * ... | RedundantNullCheckSimple.cpp:20:7:20:8 | Load: * ... |
|
||||
| RedundantNullCheckSimple.cpp:19:8:19:9 | Load: * ... | RedundantNullCheckSimple.cpp:20:7:20:8 | Load: * ... |
|
||||
| RedundantNullCheckSimple.cpp:78:7:78:10 | Load: * ... | RedundantNullCheckSimple.cpp:79:7:79:9 | Load: * ... |
|
||||
| RedundantNullCheckSimple.cpp:78:8:78:10 | Load: * ... | RedundantNullCheckSimple.cpp:79:7:79:9 | Load: * ... |
|
||||
| RedundantNullCheckSimple.cpp:92:13:92:18 | Load: * ... | RedundantNullCheckSimple.cpp:93:13:93:13 | Load: p |
|
||||
| RedundantNullCheckSimple.cpp:92:18:92:18 | Load: p | RedundantNullCheckSimple.cpp:93:13:93:13 | Load: p |
|
||||
#select
|
||||
| RedundantNullCheckSimple.cpp:4:7:4:7 | Load: p | RedundantNullCheckSimple.cpp:3:7:3:8 | Load: * ... | RedundantNullCheckSimple.cpp:4:7:4:7 | Load: p | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:3:7:3:8 | Load: * ... | the value is dereferenced |
|
||||
| RedundantNullCheckSimple.cpp:13:8:13:8 | Load: p | RedundantNullCheckSimple.cpp:10:11:10:12 | Load: * ... | RedundantNullCheckSimple.cpp:13:8:13:8 | Load: p | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:10:11:10:12 | Load: * ... | the value is dereferenced |
|
||||
| RedundantNullCheckSimple.cpp:20:7:20:8 | Load: * ... | RedundantNullCheckSimple.cpp:19:7:19:9 | Load: * ... | RedundantNullCheckSimple.cpp:20:7:20:8 | Load: * ... | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:19:7:19:9 | Load: * ... | the value is dereferenced |
|
||||
| RedundantNullCheckSimple.cpp:48:12:48:12 | Load: p | RedundantNullCheckSimple.cpp:51:10:51:11 | Load: * ... | RedundantNullCheckSimple.cpp:48:12:48:12 | Load: p | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:51:10:51:11 | Load: * ... | the value is dereferenced |
|
||||
| RedundantNullCheckSimple.cpp:79:7:79:9 | Load: * ... | RedundantNullCheckSimple.cpp:78:7:78:10 | Load: * ... | RedundantNullCheckSimple.cpp:79:7:79:9 | Load: * ... | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:78:7:78:10 | Load: * ... | the value is dereferenced |
|
||||
| RedundantNullCheckSimple.cpp:93:13:93:13 | Load: p | RedundantNullCheckSimple.cpp:92:13:92:18 | Load: * ... | RedundantNullCheckSimple.cpp:93:13:93:13 | Load: p | This null check is redundant because $@ in any case. | RedundantNullCheckSimple.cpp:92:13:92:18 | Load: * ... | the value is dereferenced |
|
||||
|
||||
@@ -1,9 +1,74 @@
|
||||
| test.cpp:36:6:36:9 | data | Memory pointed to by 'data' may have $@. | test.cpp:35:2:35:5 | call to free | been previously freed |
|
||||
| test.cpp:70:7:70:10 | data | Memory pointed to by 'data' may have $@. | test.cpp:67:2:67:5 | call to free | been previously freed |
|
||||
| test.cpp:107:6:107:9 | data | Memory pointed to by 'data' may have $@. | test.cpp:105:2:105:5 | call to free | been previously freed |
|
||||
| test.cpp:117:6:117:9 | data | Memory pointed to by 'data' may have $@. | test.cpp:115:2:115:5 | call to free | been previously freed |
|
||||
| test.cpp:150:2:150:2 | c | Memory pointed to by 'c' may have $@. | test.cpp:149:2:149:10 | delete | been previously freed |
|
||||
| test.cpp:151:4:151:4 | c | Memory pointed to by 'c' may have $@. | test.cpp:149:2:149:10 | delete | been previously freed |
|
||||
| test.cpp:170:6:170:9 | data | Memory pointed to by 'data' may have $@. | test.cpp:165:2:165:5 | call to free | been previously freed |
|
||||
| test.cpp:193:6:193:9 | data | Memory pointed to by 'data' may have $@. | test.cpp:191:3:191:6 | call to free | been previously freed |
|
||||
| test.cpp:201:6:201:6 | x | Memory pointed to by 'x' may have $@. | test.cpp:200:2:200:9 | delete | been previously freed |
|
||||
edges
|
||||
| test.cpp:39:7:39:10 | data | test.cpp:41:6:41:9 | data |
|
||||
| test.cpp:39:7:39:10 | data | test.cpp:41:6:41:9 | data |
|
||||
| test.cpp:75:7:75:10 | data | test.cpp:79:7:79:10 | data |
|
||||
| test.cpp:75:7:75:10 | data | test.cpp:79:7:79:10 | data |
|
||||
| test.cpp:106:7:106:10 | data | test.cpp:108:6:108:9 | data |
|
||||
| test.cpp:106:7:106:10 | data | test.cpp:108:6:108:9 | data |
|
||||
| test.cpp:116:7:116:10 | data | test.cpp:119:6:119:9 | data |
|
||||
| test.cpp:116:7:116:10 | data | test.cpp:119:6:119:9 | data |
|
||||
| test.cpp:127:7:127:10 | data | test.cpp:130:6:130:9 | data |
|
||||
| test.cpp:127:7:127:10 | data | test.cpp:130:6:130:9 | data |
|
||||
| test.cpp:138:7:138:10 | data | test.cpp:141:6:141:9 | data |
|
||||
| test.cpp:138:7:138:10 | data | test.cpp:141:6:141:9 | data |
|
||||
| test.cpp:181:7:181:10 | data | test.cpp:186:6:186:9 | data |
|
||||
| test.cpp:181:7:181:10 | data | test.cpp:186:6:186:9 | data |
|
||||
| test.cpp:192:7:192:10 | data | test.cpp:197:6:197:9 | data |
|
||||
| test.cpp:192:7:192:10 | data | test.cpp:197:6:197:9 | data |
|
||||
| test.cpp:203:7:203:10 | data | test.cpp:209:6:209:9 | data |
|
||||
| test.cpp:203:7:203:10 | data | test.cpp:209:6:209:9 | data |
|
||||
| test.cpp:207:8:207:11 | data | test.cpp:209:6:209:9 | data |
|
||||
| test.cpp:207:8:207:11 | data | test.cpp:209:6:209:9 | data |
|
||||
nodes
|
||||
| test.cpp:39:7:39:10 | data | semmle.label | data |
|
||||
| test.cpp:39:7:39:10 | data | semmle.label | data |
|
||||
| test.cpp:41:6:41:9 | data | semmle.label | data |
|
||||
| test.cpp:75:7:75:10 | data | semmle.label | data |
|
||||
| test.cpp:75:7:75:10 | data | semmle.label | data |
|
||||
| test.cpp:79:7:79:10 | data | semmle.label | data |
|
||||
| test.cpp:106:7:106:10 | data | semmle.label | data |
|
||||
| test.cpp:106:7:106:10 | data | semmle.label | data |
|
||||
| test.cpp:108:6:108:9 | data | semmle.label | data |
|
||||
| test.cpp:116:7:116:10 | data | semmle.label | data |
|
||||
| test.cpp:116:7:116:10 | data | semmle.label | data |
|
||||
| test.cpp:119:6:119:9 | data | semmle.label | data |
|
||||
| test.cpp:127:7:127:10 | data | semmle.label | data |
|
||||
| test.cpp:127:7:127:10 | data | semmle.label | data |
|
||||
| test.cpp:130:6:130:9 | data | semmle.label | data |
|
||||
| test.cpp:138:7:138:10 | data | semmle.label | data |
|
||||
| test.cpp:138:7:138:10 | data | semmle.label | data |
|
||||
| test.cpp:141:6:141:9 | data | semmle.label | data |
|
||||
| test.cpp:181:7:181:10 | data | semmle.label | data |
|
||||
| test.cpp:181:7:181:10 | data | semmle.label | data |
|
||||
| test.cpp:186:6:186:9 | data | semmle.label | data |
|
||||
| test.cpp:192:7:192:10 | data | semmle.label | data |
|
||||
| test.cpp:192:7:192:10 | data | semmle.label | data |
|
||||
| test.cpp:197:6:197:9 | data | semmle.label | data |
|
||||
| test.cpp:203:7:203:10 | data | semmle.label | data |
|
||||
| test.cpp:203:7:203:10 | data | semmle.label | data |
|
||||
| test.cpp:207:8:207:11 | data | semmle.label | data |
|
||||
| test.cpp:207:8:207:11 | data | semmle.label | data |
|
||||
| test.cpp:209:6:209:9 | data | semmle.label | data |
|
||||
| test.cpp:209:6:209:9 | data | semmle.label | data |
|
||||
subpaths
|
||||
#select
|
||||
| test.cpp:41:6:41:9 | data | test.cpp:39:7:39:10 | data | test.cpp:41:6:41:9 | data | Memory may have been previously freed by $@. | test.cpp:39:2:39:5 | call to free | call to free |
|
||||
| test.cpp:41:6:41:9 | data | test.cpp:39:7:39:10 | data | test.cpp:41:6:41:9 | data | Memory may have been previously freed by $@. | test.cpp:39:2:39:5 | call to free | call to free |
|
||||
| test.cpp:79:7:79:10 | data | test.cpp:75:7:75:10 | data | test.cpp:79:7:79:10 | data | Memory may have been previously freed by $@. | test.cpp:75:2:75:5 | call to free | call to free |
|
||||
| test.cpp:79:7:79:10 | data | test.cpp:75:7:75:10 | data | test.cpp:79:7:79:10 | data | Memory may have been previously freed by $@. | test.cpp:75:2:75:5 | call to free | call to free |
|
||||
| test.cpp:108:6:108:9 | data | test.cpp:106:7:106:10 | data | test.cpp:108:6:108:9 | data | Memory may have been previously freed by $@. | test.cpp:106:2:106:5 | call to free | call to free |
|
||||
| test.cpp:108:6:108:9 | data | test.cpp:106:7:106:10 | data | test.cpp:108:6:108:9 | data | Memory may have been previously freed by $@. | test.cpp:106:2:106:5 | call to free | call to free |
|
||||
| test.cpp:119:6:119:9 | data | test.cpp:116:7:116:10 | data | test.cpp:119:6:119:9 | data | Memory may have been previously freed by $@. | test.cpp:116:2:116:5 | call to free | call to free |
|
||||
| test.cpp:119:6:119:9 | data | test.cpp:116:7:116:10 | data | test.cpp:119:6:119:9 | data | Memory may have been previously freed by $@. | test.cpp:116:2:116:5 | call to free | call to free |
|
||||
| test.cpp:130:6:130:9 | data | test.cpp:127:7:127:10 | data | test.cpp:130:6:130:9 | data | Memory may have been previously freed by $@. | test.cpp:127:2:127:5 | call to free | call to free |
|
||||
| test.cpp:130:6:130:9 | data | test.cpp:127:7:127:10 | data | test.cpp:130:6:130:9 | data | Memory may have been previously freed by $@. | test.cpp:127:2:127:5 | call to free | call to free |
|
||||
| test.cpp:141:6:141:9 | data | test.cpp:138:7:138:10 | data | test.cpp:141:6:141:9 | data | Memory may have been previously freed by $@. | test.cpp:138:2:138:5 | call to free | call to free |
|
||||
| test.cpp:141:6:141:9 | data | test.cpp:138:7:138:10 | data | test.cpp:141:6:141:9 | data | Memory may have been previously freed by $@. | test.cpp:138:2:138:5 | call to free | call to free |
|
||||
| test.cpp:186:6:186:9 | data | test.cpp:181:7:181:10 | data | test.cpp:186:6:186:9 | data | Memory may have been previously freed by $@. | test.cpp:181:2:181:5 | call to free | call to free |
|
||||
| test.cpp:186:6:186:9 | data | test.cpp:181:7:181:10 | data | test.cpp:186:6:186:9 | data | Memory may have been previously freed by $@. | test.cpp:181:2:181:5 | call to free | call to free |
|
||||
| test.cpp:197:6:197:9 | data | test.cpp:192:7:192:10 | data | test.cpp:197:6:197:9 | data | Memory may have been previously freed by $@. | test.cpp:192:2:192:5 | call to free | call to free |
|
||||
| test.cpp:197:6:197:9 | data | test.cpp:192:7:192:10 | data | test.cpp:197:6:197:9 | data | Memory may have been previously freed by $@. | test.cpp:192:2:192:5 | call to free | call to free |
|
||||
| test.cpp:209:6:209:9 | data | test.cpp:203:7:203:10 | data | test.cpp:209:6:209:9 | data | Memory may have been previously freed by $@. | test.cpp:203:2:203:5 | call to free | call to free |
|
||||
| test.cpp:209:6:209:9 | data | test.cpp:203:7:203:10 | data | test.cpp:209:6:209:9 | data | Memory may have been previously freed by $@. | test.cpp:203:2:203:5 | call to free | call to free |
|
||||
| test.cpp:209:6:209:9 | data | test.cpp:207:8:207:11 | data | test.cpp:209:6:209:9 | data | Memory may have been previously freed by $@. | test.cpp:207:3:207:6 | call to free | call to free |
|
||||
| test.cpp:209:6:209:9 | data | test.cpp:207:8:207:11 | data | test.cpp:209:6:209:9 | data | Memory may have been previously freed by $@. | test.cpp:207:3:207:6 | call to free | call to free |
|
||||
|
||||
@@ -6,14 +6,18 @@ typedef unsigned long size_t;
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
|
||||
void useExternal(char* data);
|
||||
void useExternal(...);
|
||||
|
||||
void use(char* data)
|
||||
void use_if_nonzero(char* data)
|
||||
{
|
||||
if (data)
|
||||
useExternal(data);
|
||||
}
|
||||
|
||||
void use(char* data) {
|
||||
useExternal(*data);
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void noReturn();
|
||||
|
||||
@@ -31,8 +35,9 @@ void test1()
|
||||
{
|
||||
char* data;
|
||||
data = (char *)malloc(100*sizeof(char));
|
||||
use(data); // GOOD
|
||||
use_if_nonzero(data); // GOOD
|
||||
free(data);
|
||||
use_if_nonzero(data); // BAD [NOT DETECTED]
|
||||
use(data); // BAD
|
||||
}
|
||||
|
||||
@@ -42,9 +47,11 @@ void test2()
|
||||
data = (char *)malloc(100*sizeof(char));
|
||||
free(data);
|
||||
myMalloc(&data);
|
||||
use_if_nonzero(data); // GOOD
|
||||
use(data); // GOOD
|
||||
free(data);
|
||||
myMalloc2(data);
|
||||
use_if_nonzero(data); // GOOD
|
||||
use(data); // GOOD
|
||||
}
|
||||
|
||||
@@ -56,6 +63,7 @@ void test3()
|
||||
data = NULL;
|
||||
if (data)
|
||||
{
|
||||
use_if_nonzero(data); // GOOD
|
||||
use(data); // GOOD
|
||||
}
|
||||
}
|
||||
@@ -67,6 +75,7 @@ void test4()
|
||||
free(data);
|
||||
if (data)
|
||||
{
|
||||
use_if_nonzero(data); // BAD [NOT DETECTED]
|
||||
use(data); // BAD
|
||||
}
|
||||
}
|
||||
@@ -85,7 +94,8 @@ char* returnsFreedData(int i)
|
||||
void test5()
|
||||
{
|
||||
char* data = returnsFreedData(1);
|
||||
use(data); // BAD (NOT REPORTED)
|
||||
use_if_nonzero(data); // BAD [NOT DETECTED]
|
||||
use(data); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test6()
|
||||
@@ -94,7 +104,8 @@ void test6()
|
||||
data = (char *)malloc(100*sizeof(char));
|
||||
data2 = data;
|
||||
free(data);
|
||||
use(data2); // BAD (NOT REPORTED)
|
||||
use_if_nonzero(data2); // BAD [NOT DETECTED]
|
||||
use(data); // BAD
|
||||
}
|
||||
|
||||
void test7()
|
||||
@@ -104,6 +115,7 @@ void test7()
|
||||
data2 = data;
|
||||
free(data);
|
||||
data2 = NULL;
|
||||
use_if_nonzero(data); // BAD [NOT DETECTED]
|
||||
use(data); // BAD
|
||||
}
|
||||
|
||||
@@ -114,6 +126,7 @@ void test8()
|
||||
data = data2;
|
||||
free(data);
|
||||
data2 = NULL;
|
||||
use_if_nonzero(data); // BAD [NOT DETECTED]
|
||||
use(data); // BAD
|
||||
}
|
||||
|
||||
@@ -124,13 +137,15 @@ void test9()
|
||||
char *data, *data2;
|
||||
free(data);
|
||||
noReturnWrapper();
|
||||
use(data); // GOOD
|
||||
use_if_nonzero(data); // GOOD
|
||||
use(data); // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
void test10()
|
||||
{
|
||||
for (char *data; true; data = NULL)
|
||||
{
|
||||
use_if_nonzero(data); // GOOD
|
||||
use(data); // GOOD
|
||||
free(data);
|
||||
}
|
||||
@@ -140,7 +155,7 @@ class myClass
|
||||
{
|
||||
public:
|
||||
myClass() { }
|
||||
|
||||
|
||||
void myMethod() { }
|
||||
};
|
||||
|
||||
@@ -156,7 +171,8 @@ template<class T> T test()
|
||||
T* x;
|
||||
use(x); // GOOD
|
||||
delete x;
|
||||
use(x); // BAD
|
||||
use_if_nonzero(x); // BAD [NOT DETECTED]
|
||||
use(x); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
void test12(int count)
|
||||
@@ -178,7 +194,7 @@ void test13()
|
||||
{
|
||||
data = NULL;
|
||||
}
|
||||
use(data); // GOOD
|
||||
use(data); // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
void test14()
|
||||
@@ -198,7 +214,7 @@ template<class T> T test15()
|
||||
T* x;
|
||||
use(x); // GOOD
|
||||
delete x;
|
||||
use(x); // BAD
|
||||
use(x); // BAD [NOT DETECTED]
|
||||
}
|
||||
void test15runner(void)
|
||||
{
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.4.6
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.4.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.4.6
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.4.5
|
||||
lastReleaseVersion: 1.4.6
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.4.6-dev
|
||||
version: 1.5.0-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 1.4.6
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 1.4.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
## 1.4.6
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.4.5
|
||||
lastReleaseVersion: 1.4.6
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.4.6-dev
|
||||
version: 1.5.0-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.5.6
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.5
|
||||
|
||||
### New Features
|
||||
|
||||
3
csharp/ql/lib/change-notes/released/0.5.6.md
Normal file
3
csharp/ql/lib/change-notes/released/0.5.6.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 0.5.6
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.5.5
|
||||
lastReleaseVersion: 0.5.6
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 0.5.6-dev
|
||||
version: 0.6.0-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -73,12 +73,15 @@
|
||||
* sources "remote" indicates a default remote flow source, and for summaries
|
||||
* "taint" indicates a default additional taint step and "value" indicates a
|
||||
* globally applicable value-preserving step.
|
||||
* 9. The `provenance` column is a tag to indicate the origin of the summary.
|
||||
* There are two supported values: "generated" and "manual". "generated" means that
|
||||
* the model has been emitted by the model generator tool and "manual" means
|
||||
* that the model has been written by hand. This information is used in a heuristic
|
||||
* for dataflow analysis to determine, if a model or source code should be used for
|
||||
* determining flow.
|
||||
* 9. The `provenance` column is a tag to indicate the origin and verification of a model.
|
||||
* The format is {origin}-{verification} or just "manual" where the origin describes
|
||||
* the origin of the model and verification describes how the model has been verified.
|
||||
* Some examples are:
|
||||
* - "df-generated": The model has been generated by the model generator tool.
|
||||
* - "df-manual": The model has been generated by the model generator and verified by a human.
|
||||
* - "manual": The model has been written by hand.
|
||||
* This information is used in a heuristic for dataflow analysis to determine, if a
|
||||
* model or source code should be used for determining flow.
|
||||
*/
|
||||
|
||||
import csharp
|
||||
@@ -248,7 +251,7 @@ module ModelValidation {
|
||||
not ext.regexpMatch("|Attribute") and
|
||||
result = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
|
||||
or
|
||||
not provenance = ["manual", "generated"] and
|
||||
invalidProvenance(provenance) and
|
||||
result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
|
||||
)
|
||||
}
|
||||
|
||||
@@ -137,8 +137,6 @@ private class RecordConstructorFlow extends SummarizedCallable {
|
||||
preservesValue = true
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasProvenance(string provenance) { provenance = "manual" }
|
||||
}
|
||||
|
||||
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
|
||||
|
||||
@@ -95,7 +95,7 @@ class DataFlowSummarizedCallable instanceof FlowSummary::SummarizedCallable {
|
||||
DataFlowSummarizedCallable() {
|
||||
not this.fromSource()
|
||||
or
|
||||
this.fromSource() and not this.isAutoGenerated()
|
||||
this.fromSource() and not this.applyGeneratedModel()
|
||||
}
|
||||
|
||||
string toString() { result = super.toString() }
|
||||
@@ -555,3 +555,13 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) {
|
||||
apos.isImplicitCapturedArgumentPosition(v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if flow from `call`'s argument `arg` to parameter `p` is permissible.
|
||||
*
|
||||
* This is a temporary hook to support technical debt in the Go language; do not use.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate golangSpecificParamArgFilter(DataFlowCall call, ParameterNode p, ArgumentNode arg) {
|
||||
any()
|
||||
}
|
||||
|
||||
@@ -425,7 +425,8 @@ private module Cached {
|
||||
exists(ParameterPosition ppos |
|
||||
viableParam(call, ppos, p) and
|
||||
argumentPositionMatch(call, arg, ppos) and
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p))
|
||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) and
|
||||
golangSpecificParamArgFilter(call, p, arg)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -215,6 +215,54 @@ module Public {
|
||||
abstract predicate required(SummaryComponent head, SummaryComponentStack tail);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the valid model origin values.
|
||||
*/
|
||||
private string getValidModelOrigin() {
|
||||
result =
|
||||
[
|
||||
"ai", // AI (machine learning)
|
||||
"df", // Dataflow (model generator)
|
||||
"tb", // Type based (model generator)
|
||||
"hq", // Heuristic query
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* A class used to represent provenance values for MaD models.
|
||||
*
|
||||
* The provenance value is a string of the form `origin-verification`
|
||||
* (or just `manual`), where `origin` is a value indicating the
|
||||
* origin of the model, and `verification` is a value indicating, how
|
||||
* the model was verified.
|
||||
*
|
||||
* Examples could be:
|
||||
* - `df-generated`: A model produced by the model generator, but not verified by a human.
|
||||
* - `ai-manual`: A model produced by AI, but verified by a human.
|
||||
*/
|
||||
class Provenance extends string {
|
||||
private string verification;
|
||||
|
||||
Provenance() {
|
||||
exists(string origin | origin = getValidModelOrigin() |
|
||||
this = origin + "-" + verification and
|
||||
verification = ["manual", "generated"]
|
||||
)
|
||||
or
|
||||
this = verification and verification = "manual"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is a valid generated provenance value.
|
||||
*/
|
||||
predicate isGenerated() { verification = "generated" }
|
||||
|
||||
/**
|
||||
* Holds if this is a valid manual provenance value.
|
||||
*/
|
||||
predicate isManual() { verification = "manual" }
|
||||
}
|
||||
|
||||
/** A callable with a flow summary. */
|
||||
abstract class SummarizedCallable extends SummarizedCallableBase {
|
||||
bindingset[this]
|
||||
@@ -248,41 +296,61 @@ module Public {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if all the summaries that apply to `this` are auto generated and not manually created.
|
||||
* Holds if there exists a generated summary that applies to this callable.
|
||||
*/
|
||||
final predicate isAutoGenerated() {
|
||||
this.hasProvenance(["generated", "ai-generated"]) and not this.isManual()
|
||||
final predicate hasGeneratedModel() {
|
||||
exists(Provenance p | p.isGenerated() and this.hasProvenance(p))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a manual summary that applies to `this`.
|
||||
* Holds if all the summaries that apply to this callable are auto generated and not manually created.
|
||||
* That is, only apply generated models, when there are no manual models.
|
||||
*/
|
||||
final predicate isManual() { this.hasProvenance("manual") }
|
||||
final predicate applyGeneratedModel() {
|
||||
this.hasGeneratedModel() and
|
||||
not this.hasManualModel()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a summary that applies to `this` that has provenance `provenance`.
|
||||
* Holds if there exists a manual summary that applies to this callable.
|
||||
*/
|
||||
predicate hasProvenance(string provenance) { none() }
|
||||
final predicate hasManualModel() {
|
||||
exists(Provenance p | p.isManual() and this.hasProvenance(p))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there exists a manual summary that applies to this callable.
|
||||
* Always apply manual models if they exist.
|
||||
*/
|
||||
final predicate applyManualModel() { this.hasManualModel() }
|
||||
|
||||
/**
|
||||
* Holds if there exists a summary that applies to this callable
|
||||
* that has provenance `provenance`.
|
||||
*/
|
||||
predicate hasProvenance(Provenance provenance) { provenance = "manual" }
|
||||
}
|
||||
|
||||
/** A callable where there is no flow via the callable. */
|
||||
class NeutralCallable extends SummarizedCallableBase {
|
||||
NeutralCallable() { neutralElement(this, _) }
|
||||
private Provenance provenance;
|
||||
|
||||
NeutralCallable() { neutralElement(this, provenance) }
|
||||
|
||||
/**
|
||||
* Holds if the neutral is auto generated.
|
||||
*/
|
||||
predicate isAutoGenerated() { neutralElement(this, ["generated", "ai-generated"]) }
|
||||
final predicate hasGeneratedModel() { provenance.isGenerated() }
|
||||
|
||||
/**
|
||||
* Holds if there exists a manual neutral that applies to `this`.
|
||||
* Holds if there exists a manual neutral that applies to this callable.
|
||||
*/
|
||||
final predicate isManual() { this.hasProvenance("manual") }
|
||||
final predicate hasManualModel() { provenance.isManual() }
|
||||
|
||||
/**
|
||||
* Holds if the neutral has provenance `provenance`.
|
||||
* Holds if the neutral has provenance `p`.
|
||||
*/
|
||||
predicate hasProvenance(string provenance) { neutralElement(this, provenance) }
|
||||
predicate hasProvenance(Provenance p) { p = provenance }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1017,12 +1085,18 @@ module Private {
|
||||
private predicate relevantSummaryElementGenerated(
|
||||
AccessPath inSpec, AccessPath outSpec, string kind
|
||||
) {
|
||||
summaryElement(this, inSpec, outSpec, kind, ["generated", "ai-generated"]) and
|
||||
not summaryElement(this, _, _, _, "manual")
|
||||
exists(Provenance provenance |
|
||||
provenance.isGenerated() and
|
||||
summaryElement(this, inSpec, outSpec, kind, provenance)
|
||||
) and
|
||||
not this.applyManualModel()
|
||||
}
|
||||
|
||||
private predicate relevantSummaryElement(AccessPath inSpec, AccessPath outSpec, string kind) {
|
||||
summaryElement(this, inSpec, outSpec, kind, "manual")
|
||||
exists(Provenance provenance |
|
||||
provenance.isManual() and
|
||||
summaryElement(this, inSpec, outSpec, kind, provenance)
|
||||
)
|
||||
or
|
||||
this.relevantSummaryElementGenerated(inSpec, outSpec, kind)
|
||||
}
|
||||
@@ -1041,7 +1115,7 @@ module Private {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasProvenance(string provenance) {
|
||||
override predicate hasProvenance(Provenance provenance) {
|
||||
summaryElement(this, _, _, _, provenance)
|
||||
}
|
||||
}
|
||||
@@ -1052,6 +1126,10 @@ module Private {
|
||||
not exists(interpretComponent(c))
|
||||
}
|
||||
|
||||
/** Holds if `provenance` is not a valid provenance value. */
|
||||
bindingset[provenance]
|
||||
predicate invalidProvenance(string provenance) { not provenance instanceof Provenance }
|
||||
|
||||
/**
|
||||
* Holds if token `part` of specification `spec` has an invalid index.
|
||||
* E.g., `Argument[-1]`.
|
||||
@@ -1219,11 +1297,11 @@ module Private {
|
||||
}
|
||||
|
||||
private string renderProvenance(SummarizedCallable c) {
|
||||
if c.isManual() then result = "manual" else c.hasProvenance(result)
|
||||
if c.applyManualModel() then result = "manual" else c.hasProvenance(result)
|
||||
}
|
||||
|
||||
private string renderProvenanceNeutral(NeutralCallable c) {
|
||||
if c.isManual() then result = "manual" else c.hasProvenance(result)
|
||||
if c.hasManualModel() then result = "manual" else c.hasProvenance(result)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -86,8 +86,6 @@ module EntityFramework {
|
||||
abstract class EFSummarizedCallable extends SummarizedCallable {
|
||||
bindingset[this]
|
||||
EFSummarizedCallable() { any() }
|
||||
|
||||
override predicate hasProvenance(string provenance) { provenance = "manual" }
|
||||
}
|
||||
|
||||
private class DbSetAddOrUpdateRequiredSummaryComponentStack extends RequiredSummaryComponentStack {
|
||||
|
||||
@@ -24,9 +24,11 @@ abstract class Sink extends DataFlow::ExprNode { }
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ClearTextStorage` instead.
|
||||
*
|
||||
* A taint-tracking configuration for cleartext storage of sensitive information.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ClearTextStorage" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -36,6 +38,22 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for cleartext storage of sensitive information.
|
||||
*/
|
||||
private module ClearTextStorageConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for cleartext storage of sensitive information.
|
||||
*/
|
||||
module ClearTextStorage = TaintTracking::Global<ClearTextStorageConfig>;
|
||||
|
||||
/** A source of sensitive data. */
|
||||
class SensitiveExprSource extends Source {
|
||||
SensitiveExprSource() { this.getExpr() instanceof SensitiveExpr }
|
||||
|
||||
@@ -90,8 +90,12 @@ class ExternalApiDataNode extends DataFlow::Node {
|
||||
/** DEPRECATED: Alias for ExternalApiDataNode */
|
||||
deprecated class ExternalAPIDataNode = ExternalApiDataNode;
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalApiDataNode`s. */
|
||||
class UntrustedDataToExternalApiConfig extends TaintTracking::Configuration {
|
||||
/**
|
||||
* DEPRECATED: Use `RemoteSourceToExternalApi` instead.
|
||||
*
|
||||
* A configuration for tracking flow from `RemoteFlowSource`s to `ExternalApiDataNode`s.
|
||||
*/
|
||||
deprecated class UntrustedDataToExternalApiConfig extends TaintTracking::Configuration {
|
||||
UntrustedDataToExternalApiConfig() { this = "UntrustedDataToExternalAPIConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
@@ -99,17 +103,25 @@ class UntrustedDataToExternalApiConfig extends TaintTracking::Configuration {
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ExternalApiDataNode }
|
||||
}
|
||||
|
||||
/** A configuration for tracking flow from `RemoteFlowSource`s to `ExternalApiDataNode`s. */
|
||||
private module RemoteSourceToExternalApiConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof ExternalApiDataNode }
|
||||
}
|
||||
|
||||
/** A module for tracking flow from `RemoteFlowSource`s to `ExternalApiDataNode`s. */
|
||||
module RemoteSourceToExternalApi = TaintTracking::Global<RemoteSourceToExternalApiConfig>;
|
||||
|
||||
/** DEPRECATED: Alias for UntrustedDataToExternalApiConfig */
|
||||
deprecated class UntrustedDataToExternalAPIConfig = UntrustedDataToExternalApiConfig;
|
||||
|
||||
/** A node representing untrusted data being passed to an external API. */
|
||||
class UntrustedExternalApiDataNode extends ExternalApiDataNode {
|
||||
private UntrustedDataToExternalApiConfig c;
|
||||
|
||||
UntrustedExternalApiDataNode() { c.hasFlow(_, this) }
|
||||
UntrustedExternalApiDataNode() { RemoteSourceToExternalApi::flow(_, this) }
|
||||
|
||||
/** Gets a source of untrusted data which is passed to this external API data node. */
|
||||
DataFlow::Node getAnUntrustedSource() { c.hasFlow(result, this) }
|
||||
DataFlow::Node getAnUntrustedSource() { RemoteSourceToExternalApi::flow(result, this) }
|
||||
}
|
||||
|
||||
/** DEPRECATED: Alias for UntrustedExternalApiDataNode */
|
||||
|
||||
@@ -38,9 +38,11 @@ abstract class Sink extends DataFlow::ExprNode {
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `HardcodedCredentials` instead.
|
||||
*
|
||||
* A taint-tracking configuration for hard coded credentials.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "HardcodedCredentials" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -75,6 +77,56 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for hard coded credentials.
|
||||
*/
|
||||
private module HardcodedCredentialsConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink instanceof Sink and
|
||||
// Ignore values that are ultimately returned by mocks, as they don't represent "real"
|
||||
// credentials.
|
||||
not any(ReturnedByMockObject mock).getAMemberInitializationValue() = sink.asExpr() and
|
||||
not any(ReturnedByMockObject mock).getAnArgument() = sink.asExpr()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for hard coded credentials.
|
||||
*/
|
||||
module HardcodedCredentials {
|
||||
import TaintTracking::Global<HardcodedCredentialsConfig> as Super
|
||||
import Super
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `source` to `sink`.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate flowPath(HardcodedCredentials::PathNode source, HardcodedCredentials::PathNode sink) {
|
||||
Super::flowPath(source, sink) and
|
||||
// Exclude hard-coded credentials in tests if they only flow to calls to methods with a name
|
||||
// like "Add*" "Create*" or "Update*". The rationale is that hard-coded credentials within
|
||||
// tests that are only used for creating or setting values within tests are unlikely to
|
||||
// represent credentials to some accessible system.
|
||||
not (
|
||||
source.getNode().asExpr().getFile() instanceof TestFile and
|
||||
exists(MethodCall createOrAddCall, string createOrAddMethodName |
|
||||
createOrAddMethodName.matches("Update%") or
|
||||
createOrAddMethodName.matches("Create%") or
|
||||
createOrAddMethodName.matches("Add%")
|
||||
|
|
||||
createOrAddCall.getTarget().hasName(createOrAddMethodName) and
|
||||
createOrAddCall.getAnArgument() = sink.getNode().asExpr()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A string literal that is not empty.
|
||||
*/
|
||||
|
||||
@@ -25,9 +25,11 @@ abstract class Sink extends DataFlow::ExprNode { }
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `LdapInjection` instead.
|
||||
*
|
||||
* A taint-tracking configuration for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "LDAPInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -37,6 +39,32 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
module LdapInjectionConfig implements DataFlow::ConfigSig {
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unvalidated user input that is used to construct LDAP queries.
|
||||
*/
|
||||
module LdapInjection = TaintTracking::Global<LdapInjectionConfig>;
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
|
||||
@@ -25,9 +25,11 @@ abstract class Sink extends DataFlow::ExprNode { }
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `LogForging` instead.
|
||||
*
|
||||
* A taint-tracking configuration for untrusted user input used in log entries.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "LogForging" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -37,6 +39,22 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in log entries.
|
||||
*/
|
||||
private module LogForgingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for untrusted user input used in log entries.
|
||||
*/
|
||||
module LogForging = TaintTracking::Global<LogForgingConfig>;
|
||||
|
||||
/** A source of remote user input. */
|
||||
private class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
|
||||
@@ -29,10 +29,12 @@ abstract class Sink extends DataFlow::ExprNode {
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `MissingXxmlValidation` instead.
|
||||
*
|
||||
* A taint-tracking configuration for untrusted user input processed as XML without validation against a
|
||||
* known schema.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "MissingXMLValidation" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -42,6 +44,24 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input processed as XML without validation against a
|
||||
* known schema.
|
||||
*/
|
||||
private module MissingXmlValidationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for untrusted user input processed as XML without validation against a
|
||||
* known schema.
|
||||
*/
|
||||
module MissingXmlValidation = TaintTracking::Global<MissingXmlValidationConfig>;
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
|
||||
@@ -25,9 +25,11 @@ abstract class Sink extends DataFlow::ExprNode { }
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ReDoS` instead.
|
||||
*
|
||||
* A taint-tracking configuration for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ReDoS" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -37,6 +39,22 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
private module ReDoSConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for untrusted user input used in dangerous regular expression operations.
|
||||
*/
|
||||
module ReDoS = TaintTracking::Global<ReDoSConfig>;
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
|
||||
@@ -24,9 +24,11 @@ abstract class Sink extends DataFlow::ExprNode { }
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `RegexInjection` instead.
|
||||
*
|
||||
* A taint-tracking configuration for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "RegexInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -36,6 +38,22 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
private module RegexInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for untrusted user input used to construct regular expressions.
|
||||
*/
|
||||
module RegexInjection = TaintTracking::Global<RegexInjectionConfig>;
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
|
||||
@@ -24,9 +24,11 @@ abstract class Sink extends DataFlow::ExprNode { }
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ResourceInjection` instead.
|
||||
*
|
||||
* A taint-tracking configuration for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ResourceInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -36,6 +38,22 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
private module ResourceInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for untrusted user input used in resource descriptors.
|
||||
*/
|
||||
module ResourceInjection = TaintTracking::Global<ResourceInjectionConfig>;
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
|
||||
@@ -25,9 +25,11 @@ abstract class Sink extends DataFlow::ExprNode { }
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `SqlInjection` instead.
|
||||
*
|
||||
* A taint-tracking configuration for SQL injection vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "SqlInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -37,6 +39,32 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for SQL injection vulnerabilities.
|
||||
*/
|
||||
module SqlInjectionConfig implements DataFlow::ConfigSig {
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for SQL injection vulnerabilities.
|
||||
*/
|
||||
module SqlInjection = TaintTracking::Global<SqlInjectionConfig>;
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
|
||||
@@ -26,9 +26,11 @@ abstract class Sink extends DataFlow::ExprNode { }
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `TaintedPath` instead.
|
||||
*
|
||||
* A taint-tracking configuration for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "TaintedPath" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -38,6 +40,22 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
private module TaintedPathConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for uncontrolled data in path expression vulnerabilities.
|
||||
*/
|
||||
module TaintedPath = TaintTracking::Global<TaintedPathConfig>;
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
|
||||
@@ -33,9 +33,11 @@ abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `UrlRedirect` instead.
|
||||
*
|
||||
* A taint-tracking configuration for reasoning about unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "UrlRedirect" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -49,6 +51,22 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for reasoning about unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
private module UrlRedirectConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for reasoning about unvalidated URL redirect vulnerabilities.
|
||||
*/
|
||||
module UrlRedirect = TaintTracking::Global<UrlRedirectConfig>;
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
|
||||
@@ -44,9 +44,11 @@ private class InsecureXmlSink extends Sink {
|
||||
abstract class Sanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `XmlEntityInjection` instead.
|
||||
*
|
||||
* A taint-tracking configuration for untrusted user input used in XML processing.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "XMLInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -61,6 +63,36 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in XML processing.
|
||||
*/
|
||||
private module XmlEntityInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for untrusted user input used in XML processing.
|
||||
*/
|
||||
module XmlEntityInjection implements DataFlow::GlobalFlowSig {
|
||||
import TaintTracking::Global<XmlEntityInjectionConfig> as Super
|
||||
import Super
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `source` to `sink`.
|
||||
*
|
||||
* The corresponding paths are generated from the end-points and the graph
|
||||
* included in the module `PathGraph`.
|
||||
*/
|
||||
predicate flowPath(XmlEntityInjection::PathNode source, XmlEntityInjection::PathNode sink) {
|
||||
Super::flowPath(source, sink) and
|
||||
exists(sink.getNode().(Sink).getReason())
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleTypeSanitizer extends Sanitizer, SimpleTypeSanitizedExpr { }
|
||||
|
||||
private class GuidSanitizer extends Sanitizer, GuidSanitizedExpr { }
|
||||
|
||||
@@ -24,9 +24,11 @@ abstract class Sink extends DataFlow::ExprNode { }
|
||||
abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `XpathInjection` instead.
|
||||
*
|
||||
* A taint-tracking configuration for untrusted user input used in XPath expression.
|
||||
*/
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "XPathInjection" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -36,6 +38,32 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for untrusted user input used in XPath expression.
|
||||
*/
|
||||
module XpathInjectionConfig implements DataFlow::ConfigSig {
|
||||
/**
|
||||
* Holds if `source` is a relevant data flow source.
|
||||
*/
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a relevant data flow sink.
|
||||
*/
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
/**
|
||||
* Holds if data flow through `node` is prohibited. This completely removes
|
||||
* `node` from the data flow graph.
|
||||
*/
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint-tracking module for untrusted user input used in XPath expression.
|
||||
*/
|
||||
module XpathInjection = TaintTracking::Global<XpathInjectionConfig>;
|
||||
|
||||
/** A source of remote user input. */
|
||||
class RemoteSource extends Source instanceof RemoteFlowSource { }
|
||||
|
||||
|
||||
@@ -27,8 +27,12 @@ abstract class Sanitizer extends DataFlow::ExprNode { }
|
||||
*/
|
||||
abstract deprecated class SanitizerGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/** A taint tracking configuration for Zip Slip */
|
||||
class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
/**
|
||||
* DEPRECATED: Use `ZipSlip` instead.
|
||||
*
|
||||
* A taint tracking configuration for Zip Slip.
|
||||
*/
|
||||
deprecated class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
TaintTrackingConfiguration() { this = "ZipSlipTaintTracking" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
@@ -42,6 +46,22 @@ class TaintTrackingConfiguration extends TaintTracking::Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint tracking configuration for Zip Slip.
|
||||
*/
|
||||
private module ZipSlipConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof Source }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint tracking module for Zip Slip.
|
||||
*/
|
||||
module ZipSlip = TaintTracking::Global<ZipSlipConfig>;
|
||||
|
||||
/** An access to the `FullName` property of a `ZipArchiveEntry`. */
|
||||
class ArchiveFullNameSource extends Source {
|
||||
ArchiveFullNameSource() {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.5.6
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 0.5.5
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -10,21 +10,17 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import DataFlow::PathGraph
|
||||
import UnsafeYearCreationFromArithmetic::PathGraph
|
||||
|
||||
class UnsafeYearCreationFromArithmeticConfiguration extends TaintTracking::Configuration {
|
||||
UnsafeYearCreationFromArithmeticConfiguration() {
|
||||
this = "UnsafeYearCreationFromArithmeticConfiguration"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
module UnsafeYearCreationFromArithmeticConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
exists(ArithmeticOperation ao, PropertyAccess pa | ao = source.asExpr() |
|
||||
pa = ao.getAChild*() and
|
||||
pa.getProperty().hasQualifiedName("System.DateTime", "Year")
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
exists(ObjectCreation oc |
|
||||
sink.asExpr() = oc.getArgumentForName("year") and
|
||||
oc.getObjectType().getABaseType*().hasQualifiedName("System", "DateTime")
|
||||
@@ -32,10 +28,12 @@ class UnsafeYearCreationFromArithmeticConfiguration extends TaintTracking::Confi
|
||||
}
|
||||
}
|
||||
|
||||
module UnsafeYearCreationFromArithmetic =
|
||||
TaintTracking::Global<UnsafeYearCreationFromArithmeticConfig>;
|
||||
|
||||
from
|
||||
UnsafeYearCreationFromArithmeticConfiguration config, DataFlow::PathNode source,
|
||||
DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
UnsafeYearCreationFromArithmetic::PathNode source, UnsafeYearCreationFromArithmetic::PathNode sink
|
||||
where UnsafeYearCreationFromArithmetic::flowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"This $@ based on a 'System.DateTime.Year' property is used in a construction of a new 'System.DateTime' object, flowing to the 'year' argument.",
|
||||
source, "arithmetic operation"
|
||||
|
||||
@@ -18,21 +18,20 @@ import csharp
|
||||
import ParallelSink
|
||||
import ICryptoTransform
|
||||
|
||||
class NotThreadSafeCryptoUsageIntoParallelInvokeConfig extends TaintTracking::Configuration {
|
||||
NotThreadSafeCryptoUsageIntoParallelInvokeConfig() {
|
||||
this = "NotThreadSafeCryptoUsageIntoParallelInvokeConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
module NotThreadSafeCryptoUsageIntoParallelInvokeConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source instanceof LambdaCapturingICryptoTransformSource
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof ParallelSink }
|
||||
predicate isSink(DataFlow::Node sink) { sink instanceof ParallelSink }
|
||||
}
|
||||
|
||||
from Expr e, string m, LambdaExpr l, NotThreadSafeCryptoUsageIntoParallelInvokeConfig config
|
||||
module NotThreadSafeCryptoUsageIntoParallelInvoke =
|
||||
TaintTracking::Global<NotThreadSafeCryptoUsageIntoParallelInvokeConfig>;
|
||||
|
||||
from Expr e, string m, LambdaExpr l
|
||||
where
|
||||
config.hasFlow(DataFlow::exprNode(l), DataFlow::exprNode(e)) and
|
||||
NotThreadSafeCryptoUsageIntoParallelInvoke::flow(DataFlow::exprNode(l), DataFlow::exprNode(e)) and
|
||||
m =
|
||||
"A $@ seems to be used to start a new thread is capturing a local variable that either implements 'System.Security.Cryptography.ICryptoTransform' or has a field of this type."
|
||||
select e, m, l, "lambda expression"
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
import csharp
|
||||
import semmle.code.csharp.commons.QualifiedName
|
||||
import semmle.code.csharp.security.dataflow.ExternalAPIsQuery
|
||||
import DataFlow::PathGraph
|
||||
import RemoteSourceToExternalApi::PathGraph
|
||||
|
||||
from
|
||||
UntrustedDataToExternalApiConfig config, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
RemoteSourceToExternalApi::PathNode source, RemoteSourceToExternalApi::PathNode sink,
|
||||
string qualifier, string name
|
||||
where
|
||||
config.hasFlowPath(source, sink) and
|
||||
RemoteSourceToExternalApi::flowPath(source, sink) and
|
||||
sink.getNode().(ExternalApiDataNode).hasQualifiedName(qualifier, name)
|
||||
select sink, source, sink,
|
||||
"Call to " + getQualifiedName(qualifier, name) + " with untrusted data from $@.", source,
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.TaintedPathQuery
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
import TaintedPath::PathGraph
|
||||
|
||||
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where c.hasFlowPath(source, sink)
|
||||
from TaintedPath::PathNode source, TaintedPath::PathNode sink
|
||||
where TaintedPath::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This path depends on a $@.", source.getNode(),
|
||||
"user-provided value"
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.ZipSlipQuery
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
import ZipSlip::PathGraph
|
||||
|
||||
from TaintTrackingConfiguration zipTaintTracking, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where zipTaintTracking.hasFlowPath(source, sink)
|
||||
from ZipSlip::PathNode source, ZipSlip::PathNode sink
|
||||
where ZipSlip::flowPath(source, sink)
|
||||
select source.getNode(), source, sink,
|
||||
"Unsanitized archive entry, which may contain '..', is used in a $@.", sink.getNode(),
|
||||
"file system operation"
|
||||
|
||||
@@ -12,15 +12,21 @@
|
||||
*/
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.SqlInjectionQuery as SqlInjection
|
||||
import semmle.code.csharp.security.dataflow.SqlInjectionQuery
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Stored
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
import StoredSqlInjection::PathGraph
|
||||
|
||||
class StoredTaintTrackingConfiguration extends SqlInjection::TaintTrackingConfiguration {
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof StoredFlowSource }
|
||||
module StoredSqlInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof StoredFlowSource }
|
||||
|
||||
predicate isSink = SqlInjectionConfig::isSink/1;
|
||||
|
||||
predicate isBarrier = SqlInjectionConfig::isBarrier/1;
|
||||
}
|
||||
|
||||
from StoredTaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where c.hasFlowPath(source, sink)
|
||||
module StoredSqlInjection = TaintTracking::Global<StoredSqlInjectionConfig>;
|
||||
|
||||
from StoredSqlInjection::PathNode source, StoredSqlInjection::PathNode sink
|
||||
where StoredSqlInjection::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This SQL query depends on a $@.", source.getNode(),
|
||||
"stored user-provided value"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.SqlInjectionQuery
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
import SqlInjection::PathGraph
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Remote
|
||||
import semmle.code.csharp.security.dataflow.flowsources.Local
|
||||
|
||||
@@ -23,7 +23,7 @@ string getSourceType(DataFlow::Node node) {
|
||||
result = node.(LocalFlowSource).getSourceType()
|
||||
}
|
||||
|
||||
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where c.hasFlowPath(source, sink)
|
||||
from SqlInjection::PathNode source, SqlInjection::PathNode sink
|
||||
where SqlInjection::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This query depends on $@.", source,
|
||||
("this " + getSourceType(source.getNode()))
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
|
||||
import csharp
|
||||
import semmle.code.csharp.security.dataflow.LDAPInjectionQuery
|
||||
import semmle.code.csharp.dataflow.DataFlow::DataFlow::PathGraph
|
||||
import LdapInjection::PathGraph
|
||||
|
||||
from TaintTrackingConfiguration c, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where c.hasFlowPath(source, sink)
|
||||
from LdapInjection::PathNode source, LdapInjection::PathNode sink
|
||||
where LdapInjection::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This LDAP query depends on a $@.", source.getNode(),
|
||||
"user-provided value"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user