mirror of
https://github.com/github/codeql.git
synced 2026-07-21 03:08:25 +02:00
Merge branch 'main' into peewee-modeling
This commit is contained in:
@@ -17,3 +17,9 @@
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplCommon.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
|
||||
/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @github/codeql-java @github/codeql-go
|
||||
|
||||
# CodeQL tools and associated docs
|
||||
/docs/codeql-cli/ @github/codeql-cli-reviewers
|
||||
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||
/docs/ql-language-reference/ @github/codeql-frontend-reviewers
|
||||
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
|
||||
@@ -6,6 +6,7 @@
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl6.qll",
|
||||
"java/ql/src/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll",
|
||||
"cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll",
|
||||
@@ -448,5 +449,17 @@
|
||||
"SensitiveDataHeuristics Python/JS": [
|
||||
"javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
|
||||
"python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll"
|
||||
],
|
||||
"ReDoS Util Python/JS": [
|
||||
"javascript/ql/src/semmle/javascript/security/performance/ReDoSUtil.qll",
|
||||
"python/ql/src/semmle/python/security/performance/ReDoSUtil.qll"
|
||||
],
|
||||
"ReDoS Exponential Python/JS": [
|
||||
"javascript/ql/src/semmle/javascript/security/performance/ExponentialBackTracking.qll",
|
||||
"python/ql/src/semmle/python/security/performance/ExponentialBackTracking.qll"
|
||||
],
|
||||
"ReDoS Polynomial Python/JS": [
|
||||
"javascript/ql/src/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
|
||||
"python/ql/src/semmle/python/security/performance/SuperlinearBackTracking.qll"
|
||||
]
|
||||
}
|
||||
|
||||
2
cpp/change-notes/2021-06-24-uncontrolled-arithmetic.md
Normal file
2
cpp/change-notes/2021-06-24-uncontrolled-arithmetic.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm
|
||||
* The 'Uncontrolled data in arithmetic expression' (cpp/uncontrolled-arithmetic) query now recognizes more sources of randomness.
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* The 'Wrong type of arguments to formatting function' (cpp/wrong-type-format-argument) query is now more accepting of the string and character formatting differences between Microsoft and non-Microsoft platforms. There are now fewer false positive results.
|
||||
3
cpp/change-notes/2021-07-13-cleartext-storage-file.md
Normal file
3
cpp/change-notes/2021-07-13-cleartext-storage-file.md
Normal file
@@ -0,0 +1,3 @@
|
||||
lgtm,codescanning
|
||||
* The "Cleartext storage of sensitive information in file" (cpp/cleartext-storage-file) query now uses dataflow to produce additional results.
|
||||
* Heuristics in the SensitiveExprs.qll library have been improved, making the "Cleartext storage of sensitive information in file" (cpp/cleartext-storage-file), "Cleartext storage of sensitive information in buffer" (cpp/cleartext-storage-buffer) and "Cleartext storage of sensitive information in an SQLite" (cpp/cleartext-storage-database) queries more accurate.
|
||||
2
cpp/change-notes/2021-07-20-toctou-race-condition.md
Normal file
2
cpp/change-notes/2021-07-20-toctou-race-condition.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Improvements have been made to the `cpp/toctou-race-condition` query, both to find more correct results and fewer false positive results.
|
||||
2
cpp/change-notes/2021-07-27-uncontrolled-arithmetic.md
Normal file
2
cpp/change-notes/2021-07-27-uncontrolled-arithmetic.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm
|
||||
* Improvements made to the (`cpp/uncontrolled-arithmetic`) query, reducing the frequency of false positive results.
|
||||
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Virtual function specifiers are now accessible via the new predicates on `Function` (`.isDeclaredVirtual`, `.isOverride`, and `.isFinal`).
|
||||
2
cpp/change-notes/2021-08-10-has-trailing-return-type.md
Normal file
2
cpp/change-notes/2021-08-10-has-trailing-return-type.md
Normal file
@@ -0,0 +1,2 @@
|
||||
lgtm,codescanning
|
||||
* Added `Function.hasTrailingReturnType` predicate to check whether a function was declared with a trailing return type.
|
||||
@@ -7,10 +7,6 @@
|
||||
|
||||
import cpp
|
||||
|
||||
class AnonymousCompilation extends Compilation {
|
||||
override string toString() { result = "<compilation>" }
|
||||
}
|
||||
|
||||
string describe(Compilation c) {
|
||||
if c.getArgument(1) = "--mimic"
|
||||
then result = "compiler invocation " + concat(int i | i > 1 | c.getArgument(i), " " order by i)
|
||||
@@ -19,4 +15,4 @@ string describe(Compilation c) {
|
||||
|
||||
from Compilation c
|
||||
where not c.normalTermination()
|
||||
select c, "Extraction aborted for " + describe(c), 2
|
||||
select "Extraction aborted for " + describe(c)
|
||||
|
||||
@@ -19,28 +19,32 @@ import cpp
|
||||
* Holds if the argument corresponding to the `pos` conversion specifier
|
||||
* of `ffc` is expected to have type `expected`.
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate formattingFunctionCallExpectedType(
|
||||
FormattingFunctionCall ffc, int pos, Type expected
|
||||
) {
|
||||
exists(FormattingFunction f, int i, FormatLiteral fl |
|
||||
ffc instanceof FormattingFunctionCall and
|
||||
ffc.getTarget() = f and
|
||||
f.getFormatParameterIndex() = i and
|
||||
ffc.getArgument(i) = fl and
|
||||
fl.getConversionType(pos) = expected
|
||||
)
|
||||
ffc.getFormat().(FormatLiteral).getConversionType(pos) = expected
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the argument corresponding to the `pos` conversion specifier
|
||||
* of `ffc` is expected to have type `expected` and the corresponding
|
||||
* argument `arg` has type `actual`.
|
||||
* of `ffc` could alternatively have type `expected`, for example on a different
|
||||
* platform.
|
||||
*/
|
||||
private predicate formattingFunctionCallAlternateType(
|
||||
FormattingFunctionCall ffc, int pos, Type expected
|
||||
) {
|
||||
ffc.getFormat().(FormatLiteral).getConversionTypeAlternate(pos) = expected
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the argument corresponding to the `pos` conversion specifier
|
||||
* of `ffc` is `arg` and has type `actual`.
|
||||
*/
|
||||
pragma[noopt]
|
||||
predicate formatArgType(FormattingFunctionCall ffc, int pos, Type expected, Expr arg, Type actual) {
|
||||
predicate formattingFunctionCallActualType(
|
||||
FormattingFunctionCall ffc, int pos, Expr arg, Type actual
|
||||
) {
|
||||
exists(Expr argConverted |
|
||||
formattingFunctionCallExpectedType(ffc, pos, expected) and
|
||||
ffc.getConversionArgument(pos) = arg and
|
||||
argConverted = arg.getFullyConverted() and
|
||||
actual = argConverted.getType()
|
||||
@@ -72,7 +76,8 @@ class ExpectedType extends Type {
|
||||
ExpectedType() {
|
||||
exists(Type t |
|
||||
(
|
||||
formatArgType(_, _, t, _, _) or
|
||||
formattingFunctionCallExpectedType(_, _, t) or
|
||||
formattingFunctionCallAlternateType(_, _, t) or
|
||||
formatOtherArgType(_, _, t, _, _)
|
||||
) and
|
||||
this = t.getUnspecifiedType()
|
||||
@@ -91,7 +96,11 @@ class ExpectedType extends Type {
|
||||
*/
|
||||
predicate trivialConversion(ExpectedType expected, Type actual) {
|
||||
exists(Type exp, Type act |
|
||||
formatArgType(_, _, exp, _, act) and
|
||||
(
|
||||
formattingFunctionCallExpectedType(_, _, exp) or
|
||||
formattingFunctionCallAlternateType(_, _, exp)
|
||||
) and
|
||||
formattingFunctionCallActualType(_, _, _, act) and
|
||||
expected = exp.getUnspecifiedType() and
|
||||
actual = act.getUnspecifiedType()
|
||||
) and
|
||||
@@ -146,9 +155,13 @@ int sizeof_IntType() { exists(IntType it | result = it.getSize()) }
|
||||
from FormattingFunctionCall ffc, int n, Expr arg, Type expected, Type actual
|
||||
where
|
||||
(
|
||||
formatArgType(ffc, n, expected, arg, actual) and
|
||||
formattingFunctionCallExpectedType(ffc, n, expected) and
|
||||
formattingFunctionCallActualType(ffc, n, arg, actual) and
|
||||
not exists(Type anyExpected |
|
||||
formatArgType(ffc, n, anyExpected, arg, actual) and
|
||||
(
|
||||
formattingFunctionCallExpectedType(ffc, n, anyExpected) or
|
||||
formattingFunctionCallAlternateType(ffc, n, anyExpected)
|
||||
) and
|
||||
trivialConversion(anyExpected.getUnspecifiedType(), actual.getUnspecifiedType())
|
||||
)
|
||||
or
|
||||
|
||||
@@ -29,11 +29,19 @@ class ImproperNullTerminationReachability extends StackVariableReachabilityWithR
|
||||
override predicate isSourceActual(ControlFlowNode node, StackVariable v) {
|
||||
node = declWithNoInit(v)
|
||||
or
|
||||
exists(Call c, VariableAccess va |
|
||||
exists(Call c, int bufferArg, int sizeArg |
|
||||
c = node and
|
||||
c.getTarget().hasName("readlink") and
|
||||
c.getArgument(1) = va and
|
||||
va.getTarget() = v
|
||||
(
|
||||
c.getTarget().hasName("readlink") and bufferArg = 1 and sizeArg = 2
|
||||
or
|
||||
c.getTarget().hasName("readlinkat") and bufferArg = 2 and sizeArg = 3
|
||||
) and
|
||||
c.getArgument(bufferArg).(VariableAccess).getTarget() = v and
|
||||
(
|
||||
// buffer size parameter likely matches the full buffer size
|
||||
c.getArgument(sizeArg) instanceof SizeofOperator or
|
||||
c.getArgument(sizeArg).getValue().toInt() = v.getType().getSize()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ DeclStmt declWithNoInit(LocalVariable v) {
|
||||
result.getADeclaration() = v and
|
||||
not exists(v.getInitializer()) and
|
||||
/* The type of the variable is not stack-allocated. */
|
||||
not allocatedType(v.getType())
|
||||
exists(Type t | t = v.getType() | not allocatedType(t))
|
||||
}
|
||||
|
||||
class UninitialisedLocalReachability extends StackVariableReachability {
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
* @tags reliability
|
||||
* external/cwe/cwe-573
|
||||
* external/cwe/cwe-252
|
||||
* @opaque-id SM02344
|
||||
* @microsoft.severity Important
|
||||
*/
|
||||
|
||||
|
||||
@@ -15,58 +15,122 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.Overflow
|
||||
import semmle.code.cpp.security.Security
|
||||
import semmle.code.cpp.security.TaintTracking
|
||||
import TaintedWithPath
|
||||
import semmle.code.cpp.security.FlowSources
|
||||
import semmle.code.cpp.ir.dataflow.TaintTracking
|
||||
import DataFlow::PathGraph
|
||||
import Bounded
|
||||
|
||||
predicate isUnboundedRandCall(FunctionCall fc) {
|
||||
exists(Function func | func = fc.getTarget() |
|
||||
func.hasGlobalOrStdOrBslName("rand") and
|
||||
not bounded(fc) and
|
||||
func.getNumberOfParameters() = 0
|
||||
)
|
||||
/**
|
||||
* A function that outputs random data such as `std::rand`.
|
||||
*/
|
||||
abstract class RandomFunction extends Function {
|
||||
/**
|
||||
* Gets the `FunctionOutput` that describes how this function returns the random data.
|
||||
*/
|
||||
FunctionOutput getFunctionOutput() { result.isReturnValue() }
|
||||
}
|
||||
|
||||
predicate isUnboundedRandCallOrParent(Expr e) {
|
||||
isUnboundedRandCall(e)
|
||||
or
|
||||
isUnboundedRandCallOrParent(e.getAChild())
|
||||
}
|
||||
|
||||
predicate isUnboundedRandValue(Expr e) {
|
||||
isUnboundedRandCall(e)
|
||||
or
|
||||
exists(MacroInvocation mi |
|
||||
e = mi.getExpr() and
|
||||
isUnboundedRandCallOrParent(e)
|
||||
)
|
||||
}
|
||||
|
||||
class SecurityOptionsArith extends SecurityOptions {
|
||||
override predicate isUserInput(Expr expr, string cause) {
|
||||
isUnboundedRandValue(expr) and
|
||||
cause = "rand"
|
||||
/**
|
||||
* The standard function `std::rand`.
|
||||
*/
|
||||
private class StdRand extends RandomFunction {
|
||||
StdRand() {
|
||||
this.hasGlobalOrStdOrBslName("rand") and
|
||||
this.getNumberOfParameters() = 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Unix function `rand_r`.
|
||||
*/
|
||||
private class RandR extends RandomFunction {
|
||||
RandR() {
|
||||
this.hasGlobalName("rand_r") and
|
||||
this.getNumberOfParameters() = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Unix function `random`.
|
||||
*/
|
||||
private class Random extends RandomFunction {
|
||||
Random() {
|
||||
this.hasGlobalName("random") and
|
||||
this.getNumberOfParameters() = 1
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The Windows `rand_s` function.
|
||||
*/
|
||||
private class RandS extends RandomFunction {
|
||||
RandS() {
|
||||
this.hasGlobalName("rand_s") and
|
||||
this.getNumberOfParameters() = 1
|
||||
}
|
||||
|
||||
override FunctionOutput getFunctionOutput() { result.isParameterDeref(0) }
|
||||
}
|
||||
|
||||
predicate missingGuard(VariableAccess va, string effect) {
|
||||
exists(Operation op | op.getAnOperand() = va |
|
||||
missingGuardAgainstUnderflow(op, va) and effect = "underflow"
|
||||
// underflow - random numbers are usually non-negative, so underflow is
|
||||
// only likely if the type is unsigned. Multiplication is also unlikely to
|
||||
// cause underflow of a non-negative number.
|
||||
missingGuardAgainstUnderflow(op, va) and
|
||||
effect = "underflow" and
|
||||
op.getUnspecifiedType().(IntegralType).isUnsigned() and
|
||||
not op instanceof MulExpr
|
||||
or
|
||||
// overflow
|
||||
missingGuardAgainstOverflow(op, va) and effect = "overflow"
|
||||
)
|
||||
}
|
||||
|
||||
class Configuration extends TaintTrackingConfiguration {
|
||||
override predicate isSink(Element e) { missingGuard(e, _) }
|
||||
class UncontrolledArithConfiguration extends TaintTracking::Configuration {
|
||||
UncontrolledArithConfiguration() { this = "UncontrolledArithConfiguration" }
|
||||
|
||||
override predicate isBarrier(Expr e) { super.isBarrier(e) or bounded(e) }
|
||||
override predicate isSource(DataFlow::Node source) {
|
||||
exists(RandomFunction rand, Call call | call.getTarget() = rand |
|
||||
rand.getFunctionOutput().isReturnValue() and
|
||||
source.asExpr() = call
|
||||
or
|
||||
exists(int n |
|
||||
source.asDefiningArgument() = call.getArgument(n) and
|
||||
rand.getFunctionOutput().isParameterDeref(n)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { missingGuard(sink.asExpr(), _) }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node node) {
|
||||
bounded(node.asExpr())
|
||||
or
|
||||
// If this expression is part of bitwise 'and' or 'or' operation it's likely that the value is
|
||||
// only used as a bit pattern.
|
||||
node.asExpr() =
|
||||
any(Operation op |
|
||||
op instanceof BitwiseOrExpr or
|
||||
op instanceof BitwiseAndExpr or
|
||||
op instanceof ComplementExpr
|
||||
).getAnOperand*()
|
||||
or
|
||||
// block unintended flow to pointers
|
||||
node.asExpr().getUnspecifiedType() instanceof PointerType
|
||||
}
|
||||
}
|
||||
|
||||
from Expr origin, VariableAccess va, string effect, PathNode sourceNode, PathNode sinkNode
|
||||
/** Gets the expression that corresponds to `node`, if any. */
|
||||
Expr getExpr(DataFlow::Node node) { result = [node.asExpr(), node.asDefiningArgument()] }
|
||||
|
||||
from
|
||||
UncontrolledArithConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink,
|
||||
VariableAccess va, string effect
|
||||
where
|
||||
taintedWithPath(origin, va, sourceNode, sinkNode) and
|
||||
config.hasFlowPath(source, sink) and
|
||||
sink.getNode().asExpr() = va and
|
||||
missingGuard(va, effect)
|
||||
select va, sourceNode, sinkNode,
|
||||
"$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin,
|
||||
"Uncontrolled value"
|
||||
select sink.getNode(), source, sink,
|
||||
"$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".",
|
||||
getExpr(source.getNode()), "Uncontrolled value"
|
||||
|
||||
@@ -7,25 +7,6 @@ private import cpp
|
||||
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
/**
|
||||
* An operand `e` of a division expression (i.e., `e` is an operand of either a `DivExpr` or
|
||||
* a `AssignDivExpr`) is bounded when `e` is the left-hand side of the division.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate boundedDiv(Expr e, Expr left) { e = left }
|
||||
|
||||
/**
|
||||
* An operand `e` of a remainder expression `rem` (i.e., `rem` is either a `RemExpr` or
|
||||
* an `AssignRemExpr`) with left-hand side `left` and right-ahnd side `right` is bounded
|
||||
* when `e` is `left` and `right` is upper bounded by some number that is less than the maximum integer
|
||||
* allowed by the result type of `rem`.
|
||||
*/
|
||||
pragma[inline]
|
||||
private predicate boundedRem(Expr e, Expr rem, Expr left, Expr right) {
|
||||
e = left and
|
||||
upperBound(right.getFullyConverted()) < exprMaxVal(rem.getFullyConverted())
|
||||
}
|
||||
|
||||
/**
|
||||
* An operand `e` of a bitwise and expression `andExpr` (i.e., `andExpr` is either an `BitwiseAndExpr`
|
||||
* or an `AssignAndExpr`) with operands `operand1` and `operand2` is the operand that is not `e` is upper
|
||||
@@ -50,19 +31,10 @@ predicate bounded(Expr e) {
|
||||
) and
|
||||
not convertedExprMightOverflow(e)
|
||||
or
|
||||
// For `%` and `&` we require that `e` is bounded by a value that is strictly smaller than the
|
||||
// maximum possible value of the result type of the operation.
|
||||
// For example, the function call `rand()` is considered bounded in the following program:
|
||||
// ```
|
||||
// int i = rand() % (UINT8_MAX + 1);
|
||||
// ```
|
||||
// but not in:
|
||||
// ```
|
||||
// unsigned char uc = rand() % (UINT8_MAX + 1);
|
||||
// ```
|
||||
exists(RemExpr rem | boundedRem(e, rem, rem.getLeftOperand(), rem.getRightOperand()))
|
||||
// Optimitically assume that a remainder expression always yields a much smaller value.
|
||||
e = any(RemExpr rem).getLeftOperand()
|
||||
or
|
||||
exists(AssignRemExpr rem | boundedRem(e, rem, rem.getLValue(), rem.getRValue()))
|
||||
e = any(AssignRemExpr rem).getLValue()
|
||||
or
|
||||
exists(BitwiseAndExpr andExpr |
|
||||
boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand())
|
||||
@@ -73,11 +45,11 @@ predicate bounded(Expr e) {
|
||||
)
|
||||
or
|
||||
// Optimitically assume that a division always yields a much smaller value.
|
||||
boundedDiv(e, any(DivExpr div).getLeftOperand())
|
||||
e = any(DivExpr div).getLeftOperand()
|
||||
or
|
||||
boundedDiv(e, any(AssignDivExpr div).getLValue())
|
||||
e = any(AssignDivExpr div).getLValue()
|
||||
or
|
||||
boundedDiv(e, any(RShiftExpr shift).getLeftOperand())
|
||||
e = any(RShiftExpr shift).getLeftOperand()
|
||||
or
|
||||
boundedDiv(e, any(AssignRShiftExpr div).getLValue())
|
||||
e = any(AssignRShiftExpr div).getLValue()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.5
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @id cpp/cleartext-storage-file
|
||||
* @tags security
|
||||
* external/cwe/cwe-313
|
||||
@@ -14,10 +14,40 @@
|
||||
import cpp
|
||||
import semmle.code.cpp.security.SensitiveExprs
|
||||
import semmle.code.cpp.security.FileWrite
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
from FileWrite w, SensitiveExpr source, Expr dest
|
||||
/**
|
||||
* An operation on a filename.
|
||||
*/
|
||||
predicate filenameOperation(FunctionCall op, Expr path) {
|
||||
exists(string name | name = op.getTarget().getName() |
|
||||
name =
|
||||
[
|
||||
"remove", "unlink", "rmdir", "rename", "fopen", "open", "freopen", "_open", "_wopen",
|
||||
"_wfopen", "_fsopen", "_wfsopen", "chmod", "chown", "stat", "lstat", "fstat", "access",
|
||||
"_access", "_waccess", "_access_s", "_waccess_s"
|
||||
] and
|
||||
path = op.getArgument(0)
|
||||
or
|
||||
name = ["fopen_s", "wfopen_s", "rename"] and
|
||||
path = op.getArgument(1)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isFileName(GVN gvn) {
|
||||
exists(FunctionCall op, Expr path |
|
||||
filenameOperation(op, path) and
|
||||
gvn = globalValueNumber(path)
|
||||
)
|
||||
}
|
||||
|
||||
from FileWrite w, SensitiveExpr source, Expr mid, Expr dest
|
||||
where
|
||||
source = w.getASource() and
|
||||
dest = w.getDest()
|
||||
DataFlow::localFlow(DataFlow::exprNode(source), DataFlow::exprNode(mid)) and
|
||||
mid = w.getASource() and
|
||||
dest = w.getDest() and
|
||||
not isFileName(globalValueNumber(source)) and // file names are not passwords
|
||||
not exists(string convChar | convChar = w.getSourceConvChar(mid) | not convChar = ["s", "S"]) // ignore things written with other conversion characters
|
||||
select w, "This write into file '" + dest.toString() + "' may contain unencrypted data from $@",
|
||||
source, "this source."
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.7
|
||||
* @precision medium
|
||||
* @precision high
|
||||
* @id cpp/toctou-race-condition
|
||||
* @tags security
|
||||
* external/cwe/cwe-367
|
||||
@@ -16,59 +16,60 @@ import cpp
|
||||
import semmle.code.cpp.controlflow.Guards
|
||||
|
||||
/**
|
||||
* An operation on a filename.
|
||||
* An operation on a filename that is likely to modify the corresponding file
|
||||
* and may return an indication of success.
|
||||
*
|
||||
* Note: we're not interested in operations on file descriptors, as they
|
||||
* are better behaved.
|
||||
* Note: we're not interested in operations where the file is specified by a
|
||||
* descriptor, rather than a filename, as they are better behaved. We are
|
||||
* interested in functions that take a filename and return a file descriptor,
|
||||
* however.
|
||||
*/
|
||||
FunctionCall filenameOperation(Expr path) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
(
|
||||
name = "remove" or
|
||||
name = "unlink" or
|
||||
name = "rmdir" or
|
||||
name = "rename" or
|
||||
name = "chmod" or
|
||||
name = "chown" or
|
||||
name = "fopen" or
|
||||
name = "open" or
|
||||
name = "freopen" or
|
||||
name = "_open" or
|
||||
name = "_wopen" or
|
||||
name = "_wfopen"
|
||||
) and
|
||||
name =
|
||||
[
|
||||
"remove", "unlink", "rmdir", "rename", "fopen", "open", "freopen", "_open", "_wopen",
|
||||
"_wfopen", "_fsopen", "_wfsopen"
|
||||
] and
|
||||
result.getArgument(0) = path
|
||||
or
|
||||
(
|
||||
name = "fopen_s" or
|
||||
name = "wfopen_s"
|
||||
) and
|
||||
name = ["fopen_s", "wfopen_s", "rename"] and
|
||||
result.getArgument(1) = path
|
||||
)
|
||||
or
|
||||
result = sensitiveFilenameOperation(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* An operation on a filename that is likely to modify the security properties
|
||||
* of the corresponding file and may return an indication of success.
|
||||
*/
|
||||
FunctionCall sensitiveFilenameOperation(Expr path) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
name = ["chmod", "chown"] and
|
||||
result.getArgument(0) = path
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A use of `access` (or similar) on a filename.
|
||||
* An operation on a filename that returns information in the return value but
|
||||
* does not modify the corresponding file. For example, `access`.
|
||||
*/
|
||||
FunctionCall accessCheck(Expr path) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
name = "access" or
|
||||
name = "_access" or
|
||||
name = "_waccess" or
|
||||
name = "_access_s" or
|
||||
name = "_waccess_s"
|
||||
name = ["access", "_access", "_waccess", "_access_s", "_waccess_s"]
|
||||
) and
|
||||
path = result.getArgument(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* A use of `stat` (or similar) on a filename.
|
||||
* An operation on a filename that returns information via a pointer argument
|
||||
* and any return value, but does not modify the corresponding file. For
|
||||
* example, `stat`.
|
||||
*/
|
||||
FunctionCall stat(Expr path, Expr buf) {
|
||||
exists(string name | name = result.getTarget().getName() |
|
||||
name = "stat" or
|
||||
name = "lstat" or
|
||||
name = "fstat" or
|
||||
name = ["stat", "lstat", "fstat"] or
|
||||
name.matches("\\_stat%") or
|
||||
name.matches("\\_wstat%")
|
||||
) and
|
||||
@@ -77,7 +78,7 @@ FunctionCall stat(Expr path, Expr buf) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `use` points to `source`, either by being the same or by
|
||||
* Holds if `use` refers to `source`, either by being the same or by
|
||||
* one step of variable indirection.
|
||||
*/
|
||||
predicate referenceTo(Expr source, Expr use) {
|
||||
@@ -88,36 +89,45 @@ predicate referenceTo(Expr source, Expr use) {
|
||||
)
|
||||
}
|
||||
|
||||
from FunctionCall fc, Expr check, Expr checkUse, Expr opUse
|
||||
from Expr check, Expr checkPath, FunctionCall use, Expr usePath
|
||||
where
|
||||
// checkUse looks like a check on a filename
|
||||
// `check` looks like a check on a filename
|
||||
(
|
||||
// either:
|
||||
// an access check
|
||||
check = accessCheck(checkUse)
|
||||
or
|
||||
// a stat
|
||||
check = stat(checkUse, _)
|
||||
(
|
||||
// either:
|
||||
// an access check
|
||||
check = accessCheck(checkPath)
|
||||
or
|
||||
// a stat
|
||||
check = stat(checkPath, _)
|
||||
or
|
||||
// access to a member variable on the stat buf
|
||||
// (morally, this should be a use-use pair, but it seems unlikely
|
||||
// that this variable will get reused in practice)
|
||||
exists(Expr call, Expr e, Variable v |
|
||||
call = stat(checkPath, e) and
|
||||
e.getAChild*().(VariableAccess).getTarget() = v and
|
||||
check.(VariableAccess).getTarget() = v and
|
||||
not e.getAChild*() = check // the call that writes to the pointer is not where the pointer is checked.
|
||||
)
|
||||
) and
|
||||
// `op` looks like an operation on a filename
|
||||
use = filenameOperation(usePath)
|
||||
or
|
||||
// another filename operation (null pointers can indicate errors)
|
||||
check = filenameOperation(checkUse)
|
||||
or
|
||||
// access to a member variable on the stat buf
|
||||
// (morally, this should be a use-use pair, but it seems unlikely
|
||||
// that this variable will get reused in practice)
|
||||
exists(Variable buf | exists(stat(checkUse, buf.getAnAccess())) |
|
||||
check.(VariableAccess).getQualifier() = buf.getAnAccess()
|
||||
)
|
||||
check = filenameOperation(checkPath) and
|
||||
// `op` looks like a sensitive operation on a filename
|
||||
use = sensitiveFilenameOperation(usePath)
|
||||
) and
|
||||
// checkUse and opUse refer to the same SSA variable
|
||||
exists(SsaDefinition def, StackVariable v | def.getAUse(v) = checkUse and def.getAUse(v) = opUse) and
|
||||
// opUse looks like an operation on a filename
|
||||
fc = filenameOperation(opUse) and
|
||||
// the return value of check is used (possibly with one step of
|
||||
// variable indirection) in a guard which controls fc
|
||||
// `checkPath` and `usePath` refer to the same SSA variable
|
||||
exists(SsaDefinition def, StackVariable v |
|
||||
def.getAUse(v) = checkPath and def.getAUse(v) = usePath
|
||||
) and
|
||||
// the return value of `check` is used (possibly with one step of
|
||||
// variable indirection) in a guard which controls `use`
|
||||
exists(GuardCondition guard | referenceTo(check, guard.getAChild*()) |
|
||||
guard.controls(fc.(ControlFlowNode).getBasicBlock(), _)
|
||||
guard.controls(use.(ControlFlowNode).getBasicBlock(), _)
|
||||
)
|
||||
select fc,
|
||||
select use,
|
||||
"The $@ being operated upon was previously $@, but the underlying file may have been changed since then.",
|
||||
opUse, "filename", check, "checked"
|
||||
usePath, "filename", check, "checked"
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @security-severity 7.8
|
||||
* @opaque-id SM02313
|
||||
* @id cpp/conditionally-uninitialized-variable
|
||||
* @tags security
|
||||
* external/cwe/cwe-457
|
||||
|
||||
@@ -182,7 +182,7 @@ class ThrowingAllocator extends Function {
|
||||
// 3. the allocator isn't marked with `throw()` or `noexcept`.
|
||||
not exists(this.getBlock()) and
|
||||
not exists(Parameter p | p = this.getAParameter() |
|
||||
p.getUnspecifiedType() instanceof NoThrowType
|
||||
p.getUnspecifiedType().stripType() instanceof NoThrowType
|
||||
) and
|
||||
not this.isNoExcept() and
|
||||
not this.isNoThrow()
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
* @description The total number of lines of C/C++ code across all files, including system headers, libraries, and auto-generated files. This is a useful metric of the size of a database. For all files that were seen during the build, this query counts the lines of code, excluding whitespace or comments.
|
||||
* @kind metric
|
||||
* @tags summary
|
||||
* lines-of-code
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
...
|
||||
int i1;
|
||||
char c1;
|
||||
...
|
||||
if((c1<50)&&(c>10))
|
||||
switch(c1){
|
||||
case 300: // BAD: the code will not be executed
|
||||
...
|
||||
if((i1<5)&&(i1>0))
|
||||
switch(i1){ // BAD
|
||||
case 21: // BAD: the code will not be executed
|
||||
...
|
||||
switch(c1){
|
||||
...
|
||||
dafault: // BAD: maybe it will be right `default`
|
||||
...
|
||||
}
|
||||
|
||||
...
|
||||
switch(c1){
|
||||
i1=c1*2; // BAD: the code will not be executed
|
||||
case 12:
|
||||
...
|
||||
switch(c1){ // GOOD
|
||||
case 12:
|
||||
break;
|
||||
case 10:
|
||||
break;
|
||||
case 9:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
...
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>A mismatch between conditionals and <code>switch</code> cases can lead to control-flow violations (CWE-691) where the developer either does not handle all combinations of conditions or unintentionally created dead code (CWE-561).</p>
|
||||
|
||||
|
||||
</overview>
|
||||
|
||||
<example>
|
||||
<p>The following example demonstrates fallacious and fixed ways of using a <code>switch</code> statement.</p>
|
||||
<sample src="FindIncorrectlyUsedSwitch.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/MSC12-C.+Detect+and+remove+code+that+has+no+effect+or+is+never+executed">MSC12-C. Detect and remove code that has no effect or is never executed</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* @name Incorrect switch statement
|
||||
* @description --Finding places the dangerous use of a switch.
|
||||
* --For example, when the range of values for a condition does not cover all of the selection values..
|
||||
* @kind problem
|
||||
* @id cpp/operator-find-incorrectly-used-switch
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-561
|
||||
* external/cwe/cwe-691
|
||||
* external/cwe/cwe-478
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
/** Holds if the range contains no boundary values. */
|
||||
predicate isRealRange(Expr exp) {
|
||||
upperBound(exp).toString() != "18446744073709551616" and
|
||||
upperBound(exp).toString() != "9223372036854775807" and
|
||||
upperBound(exp).toString() != "4294967295" and
|
||||
upperBound(exp).toString() != "Infinity" and
|
||||
upperBound(exp).toString() != "NaN" and
|
||||
lowerBound(exp).toString() != "-9223372036854775808" and
|
||||
lowerBound(exp).toString() != "-4294967296" and
|
||||
lowerBound(exp).toString() != "-Infinity" and
|
||||
lowerBound(exp).toString() != "NaN" and
|
||||
upperBound(exp) != 2147483647 and
|
||||
upperBound(exp) != 268435455 and
|
||||
upperBound(exp) != 33554431 and
|
||||
upperBound(exp) != 8388607 and
|
||||
upperBound(exp) != 65535 and
|
||||
upperBound(exp) != 32767 and
|
||||
upperBound(exp) != 255 and
|
||||
upperBound(exp) != 127 and
|
||||
upperBound(exp) != 63 and
|
||||
upperBound(exp) != 31 and
|
||||
upperBound(exp) != 15 and
|
||||
upperBound(exp) != 7 and
|
||||
lowerBound(exp) != -2147483648 and
|
||||
lowerBound(exp) != -268435456 and
|
||||
lowerBound(exp) != -33554432 and
|
||||
lowerBound(exp) != -8388608 and
|
||||
lowerBound(exp) != -65536 and
|
||||
lowerBound(exp) != -32768 and
|
||||
lowerBound(exp) != -128
|
||||
}
|
||||
|
||||
/** Holds if the range of values for the condition is less than the choices. */
|
||||
predicate isNotAllSelected(SwitchStmt swtmp) {
|
||||
not swtmp.getExpr().isConstant() and
|
||||
exists(int i |
|
||||
i != 0 and
|
||||
(
|
||||
i = lowerBound(swtmp.getASwitchCase().getExpr()) and
|
||||
upperBound(swtmp.getExpr()) < i
|
||||
or
|
||||
(
|
||||
i = upperBound(swtmp.getASwitchCase().getExpr()) or
|
||||
i = upperBound(swtmp.getASwitchCase().getEndExpr())
|
||||
) and
|
||||
lowerBound(swtmp.getExpr()) > i
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the range of values for the condition is greater than the selection. */
|
||||
predicate isConditionBig(SwitchStmt swtmp) {
|
||||
not swtmp.hasDefaultCase() and
|
||||
not exists(int iu, int il |
|
||||
(
|
||||
iu = upperBound(swtmp.getASwitchCase().getExpr()) or
|
||||
iu = upperBound(swtmp.getASwitchCase().getEndExpr())
|
||||
) and
|
||||
upperBound(swtmp.getExpr()) = iu and
|
||||
(
|
||||
il = lowerBound(swtmp.getASwitchCase().getExpr()) or
|
||||
il = lowerBound(swtmp.getASwitchCase().getEndExpr())
|
||||
) and
|
||||
lowerBound(swtmp.getExpr()) = il
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if there are labels inside the block with names similar to `default` or `case`. */
|
||||
predicate isWrongLableName(SwitchStmt swtmp) {
|
||||
not swtmp.hasDefaultCase() and
|
||||
exists(LabelStmt lb |
|
||||
(
|
||||
(
|
||||
lb.getName().charAt(0) = "d" or
|
||||
lb.getName().charAt(0) = "c"
|
||||
) and
|
||||
(
|
||||
lb.getName().charAt(1) = "e" or
|
||||
lb.getName().charAt(1) = "a"
|
||||
) and
|
||||
(
|
||||
lb.getName().charAt(2) = "f" or
|
||||
lb.getName().charAt(2) = "s"
|
||||
)
|
||||
) and
|
||||
lb.getEnclosingStmt().getParentStmt*() = swtmp.getStmt() and
|
||||
not exists(GotoStmt gs | gs.getName() = lb.getName())
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the block contains code before the first `case`. */
|
||||
predicate isCodeBeforeCase(SwitchStmt swtmp) {
|
||||
exists(Expr exp |
|
||||
exp.getEnclosingStmt().getParentStmt*() = swtmp.getStmt() and
|
||||
not exists(Loop lp |
|
||||
exp.getEnclosingStmt().getParentStmt*() = lp and
|
||||
lp.getEnclosingStmt().getParentStmt*() = swtmp.getStmt()
|
||||
) and
|
||||
not exists(Stmt sttmp, SwitchCase sctmp |
|
||||
sttmp = swtmp.getASwitchCase().getAStmt() and
|
||||
sctmp = swtmp.getASwitchCase() and
|
||||
(
|
||||
exp.getEnclosingStmt().getParentStmt*() = sttmp or
|
||||
exp.getEnclosingStmt() = sctmp
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from SwitchStmt sw, string msg
|
||||
where
|
||||
isRealRange(sw.getExpr()) and
|
||||
lowerBound(sw.getExpr()) != upperBound(sw.getExpr()) and
|
||||
lowerBound(sw.getExpr()) != 0 and
|
||||
not exists(Expr cexp |
|
||||
cexp = sw.getASwitchCase().getExpr() and not isRealRange(cexp)
|
||||
or
|
||||
cexp = sw.getASwitchCase().getEndExpr() and not isRealRange(cexp)
|
||||
) and
|
||||
not exists(Expr exptmp |
|
||||
exptmp = sw.getExpr().getAChild*() and
|
||||
not exptmp.isConstant() and
|
||||
not isRealRange(exptmp)
|
||||
) and
|
||||
(sw.getASwitchCase().terminatesInBreakStmt() or sw.getASwitchCase().terminatesInReturnStmt()) and
|
||||
(
|
||||
isNotAllSelected(sw) and msg = "The range of condition values is less than the selection."
|
||||
or
|
||||
isConditionBig(sw) and msg = "The range of condition values is wider than the choices."
|
||||
)
|
||||
or
|
||||
isWrongLableName(sw) and msg = "Possibly erroneous label name."
|
||||
or
|
||||
isCodeBeforeCase(sw) and msg = "Code before case will not be executed."
|
||||
select sw, msg
|
||||
@@ -0,0 +1,9 @@
|
||||
...
|
||||
throw ("my exception!",546); // BAD
|
||||
...
|
||||
throw errorFunc("my exception!",546); // GOOD
|
||||
...
|
||||
std::runtime_error("msg error"); // BAD
|
||||
...
|
||||
throw std::runtime_error("msg error"); // GOOD
|
||||
...
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Finding places for the dangerous use of exceptions.</p>
|
||||
|
||||
</overview>
|
||||
|
||||
<example>
|
||||
<p>The following example demonstrates erroneous and fixed methods for using exceptions.</p>
|
||||
<sample src="FindIncorrectlyUsedExceptions.cpp" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT CPP Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/cplusplus/DCL57-CPP.+Do+not+let+exceptions+escape+from+destructors+or+deallocation+functions">DCL57-CPP. Do not let exceptions escape from destructors or deallocation functions</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @name Operator Find Incorrectly Used Exceptions
|
||||
* @description --Finding places for the dangerous use of exceptions.
|
||||
* @kind problem
|
||||
* @id cpp/operator-find-incorrectly-used-exceptions
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags correctness
|
||||
* security
|
||||
* external/cwe/cwe-703
|
||||
* external/cwe/cwe-248
|
||||
* external/cwe/cwe-390
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
from FunctionCall fc, string msg
|
||||
where
|
||||
exists(ThrowExpr texp |
|
||||
texp.getEnclosingFunction() = fc.getTarget() and
|
||||
(
|
||||
fc.getTarget().hasGlobalOrStdName("DllMain") and
|
||||
not exists(TryStmt ts |
|
||||
texp.getEnclosingStmt().getParentStmt*() = ts.getStmt() and
|
||||
not ts.getACatchClause().isEmpty()
|
||||
) and
|
||||
msg = "DllMain contains an exeption not wrapped in a try..catch block."
|
||||
or
|
||||
texp.getExpr().isParenthesised() and
|
||||
texp.getExpr().(CommaExpr).getLeftOperand().isConstant() and
|
||||
texp.getExpr().(CommaExpr).getRightOperand().isConstant() and
|
||||
msg = "There is an exception in the function that requires your attention."
|
||||
)
|
||||
)
|
||||
or
|
||||
fc.getTarget() instanceof Constructor and
|
||||
(
|
||||
fc.getTargetType().(Class).getABaseClass+().hasGlobalOrStdName("exception") or
|
||||
fc.getTargetType().(Class).getABaseClass+().hasGlobalOrStdName("CException")
|
||||
) and
|
||||
not fc.isInMacroExpansion() and
|
||||
not exists(ThrowExpr texp | fc.getEnclosingStmt() = texp.getEnclosingStmt()) and
|
||||
not exists(FunctionCall fctmp | fctmp.getAnArgument() = fc) and
|
||||
not fc instanceof ConstructorDirectInit and
|
||||
not fc.getEnclosingStmt() instanceof DeclStmt and
|
||||
not fc instanceof ConstructorDelegationInit and
|
||||
not fc.getParent() instanceof Initializer and
|
||||
not fc.getParent() instanceof AllocationExpr and
|
||||
msg = "This object does not generate an exception."
|
||||
select fc, msg
|
||||
@@ -0,0 +1,12 @@
|
||||
intA = ++intA + 1; // BAD: undefined behavior when changing variable `intA`
|
||||
...
|
||||
intA++;
|
||||
intA = intA + 1; // GOOD: correct design
|
||||
...
|
||||
char * buff;
|
||||
...
|
||||
if(funcAdd(buff)+fucDel(buff)>0) return 1; // BAD: undefined behavior when calling functions to change the `buff` variable
|
||||
...
|
||||
intA = funcAdd(buff);
|
||||
intB = funcDel(buff);
|
||||
if(intA+intB>0) return 1; // GOOD: correct design
|
||||
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>In some situations, the code constructs used may be executed in the wrong order in which the developer designed them. For example, if you call multiple functions as part of a single expression, and the functions have the ability to modify a shared resource, then the sequence in which the resource is changed can be unpredictable. These code snippets look suspicious and require the developer's attention.</p>
|
||||
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>We recommend that you use more guaranteed, in terms of sequence of execution, coding techniques.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates sections of code with insufficient execution sequence definition.</p>
|
||||
<sample src="UndefinedOrImplementationDefinedBehavior.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CWE Common Weakness Enumeration:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/EXP10-C.+Do+not+depend+on+the+order+of+evaluation+of+subexpressions+or+the+order+in+which+side+effects+take+place"> EXP10-C. Do not depend on the order of evaluation of subexpressions or the order in which side effects take place</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,167 @@
|
||||
/**
|
||||
* @name Errors Of Undefined Program Behavior
|
||||
* @description --In some situations, the code constructs used may be executed in the wrong order in which the developer designed them.
|
||||
* --For example, if you call multiple functions as part of a single expression, and the functions have the ability to modify a shared resource, then the sequence in which the resource is changed can be unpredictable.
|
||||
* --These code snippets look suspicious and require the developer's attention.
|
||||
* @kind problem
|
||||
* @id cpp/errors-of-undefined-program-behavior
|
||||
* @problem.severity warning
|
||||
* @precision medium
|
||||
* @tags security
|
||||
* external/cwe/cwe-758
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.valuenumbering.HashCons
|
||||
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
|
||||
/**
|
||||
* Threatening expressions of undefined behavior.
|
||||
*/
|
||||
class ExpressionsOfTheSameLevel extends Expr {
|
||||
Expr exp2;
|
||||
|
||||
ExpressionsOfTheSameLevel() {
|
||||
this != exp2 and
|
||||
this.getParent() = exp2.getParent()
|
||||
}
|
||||
|
||||
/** Holds if the underlying expression is a function call. */
|
||||
predicate expressionCall() {
|
||||
this instanceof FunctionCall and
|
||||
exp2.getAChild*() instanceof FunctionCall and
|
||||
not this.getParent() instanceof Operator and
|
||||
not this.(FunctionCall).hasQualifier()
|
||||
}
|
||||
|
||||
/** Holds if the underlying expression is a call to a function to free resources. */
|
||||
predicate existsCloseOrFreeCall() {
|
||||
(
|
||||
globalValueNumber(this.(FunctionCall).getAnArgument()) =
|
||||
globalValueNumber(exp2.getAChild*().(FunctionCall).getAnArgument()) or
|
||||
hashCons(this.(FunctionCall).getAnArgument()) =
|
||||
hashCons(exp2.getAChild*().(FunctionCall).getAnArgument())
|
||||
) and
|
||||
(
|
||||
this.(FunctionCall).getTarget().hasGlobalOrStdName("close") or
|
||||
this.(FunctionCall).getTarget().hasGlobalOrStdName("free") or
|
||||
this.(FunctionCall).getTarget().hasGlobalOrStdName("fclose")
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the arguments in the function can be changed. */
|
||||
predicate generalArgumentDerivedType() {
|
||||
exists(Parameter prt1, Parameter prt2, AssignExpr aet1, AssignExpr aet2, int i, int j |
|
||||
not this.(FunctionCall).getArgument(i).isConstant() and
|
||||
hashCons(this.(FunctionCall).getArgument(i)) =
|
||||
hashCons(exp2.getAChild*().(FunctionCall).getArgument(j)) and
|
||||
prt1 = this.(FunctionCall).getTarget().getParameter(i) and
|
||||
prt2 = exp2.getAChild*().(FunctionCall).getTarget().getParameter(j) and
|
||||
prt1.getType() instanceof DerivedType and
|
||||
(
|
||||
aet1 = this.(FunctionCall).getTarget().getEntryPoint().getASuccessor*() and
|
||||
(
|
||||
aet1.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() =
|
||||
prt1.getAnAccess().getTarget() or
|
||||
aet1.getLValue().(VariableAccess).getTarget() = prt1.getAnAccess().getTarget()
|
||||
)
|
||||
or
|
||||
exists(FunctionCall fc1 |
|
||||
fc1.getTarget().hasGlobalName("memcpy") and
|
||||
fc1.getArgument(0).(VariableAccess).getTarget() = prt1.getAnAccess().getTarget() and
|
||||
fc1 = this.(FunctionCall).getTarget().getEntryPoint().getASuccessor*()
|
||||
)
|
||||
) and
|
||||
(
|
||||
aet2 = exp2.getAChild*().(FunctionCall).getTarget().getEntryPoint().getASuccessor*() and
|
||||
(
|
||||
aet2.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() =
|
||||
prt2.getAnAccess().getTarget() or
|
||||
aet2.getLValue().(VariableAccess).getTarget() = prt2.getAnAccess().getTarget()
|
||||
)
|
||||
or
|
||||
exists(FunctionCall fc1 |
|
||||
fc1.getTarget().hasGlobalName("memcpy") and
|
||||
fc1.getArgument(0).(VariableAccess).getTarget() = prt2.getAnAccess().getTarget() and
|
||||
fc1 = exp2.(FunctionCall).getTarget().getEntryPoint().getASuccessor*()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if functions have a common global argument. */
|
||||
predicate generalGlobalArgument() {
|
||||
exists(Declaration dl, AssignExpr aet1, AssignExpr aet2 |
|
||||
dl instanceof GlobalVariable and
|
||||
(
|
||||
(
|
||||
aet1.getLValue().(Access).getTarget() = dl or
|
||||
aet1.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = dl
|
||||
) and
|
||||
aet1 = this.(FunctionCall).getTarget().getEntryPoint().getASuccessor*() and
|
||||
not aet1.getRValue().isConstant()
|
||||
or
|
||||
exists(FunctionCall fc1 |
|
||||
fc1.getTarget().hasGlobalName("memcpy") and
|
||||
fc1.getArgument(0).(VariableAccess).getTarget() = dl and
|
||||
fc1 = this.(FunctionCall).getTarget().getEntryPoint().getASuccessor*()
|
||||
)
|
||||
) and
|
||||
(
|
||||
(
|
||||
aet2.getLValue().(Access).getTarget() = dl or
|
||||
aet2.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = dl
|
||||
) and
|
||||
aet2 = exp2.(FunctionCall).getTarget().getEntryPoint().getASuccessor*()
|
||||
or
|
||||
exists(FunctionCall fc1 |
|
||||
fc1.getTarget().hasGlobalName("memcpy") and
|
||||
fc1.getArgument(0).(VariableAccess).getTarget() = dl and
|
||||
fc1 = exp2.(FunctionCall).getTarget().getEntryPoint().getASuccessor*()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if sequence point is not present in expression. */
|
||||
predicate orderOfActionExpressions() {
|
||||
not this.getParent() instanceof BinaryLogicalOperation and
|
||||
not this.getParent() instanceof ConditionalExpr and
|
||||
not this.getParent() instanceof Loop and
|
||||
not this.getParent() instanceof CommaExpr
|
||||
}
|
||||
|
||||
/** Holds if expression is crement. */
|
||||
predicate dangerousCrementChanges() {
|
||||
hashCons(this.(CrementOperation).getOperand()) = hashCons(exp2.(CrementOperation).getOperand())
|
||||
or
|
||||
hashCons(this.(CrementOperation).getOperand()) = hashCons(exp2)
|
||||
or
|
||||
hashCons(this.(CrementOperation).getOperand()) = hashCons(exp2.(ArrayExpr).getArrayOffset())
|
||||
or
|
||||
hashCons(this.(Assignment).getLValue()) = hashCons(exp2.(Assignment).getLValue())
|
||||
or
|
||||
not this.getAChild*() instanceof Call and
|
||||
(
|
||||
hashCons(this.getAChild*().(CrementOperation).getOperand()) = hashCons(exp2) or
|
||||
hashCons(this.getAChild*().(CrementOperation).getOperand()) =
|
||||
hashCons(exp2.(Assignment).getLValue())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from ExpressionsOfTheSameLevel eots
|
||||
where
|
||||
eots.orderOfActionExpressions() and
|
||||
(
|
||||
eots.expressionCall() and
|
||||
(
|
||||
eots.generalArgumentDerivedType() or
|
||||
eots.generalGlobalArgument() or
|
||||
eots.existsCloseOrFreeCall()
|
||||
)
|
||||
or
|
||||
eots.dangerousCrementChanges()
|
||||
)
|
||||
select eots,
|
||||
"This expression may have undefined behavior, because the order of evaluation is not specified."
|
||||
@@ -0,0 +1,7 @@
|
||||
bool a=1,b=0,c=1,res;
|
||||
...
|
||||
res = a||b^c; // BAD: possible priority error `res==1`
|
||||
...
|
||||
res = a||(b^c); // GOOD: `res==1`
|
||||
...
|
||||
res = (a||b)^c; // GOOD: `res==0`
|
||||
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Find places of confusing use of logical and bitwise operations.</p>
|
||||
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>We recommend using parentheses to explicitly emphasize priority.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>The following example demonstrates fallacious and fixed methods of using logical and bitwise operations.</p>
|
||||
<sample src="OperatorPrecedenceLogicErrorWhenUseBitwiseOrLogicalOperations.c" />
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>
|
||||
CERT C Coding Standard:
|
||||
<a href="https://wiki.sei.cmu.edu/confluence/display/c/EXP00-C.+Use+parentheses+for+precedence+of+operation">EXP00-C. Use parentheses for precedence of operation</a>.
|
||||
</li>
|
||||
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* @name Operator Precedence Logic Error When Use Bitwise Or Logical Operations
|
||||
* @description --Finding places to use bit and logical operations, without explicit priority allocation.
|
||||
* --For example, `a || b ^ c` and `(a || b) ^ c` give different results when `b` is zero.
|
||||
* @kind problem
|
||||
* @id cpp/operator-precedence-logic-error-when-use-bitwise-logical-operations
|
||||
* @problem.severity recommendation
|
||||
* @precision medium
|
||||
* @tags maintainability
|
||||
* readability
|
||||
* external/cwe/cwe-783
|
||||
* external/cwe/cwe-480
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
|
||||
/** Holds if `exptmp` equals expression logical or followed by logical and. */
|
||||
predicate isLogicalOrAndExpr(LogicalOrExpr exptmp) {
|
||||
not exptmp.getLeftOperand() instanceof BinaryOperation and
|
||||
not exptmp.getRightOperand().isParenthesised() and
|
||||
exptmp.getRightOperand() instanceof LogicalAndExpr
|
||||
}
|
||||
|
||||
/** Holds if `exptmp` equals expression logical or followed by bit operation. */
|
||||
predicate isLogicalOrandBitwise(Expr exptmp) {
|
||||
not exptmp.(LogicalOrExpr).getLeftOperand() instanceof BinaryOperation and
|
||||
not exptmp.(LogicalOrExpr).getRightOperand().isParenthesised() and
|
||||
(
|
||||
exptmp.(LogicalOrExpr).getRightOperand().(BinaryBitwiseOperation).getLeftOperand().getType()
|
||||
instanceof BoolType and
|
||||
// The essence of these lines is to improve the quality of detection by eliminating the situation
|
||||
// of processing a logical type by bit operations. In fact, the predicate looks for a situation
|
||||
// when the left operand of a bit operation has a boolean type, which already suggests that the priority is not correct.
|
||||
// But if the right-hand operand is 0 or 1, then there is a possibility that the author intended so.
|
||||
not exptmp
|
||||
.(LogicalOrExpr)
|
||||
.getRightOperand()
|
||||
.(BinaryBitwiseOperation)
|
||||
.getRightOperand()
|
||||
.getValue() = "0" and
|
||||
not exptmp
|
||||
.(LogicalOrExpr)
|
||||
.getRightOperand()
|
||||
.(BinaryBitwiseOperation)
|
||||
.getRightOperand()
|
||||
.getValue() = "1"
|
||||
)
|
||||
or
|
||||
not exptmp.(LogicalAndExpr).getLeftOperand() instanceof BinaryOperation and
|
||||
not exptmp.(LogicalAndExpr).getRightOperand().isParenthesised() and
|
||||
(
|
||||
exptmp.(LogicalAndExpr).getRightOperand().(BinaryBitwiseOperation).getLeftOperand().getType()
|
||||
instanceof BoolType and
|
||||
// Looking for a situation in which the right-hand operand of a bit operation is not limited to 0 or 1.
|
||||
// In this case, the logical operation will be performed with the result of a binary operation that is not a Boolean type.
|
||||
// In my opinion this indicates a priority error. after all, it will be quite difficult for a developer
|
||||
// to evaluate the conversion of the results of a bit operation to a boolean type.
|
||||
not exptmp
|
||||
.(LogicalAndExpr)
|
||||
.getRightOperand()
|
||||
.(BinaryBitwiseOperation)
|
||||
.getRightOperand()
|
||||
.getValue() = "0" and
|
||||
not exptmp
|
||||
.(LogicalAndExpr)
|
||||
.getRightOperand()
|
||||
.(BinaryBitwiseOperation)
|
||||
.getRightOperand()
|
||||
.getValue() = "1"
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `exptmp` equals expression bit operations in reverse priority order. */
|
||||
predicate isBitwiseandBitwise(Expr exptmp) {
|
||||
not exptmp.(BitwiseOrExpr).getLeftOperand() instanceof BinaryOperation and
|
||||
not exptmp.(BitwiseOrExpr).getRightOperand().isParenthesised() and
|
||||
(
|
||||
exptmp.(BitwiseOrExpr).getRightOperand() instanceof BitwiseAndExpr or
|
||||
exptmp.(BitwiseOrExpr).getRightOperand() instanceof BitwiseXorExpr
|
||||
)
|
||||
or
|
||||
not exptmp.(BitwiseXorExpr).getLeftOperand() instanceof BinaryOperation and
|
||||
not exptmp.(BitwiseXorExpr).getRightOperand().isParenthesised() and
|
||||
exptmp.(BitwiseXorExpr).getRightOperand() instanceof BitwiseAndExpr
|
||||
}
|
||||
|
||||
/** Holds if the range contains no boundary values. */
|
||||
predicate isRealRange(Expr exp) {
|
||||
upperBound(exp).toString() != "18446744073709551616" and
|
||||
upperBound(exp).toString() != "9223372036854775807" and
|
||||
upperBound(exp).toString() != "4294967295" and
|
||||
upperBound(exp).toString() != "Infinity" and
|
||||
upperBound(exp).toString() != "NaN" and
|
||||
lowerBound(exp).toString() != "-9223372036854775808" and
|
||||
lowerBound(exp).toString() != "-4294967296" and
|
||||
lowerBound(exp).toString() != "-Infinity" and
|
||||
lowerBound(exp).toString() != "NaN" and
|
||||
upperBound(exp) != 2147483647 and
|
||||
upperBound(exp) != 268435455 and
|
||||
upperBound(exp) != 33554431 and
|
||||
upperBound(exp) != 8388607 and
|
||||
upperBound(exp) != 65535 and
|
||||
upperBound(exp) != 32767 and
|
||||
upperBound(exp) != 255 and
|
||||
upperBound(exp) != 127 and
|
||||
lowerBound(exp) != -2147483648 and
|
||||
lowerBound(exp) != -268435456 and
|
||||
lowerBound(exp) != -33554432 and
|
||||
lowerBound(exp) != -8388608 and
|
||||
lowerBound(exp) != -65536 and
|
||||
lowerBound(exp) != -32768 and
|
||||
lowerBound(exp) != -128
|
||||
or
|
||||
lowerBound(exp) = 0 and
|
||||
upperBound(exp) = 1
|
||||
}
|
||||
|
||||
/** Holds if expressions are of different size or range */
|
||||
pragma[inline]
|
||||
predicate isDifferentSize(Expr exp1, Expr exp2, Expr exp3) {
|
||||
exp1.getType().getSize() = exp2.getType().getSize() and
|
||||
exp1.getType().getSize() != exp3.getType().getSize()
|
||||
or
|
||||
(
|
||||
isRealRange(exp1) and
|
||||
isRealRange(exp2) and
|
||||
isRealRange(exp3)
|
||||
) and
|
||||
upperBound(exp1).maximum(upperBound(exp2)) - upperBound(exp1).minimum(upperBound(exp2)) < 16 and
|
||||
lowerBound(exp1).maximum(lowerBound(exp2)) - lowerBound(exp1).minimum(lowerBound(exp2)) < 16 and
|
||||
(
|
||||
upperBound(exp1).maximum(upperBound(exp3)) - upperBound(exp1).minimum(upperBound(exp3)) > 256 or
|
||||
lowerBound(exp1).maximum(lowerBound(exp2)) - lowerBound(exp1).minimum(lowerBound(exp2)) > 256
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if different values of the expression obtained from the parameters of the predicate can be obtained. */
|
||||
pragma[inline]
|
||||
predicate isDifferentResults(
|
||||
Expr exp1, Expr exp2, Expr exp3, BinaryBitwiseOperation op1, BinaryBitwiseOperation op2
|
||||
) {
|
||||
(
|
||||
isRealRange(exp1) and
|
||||
isRealRange(exp2) and
|
||||
isRealRange(exp3)
|
||||
) and
|
||||
exists(int i1, int i2, int i3 |
|
||||
i1 in [lowerBound(exp1).floor() .. upperBound(exp1).floor()] and
|
||||
i2 in [lowerBound(exp2).floor() .. upperBound(exp2).floor()] and
|
||||
i3 in [lowerBound(exp3).floor() .. upperBound(exp3).floor()] and
|
||||
(
|
||||
op1 instanceof BitwiseOrExpr and
|
||||
op2 instanceof BitwiseAndExpr and
|
||||
i1.bitOr(i2).bitAnd(i3) != i2.bitAnd(i3).bitOr(i1)
|
||||
or
|
||||
op1 instanceof BitwiseOrExpr and
|
||||
op2 instanceof BitwiseXorExpr and
|
||||
i1.bitOr(i2).bitXor(i3) != i2.bitXor(i3).bitOr(i1)
|
||||
or
|
||||
op1 instanceof BitwiseXorExpr and
|
||||
op2 instanceof BitwiseAndExpr and
|
||||
i1.bitXor(i2).bitAnd(i3) != i2.bitAnd(i3).bitXor(i1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from Expr exp, string msg
|
||||
where
|
||||
isLogicalOrAndExpr(exp) and
|
||||
msg = "Logical AND has a higher priority."
|
||||
or
|
||||
isLogicalOrandBitwise(exp) and
|
||||
msg = "Binary operations have higher priority."
|
||||
or
|
||||
// Looking for a situation where the equality of the sizes of the first operands
|
||||
// might indicate that the developer planned to perform an operation between them.
|
||||
// However, the absence of parentheses means that the rightmost operation will be performed initially.
|
||||
isBitwiseandBitwise(exp) and
|
||||
isDifferentSize(exp.(BinaryBitwiseOperation).getLeftOperand(),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getLeftOperand(),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getRightOperand()) and
|
||||
msg = "Expression ranges do not match operation precedence."
|
||||
or
|
||||
// Looking for a out those expressions that, as a result of identifying the priority with parentheses,
|
||||
// will give different values. As a consequence, this piece of code was supposed to find errors associated
|
||||
// with possible outcomes of operations.
|
||||
isBitwiseandBitwise(exp) and
|
||||
isDifferentResults(exp.(BinaryBitwiseOperation).getLeftOperand(),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getLeftOperand(),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation).getRightOperand(),
|
||||
exp.(BinaryBitwiseOperation),
|
||||
exp.(BinaryBitwiseOperation).getRightOperand().(BinaryBitwiseOperation)) and
|
||||
msg = "specify the priority with parentheses."
|
||||
select exp, msg
|
||||
@@ -272,20 +272,16 @@ class File extends Container, @file {
|
||||
* are compiled by a Microsoft compiler are detected by this predicate.
|
||||
*/
|
||||
predicate compiledAsMicrosoft() {
|
||||
exists(Compilation c |
|
||||
c.getAFileCompiled() = this and
|
||||
exists(File f, Compilation c |
|
||||
c.getAFileCompiled() = f and
|
||||
(
|
||||
c.getAnArgument() = "--microsoft" or
|
||||
c.getAnArgument()
|
||||
.toLowerCase()
|
||||
.replaceAll("\\", "/")
|
||||
.matches(["%/cl.exe", "%/clang-cl.exe"])
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(File parent |
|
||||
parent.compiledAsMicrosoft() and
|
||||
parent.getAnIncludedFile() = this
|
||||
) and
|
||||
f.getAnIncludedFile*() = this
|
||||
)
|
||||
}
|
||||
|
||||
@@ -358,6 +354,11 @@ class File extends Container, @file {
|
||||
string getShortName() { files(underlyingElement(this), _, result, _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if any file was compiled by a Microsoft compiler.
|
||||
*/
|
||||
predicate anyFileCompiledAsMicrosoft() { any(File f).compiledAsMicrosoft() }
|
||||
|
||||
/**
|
||||
* A C/C++ header file, as determined (mainly) by file extension.
|
||||
*
|
||||
|
||||
@@ -82,9 +82,23 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
/** Holds if this function is inline. */
|
||||
predicate isInline() { this.hasSpecifier("inline") }
|
||||
|
||||
/** Holds if this function is virtual. */
|
||||
/**
|
||||
* Holds if this function is virtual.
|
||||
*
|
||||
* Unlike `isDeclaredVirtual()`, `isVirtual()` holds even if the function
|
||||
* is not explicitly declared with the `virtual` specifier.
|
||||
*/
|
||||
predicate isVirtual() { this.hasSpecifier("virtual") }
|
||||
|
||||
/** Holds if this function is declared with the `virtual` specifier. */
|
||||
predicate isDeclaredVirtual() { this.hasSpecifier("declared_virtual") }
|
||||
|
||||
/** Holds if this function is declared with the `override` specifier. */
|
||||
predicate isOverride() { this.hasSpecifier("override") }
|
||||
|
||||
/** Holds if this function is declared with the `final` specifier. */
|
||||
predicate isFinal() { this.hasSpecifier("final") }
|
||||
|
||||
/**
|
||||
* Holds if this function is deleted.
|
||||
* This may be because it was explicitly deleted with an `= delete`
|
||||
@@ -137,6 +151,20 @@ class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
||||
*/
|
||||
predicate isNaked() { getAnAttribute().hasName("naked") }
|
||||
|
||||
/**
|
||||
* Holds if this function has a trailing return type.
|
||||
*
|
||||
* Note that this is true whether or not deduction took place. For example,
|
||||
* this holds for both `e` and `f`, but not `g` or `h`:
|
||||
* ```
|
||||
* auto e() -> int { return 0; }
|
||||
* auto f() -> auto { return 0; }
|
||||
* auto g() { return 0; }
|
||||
* int h() { return 0; }
|
||||
* ```
|
||||
*/
|
||||
predicate hasTrailingReturnType() { this.hasSpecifier("has_trailing_return_type") }
|
||||
|
||||
/** Gets the return type of this function. */
|
||||
Type getType() { function_return_type(underlyingElement(this), unresolveElement(result)) }
|
||||
|
||||
|
||||
@@ -306,7 +306,7 @@ class FormatLiteral extends Literal {
|
||||
* Holds if this `FormatLiteral` is in a context that supports
|
||||
* Microsoft rules and extensions.
|
||||
*/
|
||||
predicate isMicrosoft() { any(File f).compiledAsMicrosoft() }
|
||||
predicate isMicrosoft() { anyFileCompiledAsMicrosoft() }
|
||||
|
||||
/**
|
||||
* Gets the format string, with '%%' and '%@' replaced by '_' (to avoid processing
|
||||
@@ -869,6 +869,33 @@ class FormatLiteral extends Literal {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an alternate argument type that would be required by the nth
|
||||
* conversion specifier on a Microsoft or non-Microsoft platform, opposite
|
||||
* to that of the snapshot. This may be useful for answering 'what might
|
||||
* happen' questions.
|
||||
*/
|
||||
Type getConversionTypeAlternate(int n) {
|
||||
exists(string len, string conv |
|
||||
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
|
||||
(len != "l" and len != "w" and len != "h") and
|
||||
getUse().getTarget().(FormattingFunction).getFormatCharType().getSize() > 1 and // wide function
|
||||
(
|
||||
conv = "c" and
|
||||
result = getNonDefaultCharType()
|
||||
or
|
||||
conv = "C" and
|
||||
result = getDefaultCharType()
|
||||
or
|
||||
conv = "s" and
|
||||
result.(PointerType).getBaseType() = getNonDefaultCharType()
|
||||
or
|
||||
conv = "S" and
|
||||
result.(PointerType).getBaseType() = getDefaultCharType()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the nth conversion specifier of this format string (if `mode = 2`), it's
|
||||
* minimum field width (if `mode = 0`) or it's precision (if `mode = 1`) requires a
|
||||
|
||||
@@ -943,13 +943,8 @@ private module Stage2 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1122,8 +1117,7 @@ private module Stage2 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -1393,9 +1387,9 @@ private module Stage2 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -1615,13 +1609,8 @@ private module Stage3 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1816,8 +1805,7 @@ private module Stage3 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2087,9 +2075,9 @@ private module Stage3 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -2364,20 +2352,16 @@ private module Stage4 {
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
c = resolveCall(call, outercc) and
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
resolveReturn(innercc, inner, call)
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
|
||||
localFlowEntry(node, config) and
|
||||
@@ -2579,8 +2563,7 @@ private module Stage4 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2850,9 +2833,9 @@ private module Stage4 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
|
||||
@@ -943,13 +943,8 @@ private module Stage2 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1122,8 +1117,7 @@ private module Stage2 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -1393,9 +1387,9 @@ private module Stage2 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -1615,13 +1609,8 @@ private module Stage3 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1816,8 +1805,7 @@ private module Stage3 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2087,9 +2075,9 @@ private module Stage3 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -2364,20 +2352,16 @@ private module Stage4 {
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
c = resolveCall(call, outercc) and
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
resolveReturn(innercc, inner, call)
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
|
||||
localFlowEntry(node, config) and
|
||||
@@ -2579,8 +2563,7 @@ private module Stage4 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2850,9 +2833,9 @@ private module Stage4 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
|
||||
@@ -943,13 +943,8 @@ private module Stage2 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1122,8 +1117,7 @@ private module Stage2 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -1393,9 +1387,9 @@ private module Stage2 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -1615,13 +1609,8 @@ private module Stage3 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1816,8 +1805,7 @@ private module Stage3 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2087,9 +2075,9 @@ private module Stage3 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -2364,20 +2352,16 @@ private module Stage4 {
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
c = resolveCall(call, outercc) and
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
resolveReturn(innercc, inner, call)
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
|
||||
localFlowEntry(node, config) and
|
||||
@@ -2579,8 +2563,7 @@ private module Stage4 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2850,9 +2833,9 @@ private module Stage4 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
|
||||
@@ -943,13 +943,8 @@ private module Stage2 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1122,8 +1117,7 @@ private module Stage2 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -1393,9 +1387,9 @@ private module Stage2 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -1615,13 +1609,8 @@ private module Stage3 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1816,8 +1805,7 @@ private module Stage3 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2087,9 +2075,9 @@ private module Stage3 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -2364,20 +2352,16 @@ private module Stage4 {
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
c = resolveCall(call, outercc) and
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
resolveReturn(innercc, inner, call)
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
|
||||
localFlowEntry(node, config) and
|
||||
@@ -2579,8 +2563,7 @@ private module Stage4 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2850,9 +2833,9 @@ private module Stage4 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
|
||||
@@ -1117,6 +1117,44 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
|
||||
result = getReturnPosition0(ret, ret.getKind())
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether `inner` can return to `call` in the call context `innercc`.
|
||||
* Assumes a context of `inner = viableCallableExt(call)`.
|
||||
*/
|
||||
bindingset[innercc, inner, call]
|
||||
predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
innercc instanceof CallContextAny
|
||||
or
|
||||
exists(DataFlowCallable c0, DataFlowCall call0 |
|
||||
callEnclosingCallable(call0, inner) and
|
||||
innercc = TReturn(c0, call0) and
|
||||
c0 = prunedViableImplInCallContextReverse(call0, call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether `call` can resolve to `calltarget` in the call context `cc`.
|
||||
* Assumes a context of `calltarget = viableCallableExt(call)`.
|
||||
*/
|
||||
bindingset[cc, call, calltarget]
|
||||
predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) {
|
||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
|
||||
if reducedViableImplInCallContext(call, _, ctx)
|
||||
then calltarget = prunedViableImplInCallContext(call, ctx)
|
||||
else any()
|
||||
)
|
||||
or
|
||||
cc instanceof CallContextSomeCall
|
||||
or
|
||||
cc instanceof CallContextAny
|
||||
or
|
||||
cc instanceof CallContextReturn
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a return from `callable` in `cc` to `call`. This is equivalent to
|
||||
* `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`.
|
||||
*/
|
||||
bindingset[cc, callable]
|
||||
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
||||
cc instanceof CallContextAny and callable = viableCallableExt(call)
|
||||
@@ -1128,6 +1166,10 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a call from `call` in `cc` to `result`. This is equivalent to
|
||||
* `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`.
|
||||
*/
|
||||
bindingset[call, cc]
|
||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
|
||||
|
||||
@@ -943,13 +943,8 @@ private module Stage2 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1122,8 +1117,7 @@ private module Stage2 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -1393,9 +1387,9 @@ private module Stage2 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -1615,13 +1609,8 @@ private module Stage3 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1816,8 +1805,7 @@ private module Stage3 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2087,9 +2075,9 @@ private module Stage3 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -2364,20 +2352,16 @@ private module Stage4 {
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
c = resolveCall(call, outercc) and
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
resolveReturn(innercc, inner, call)
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
|
||||
localFlowEntry(node, config) and
|
||||
@@ -2579,8 +2563,7 @@ private module Stage4 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2850,9 +2833,9 @@ private module Stage4 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
|
||||
@@ -735,7 +735,12 @@ private module FieldFlow {
|
||||
private class FieldConfiguration extends Configuration {
|
||||
FieldConfiguration() { this = "FieldConfiguration" }
|
||||
|
||||
override predicate isSource(Node source) { storeStep(source, _, _) }
|
||||
override predicate isSource(Node source) {
|
||||
storeStep(source, _, _)
|
||||
or
|
||||
// Also mark `foo(a.b);` as a source when `a.b` may be overwritten by `foo`.
|
||||
readStep(_, _, any(Node node | node.asExpr() = source.asDefiningArgument()))
|
||||
}
|
||||
|
||||
override predicate isSink(Node sink) { readStep(_, _, sink) }
|
||||
|
||||
|
||||
@@ -231,10 +231,35 @@ private predicate functionSignature(Function f, string qualifiedName, int nparam
|
||||
* Holds if the set of viable implementations that can be called by `call`
|
||||
* might be improved by knowing the call context.
|
||||
*/
|
||||
predicate mayBenefitFromCallContext(CallInstruction call, Function f) { none() }
|
||||
predicate mayBenefitFromCallContext(CallInstruction call, Function f) {
|
||||
mayBenefitFromCallContext(call, f, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` is a call through a function pointer, and the pointer
|
||||
* value is given as the `arg`'th argument to `f`.
|
||||
*/
|
||||
private predicate mayBenefitFromCallContext(
|
||||
VirtualDispatch::DataSensitiveCall call, Function f, int arg
|
||||
) {
|
||||
f = pragma[only_bind_out](call).getEnclosingCallable() and
|
||||
exists(InitializeParameterInstruction init |
|
||||
not exists(call.getStaticCallTarget()) and
|
||||
init.getEnclosingFunction() = f and
|
||||
call.flowsFrom(DataFlow::instructionNode(init), _) and
|
||||
init.getParameter().getIndex() = arg
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a viable dispatch target of `call` in the context `ctx`. This is
|
||||
* restricted to those `call`s for which a context might make a difference.
|
||||
*/
|
||||
Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) { none() }
|
||||
Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) {
|
||||
result = viableCallable(call) and
|
||||
exists(int i, Function f |
|
||||
mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and
|
||||
f = ctx.getStaticCallTarget() and
|
||||
result = ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -943,13 +943,8 @@ private module Stage2 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1122,8 +1117,7 @@ private module Stage2 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -1393,9 +1387,9 @@ private module Stage2 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -1615,13 +1609,8 @@ private module Stage3 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1816,8 +1805,7 @@ private module Stage3 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2087,9 +2075,9 @@ private module Stage3 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -2364,20 +2352,16 @@ private module Stage4 {
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
c = resolveCall(call, outercc) and
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
resolveReturn(innercc, inner, call)
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
|
||||
localFlowEntry(node, config) and
|
||||
@@ -2579,8 +2563,7 @@ private module Stage4 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2850,9 +2833,9 @@ private module Stage4 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
|
||||
@@ -943,13 +943,8 @@ private module Stage2 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1122,8 +1117,7 @@ private module Stage2 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -1393,9 +1387,9 @@ private module Stage2 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -1615,13 +1609,8 @@ private module Stage3 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1816,8 +1805,7 @@ private module Stage3 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2087,9 +2075,9 @@ private module Stage3 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -2364,20 +2352,16 @@ private module Stage4 {
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
c = resolveCall(call, outercc) and
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
resolveReturn(innercc, inner, call)
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
|
||||
localFlowEntry(node, config) and
|
||||
@@ -2579,8 +2563,7 @@ private module Stage4 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2850,9 +2833,9 @@ private module Stage4 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
|
||||
@@ -943,13 +943,8 @@ private module Stage2 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1122,8 +1117,7 @@ private module Stage2 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -1393,9 +1387,9 @@ private module Stage2 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -1615,13 +1609,8 @@ private module Stage3 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1816,8 +1805,7 @@ private module Stage3 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2087,9 +2075,9 @@ private module Stage3 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -2364,20 +2352,16 @@ private module Stage4 {
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
c = resolveCall(call, outercc) and
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
resolveReturn(innercc, inner, call)
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
|
||||
localFlowEntry(node, config) and
|
||||
@@ -2579,8 +2563,7 @@ private module Stage4 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2850,9 +2833,9 @@ private module Stage4 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
|
||||
@@ -943,13 +943,8 @@ private module Stage2 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1122,8 +1117,7 @@ private module Stage2 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -1393,9 +1387,9 @@ private module Stage2 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -1615,13 +1609,8 @@ private module Stage3 {
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) { any() }
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
any()
|
||||
}
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) { any() }
|
||||
@@ -1816,8 +1805,7 @@ private module Stage3 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2087,9 +2075,9 @@ private module Stage3 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
@@ -2364,20 +2352,16 @@ private module Stage4 {
|
||||
|
||||
bindingset[call, c, outercc]
|
||||
private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
|
||||
c = resolveCall(call, outercc) and
|
||||
checkCallContextCall(outercc, call, c) and
|
||||
if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
|
||||
}
|
||||
|
||||
bindingset[call, c]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call) {
|
||||
bindingset[call, c, innercc]
|
||||
private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
|
||||
checkCallContextReturn(innercc, c, call) and
|
||||
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
|
||||
}
|
||||
|
||||
bindingset[innercc, inner, call]
|
||||
private predicate checkCallContextReturn(Cc innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
resolveReturn(innercc, inner, call)
|
||||
}
|
||||
|
||||
bindingset[node, cc, config]
|
||||
private LocalCc getLocalCc(NodeEx node, Cc cc, Configuration config) {
|
||||
localFlowEntry(node, config) and
|
||||
@@ -2579,8 +2563,7 @@ private module Stage4 {
|
||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||
inner = ret.getEnclosingCallable() and
|
||||
checkCallContextReturn(innercc, inner, call) and
|
||||
ccOut = getCallContextReturn(inner, call)
|
||||
ccOut = getCallContextReturn(inner, call, innercc)
|
||||
|
|
||||
ap instanceof ApNil or allowsFieldFlow = true
|
||||
)
|
||||
@@ -2850,9 +2833,9 @@ private module Stage4 {
|
||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||
parameterFlow(p, ap, ap0, c, config) and
|
||||
c = ret.getEnclosingCallable() and
|
||||
revFlow(ret, true, apSome(_), ap0, config) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), pragma[only_bind_into](ap0),
|
||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||
pragma[only_bind_into](config)) and
|
||||
fwdFlow(ret, any(CcCall ccc), apSome(ap), ap0, config) and
|
||||
kind = ret.getKind() and
|
||||
p.getPosition() = pos and
|
||||
// we don't expect a parameter to return stored in itself
|
||||
|
||||
@@ -1117,6 +1117,44 @@ ReturnPosition getReturnPosition(ReturnNodeExt ret) {
|
||||
result = getReturnPosition0(ret, ret.getKind())
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether `inner` can return to `call` in the call context `innercc`.
|
||||
* Assumes a context of `inner = viableCallableExt(call)`.
|
||||
*/
|
||||
bindingset[innercc, inner, call]
|
||||
predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) {
|
||||
innercc instanceof CallContextAny
|
||||
or
|
||||
exists(DataFlowCallable c0, DataFlowCall call0 |
|
||||
callEnclosingCallable(call0, inner) and
|
||||
innercc = TReturn(c0, call0) and
|
||||
c0 = prunedViableImplInCallContextReverse(call0, call)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether `call` can resolve to `calltarget` in the call context `cc`.
|
||||
* Assumes a context of `calltarget = viableCallableExt(call)`.
|
||||
*/
|
||||
bindingset[cc, call, calltarget]
|
||||
predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) {
|
||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
|
||||
if reducedViableImplInCallContext(call, _, ctx)
|
||||
then calltarget = prunedViableImplInCallContext(call, ctx)
|
||||
else any()
|
||||
)
|
||||
or
|
||||
cc instanceof CallContextSomeCall
|
||||
or
|
||||
cc instanceof CallContextAny
|
||||
or
|
||||
cc instanceof CallContextReturn
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a return from `callable` in `cc` to `call`. This is equivalent to
|
||||
* `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`.
|
||||
*/
|
||||
bindingset[cc, callable]
|
||||
predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) {
|
||||
cc instanceof CallContextAny and callable = viableCallableExt(call)
|
||||
@@ -1128,6 +1166,10 @@ predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a call from `call` in `cc` to `result`. This is equivalent to
|
||||
* `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`.
|
||||
*/
|
||||
bindingset[call, cc]
|
||||
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
|
||||
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
|
||||
|
||||
@@ -50,7 +50,7 @@ abstract class FormattingFunction extends ArrayFunction, TaintFunction {
|
||||
* Holds if this `FormattingFunction` is in a context that supports
|
||||
* Microsoft rules and extensions.
|
||||
*/
|
||||
predicate isMicrosoft() { any(File f).compiledAsMicrosoft() }
|
||||
predicate isMicrosoft() { anyFileCompiledAsMicrosoft() }
|
||||
|
||||
/**
|
||||
* Holds if the default meaning of `%s` is a `wchar_t *`, rather than
|
||||
|
||||
@@ -863,16 +863,16 @@ private float getLowerBoundsImpl(Expr expr) {
|
||||
result = 0
|
||||
or
|
||||
// If either input could be negative then the output could be
|
||||
// negative. If so, the lower bound of `x%y` is `-abs(y)`, which is
|
||||
// equal to `min(-y,y)`.
|
||||
// negative. If so, the lower bound of `x%y` is `-abs(y) + 1`, which is
|
||||
// equal to `min(-y + 1,y - 1)`.
|
||||
exists(float childLB |
|
||||
childLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and
|
||||
not childLB >= 0
|
||||
|
|
||||
result = getFullyConvertedLowerBounds(remExpr.getRightOperand())
|
||||
result = getFullyConvertedLowerBounds(remExpr.getRightOperand()) - 1
|
||||
or
|
||||
exists(float rhsUB | rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand()) |
|
||||
result = -rhsUB
|
||||
result = -rhsUB + 1
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -1058,16 +1058,16 @@ private float getUpperBoundsImpl(Expr expr) {
|
||||
expr = remExpr and
|
||||
rhsUB = getFullyConvertedUpperBounds(remExpr.getRightOperand())
|
||||
|
|
||||
result = rhsUB
|
||||
result = rhsUB - 1
|
||||
or
|
||||
// If the right hand side could be negative then we need to take its
|
||||
// absolute value. Since `abs(x) = max(-x,x)` this is equivalent to
|
||||
// adding `-rhsLB` to the set of upper bounds.
|
||||
exists(float rhsLB |
|
||||
rhsLB = getFullyConvertedLowerBounds(remExpr.getAnOperand()) and
|
||||
rhsLB = getFullyConvertedLowerBounds(remExpr.getRightOperand()) and
|
||||
not rhsLB >= 0
|
||||
|
|
||||
result = -rhsLB
|
||||
result = -rhsLB + 1
|
||||
)
|
||||
)
|
||||
or
|
||||
|
||||
@@ -19,6 +19,15 @@ class FileWrite extends Expr {
|
||||
* Gets the expression for the object being written to.
|
||||
*/
|
||||
Expr getDest() { fileWrite(this, _, result) }
|
||||
|
||||
/**
|
||||
* Gets the conversion character for this write, if it exists and is known. For example in the following code the write of `value1` has conversion character `"s"`, whereas the write of `value2` has no conversion specifier.
|
||||
* ```
|
||||
* fprintf(file, "%s", value1);
|
||||
* stream << value2;
|
||||
* ```
|
||||
*/
|
||||
string getSourceConvChar(Expr source) { fileWriteWithConvChar(this, source, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,3 +159,20 @@ private predicate fileWrite(Call write, Expr source, Expr dest) {
|
||||
// file stream using '<<', 'put' or 'write'
|
||||
fileStreamChain(write, source, dest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the function call is a write to a file from 'source' with
|
||||
* conversion character 'conv'. Does not hold if there isn't a conversion
|
||||
* character, or if it is unknown (for example the format string is not a
|
||||
* constant).
|
||||
*/
|
||||
private predicate fileWriteWithConvChar(FormattingFunctionCall ffc, Expr source, string conv) {
|
||||
// fprintf
|
||||
exists(FormattingFunction f, int n |
|
||||
f = ffc.getTarget() and
|
||||
source = ffc.getFormatArgument(n)
|
||||
|
|
||||
exists(f.getOutputParameterIndex(true)) and
|
||||
conv = ffc.(FormattingFunctionCall).getFormat().(FormatLiteral).getConversionChar(n)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.controlflow.Dominance
|
||||
// `GlobalValueNumbering` is only imported to prevent IR re-evaluation.
|
||||
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
|
||||
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
||||
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
||||
|
||||
|
||||
@@ -14,14 +14,13 @@ private predicate suspicious(string s) {
|
||||
(
|
||||
s.matches("%password%") or
|
||||
s.matches("%passwd%") or
|
||||
s.matches("%account%") or
|
||||
s.matches("%accnt%") or
|
||||
s.matches("%trusted%")
|
||||
) and
|
||||
not (
|
||||
s.matches("%hashed%") or
|
||||
s.matches("%encrypted%") or
|
||||
s.matches("%crypt%")
|
||||
s.matches("%hash%") or
|
||||
s.matches("%crypt%") or
|
||||
s.matches("%file%") or
|
||||
s.matches("%path%")
|
||||
)
|
||||
}
|
||||
|
||||
@@ -29,14 +28,20 @@ private predicate suspicious(string s) {
|
||||
* A variable that might contain a password or other sensitive information.
|
||||
*/
|
||||
class SensitiveVariable extends Variable {
|
||||
SensitiveVariable() { suspicious(getName().toLowerCase()) }
|
||||
SensitiveVariable() {
|
||||
suspicious(getName().toLowerCase()) and
|
||||
not this.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that might return a password or other sensitive information.
|
||||
*/
|
||||
class SensitiveFunction extends Function {
|
||||
SensitiveFunction() { suspicious(getName().toLowerCase()) }
|
||||
SensitiveFunction() {
|
||||
suspicious(getName().toLowerCase()) and
|
||||
not this.getUnspecifiedType() instanceof IntegralType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
| test.c:20:3:28:3 | switch (...) ... | Possibly erroneous label name. |
|
||||
| test.c:30:3:38:3 | switch (...) ... | Code before case will not be executed. |
|
||||
| test.c:41:3:50:3 | switch (...) ... | The range of condition values is less than the selection. |
|
||||
| test.c:53:3:58:3 | switch (...) ... | The range of condition values is wider than the choices. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-561/FindIncorrectlyUsedSwitch.ql
|
||||
@@ -0,0 +1,60 @@
|
||||
void testFunction(char c1,int i1)
|
||||
{
|
||||
|
||||
switch(c1){ // GOOD
|
||||
case 12:
|
||||
break;
|
||||
case 10:
|
||||
break;
|
||||
case 9:
|
||||
break;
|
||||
}
|
||||
|
||||
switch(i1){ // GOOD
|
||||
for(i1=0;i1<20;i1++){
|
||||
case 12:
|
||||
case 10:
|
||||
case 9:
|
||||
}
|
||||
}
|
||||
switch(c1){ // BAD
|
||||
case 12:
|
||||
break;
|
||||
case 10:
|
||||
break;
|
||||
case 9:
|
||||
break;
|
||||
dafault:
|
||||
}
|
||||
|
||||
switch(c1){ // BAD
|
||||
c1=c1*2;
|
||||
case 12:
|
||||
break;
|
||||
case 10:
|
||||
break;
|
||||
case 9:
|
||||
break;
|
||||
}
|
||||
|
||||
if((c1<6)&&(c1>0))
|
||||
switch(c1){ // BAD
|
||||
case 8:
|
||||
break;
|
||||
case 5:
|
||||
break;
|
||||
case 3:
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
|
||||
if((c1<6)&&(c1>0))
|
||||
switch(c1){ // BAD
|
||||
case 3:
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.cpp:35:3:35:33 | call to runtime_error | This object does not generate an exception. |
|
||||
| test.cpp:41:3:41:11 | call to funcTest1 | There is an exception in the function that requires your attention. |
|
||||
| test.cpp:42:3:42:9 | call to DllMain | DllMain contains an exeption not wrapped in a try..catch block. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-703/FindIncorrectlyUsedExceptions.ql
|
||||
@@ -0,0 +1,44 @@
|
||||
namespace std
|
||||
{
|
||||
class exception {
|
||||
};
|
||||
|
||||
class runtime_error : public exception {
|
||||
public:
|
||||
runtime_error(const char *msg);
|
||||
};
|
||||
}
|
||||
|
||||
typedef unsigned int size_t;
|
||||
void clean();
|
||||
|
||||
|
||||
void funcTest1()
|
||||
{
|
||||
throw ("my exception!",546); // BAD
|
||||
}
|
||||
|
||||
void DllMain()
|
||||
{
|
||||
try { throw "my exception!"; } // BAD
|
||||
catch (...) { }
|
||||
}
|
||||
|
||||
void funcTest2()
|
||||
{
|
||||
try { throw "my exception!"; } // GOOD
|
||||
catch (...) { clean(); }
|
||||
}
|
||||
|
||||
void funcTest3()
|
||||
{
|
||||
std::runtime_error("msg error"); // BAD
|
||||
throw std::runtime_error("msg error"); // GOOD
|
||||
}
|
||||
|
||||
void TestFunc()
|
||||
{
|
||||
funcTest1();
|
||||
DllMain();
|
||||
funcTest2();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
| test.c:13:10:13:21 | call to tmpFunction1 | This expression may have undefined behavior, because the order of evaluation is not specified. |
|
||||
| test.c:13:30:13:41 | call to tmpFunction2 | This expression may have undefined behavior, because the order of evaluation is not specified. |
|
||||
| test.c:16:15:16:20 | ... ++ | This expression may have undefined behavior, because the order of evaluation is not specified. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-758/UndefinedOrImplementationDefinedBehavior.ql
|
||||
@@ -0,0 +1,19 @@
|
||||
char tmpFunction1(char * buf)
|
||||
{
|
||||
buf[1]=buf[1] + buf[2] + buf[3];
|
||||
return buf[1];
|
||||
}
|
||||
char tmpFunction2(char * buf)
|
||||
{
|
||||
buf[2]=buf[1] + buf[2] + buf[3];
|
||||
return buf[2];
|
||||
}
|
||||
void workFunction_0(char *s, char * buf) {
|
||||
int intA;
|
||||
intA = tmpFunction1(buf) + tmpFunction2(buf); // BAD
|
||||
intA = tmpFunction1(buf); //GOOD
|
||||
intA += tmpFunction2(buf); // GOOD
|
||||
buf[intA] = intA++; // BAD
|
||||
intA++;
|
||||
buf[intA] = intA; // GOOD
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
| test.cpp:4:6:4:15 | ... \|\| ... | Logical AND has a higher priority. |
|
||||
| test.cpp:11:6:11:14 | ... \|\| ... | Binary operations have higher priority. |
|
||||
| test.cpp:19:6:19:14 | ... \|\| ... | Binary operations have higher priority. |
|
||||
| test.cpp:24:6:24:13 | ... \| ... | Expression ranges do not match operation precedence. |
|
||||
| test.cpp:28:6:28:13 | ... ^ ... | Expression ranges do not match operation precedence. |
|
||||
| test.cpp:33:6:33:13 | ... \| ... | Expression ranges do not match operation precedence. |
|
||||
| test.cpp:38:6:38:13 | ... \| ... | specify the priority with parentheses. |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-783/OperatorPrecedenceLogicErrorWhenUseBitwiseOrLogicalOperations.ql
|
||||
@@ -0,0 +1,43 @@
|
||||
void testFunction(int i1, int i2, int i3, bool b1, bool b2, bool b3, char c1)
|
||||
{
|
||||
|
||||
if(b1||b2&&b3) //BAD
|
||||
return;
|
||||
if((b1||b2)&&b3) //GOOD
|
||||
return;
|
||||
if(b1||(b2&&b3)) //GOOD
|
||||
return;
|
||||
|
||||
if(b1||b2&i1) //BAD
|
||||
return;
|
||||
if((b1||b2)&i1) //GOOD
|
||||
return;
|
||||
if(b1||b2&1) //GOOD
|
||||
return;
|
||||
if(b1&&b2&0) //GOOD
|
||||
return;
|
||||
if(b1||b2|i1) //BAD
|
||||
return;
|
||||
if((b1||b2)|i1) //GOOD
|
||||
return;
|
||||
|
||||
if(i1|i2&c1) //BAD
|
||||
return;
|
||||
if((i1|i2)&i3) //GOOD
|
||||
return;
|
||||
if(i1^i2&c1) //BAD
|
||||
return;
|
||||
if((i1^i2)&i3) //GOOD
|
||||
return;
|
||||
|
||||
if(i1|i2^c1) //BAD
|
||||
return;
|
||||
if((i1|i2)^i3) //GOOD
|
||||
return;
|
||||
|
||||
if(b1|b2^b3) //BAD
|
||||
return;
|
||||
if((b1|b2)^b3) //GOOD
|
||||
return;
|
||||
|
||||
}
|
||||
@@ -79,6 +79,7 @@
|
||||
| file://:0:0:0:0 | declaration of 1st parameter |
|
||||
| file://:0:0:0:0 | declared_constexpr |
|
||||
| file://:0:0:0:0 | declared_constinit |
|
||||
| file://:0:0:0:0 | declared_virtual |
|
||||
| file://:0:0:0:0 | decltype(nullptr) |
|
||||
| file://:0:0:0:0 | definition of fp_offset |
|
||||
| file://:0:0:0:0 | definition of gp_offset |
|
||||
@@ -91,10 +92,12 @@
|
||||
| file://:0:0:0:0 | explicit |
|
||||
| file://:0:0:0:0 | extern |
|
||||
| file://:0:0:0:0 | far |
|
||||
| file://:0:0:0:0 | final |
|
||||
| file://:0:0:0:0 | float |
|
||||
| file://:0:0:0:0 | forceinline |
|
||||
| file://:0:0:0:0 | fp_offset |
|
||||
| file://:0:0:0:0 | gp_offset |
|
||||
| file://:0:0:0:0 | has_trailing_return_type |
|
||||
| file://:0:0:0:0 | implicit_int |
|
||||
| file://:0:0:0:0 | inline |
|
||||
| file://:0:0:0:0 | int |
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
| file://:0:0:0:0 | const __va_list_tag & |
|
||||
| file://:0:0:0:0 | declared_constexpr |
|
||||
| file://:0:0:0:0 | declared_constinit |
|
||||
| file://:0:0:0:0 | declared_virtual |
|
||||
| file://:0:0:0:0 | decltype(nullptr) |
|
||||
| file://:0:0:0:0 | definition of <error> |
|
||||
| file://:0:0:0:0 | definition of fp_offset |
|
||||
@@ -62,10 +63,12 @@
|
||||
| file://:0:0:0:0 | explicit |
|
||||
| file://:0:0:0:0 | extern |
|
||||
| file://:0:0:0:0 | far |
|
||||
| file://:0:0:0:0 | final |
|
||||
| file://:0:0:0:0 | float |
|
||||
| file://:0:0:0:0 | forceinline |
|
||||
| file://:0:0:0:0 | fp_offset |
|
||||
| file://:0:0:0:0 | gp_offset |
|
||||
| file://:0:0:0:0 | has_trailing_return_type |
|
||||
| file://:0:0:0:0 | implicit_int |
|
||||
| file://:0:0:0:0 | initializer for <error> |
|
||||
| file://:0:0:0:0 | inline |
|
||||
|
||||
@@ -70,3 +70,8 @@
|
||||
| test.cpp:391:11:391:13 | tmp | test.cpp:391:10:391:13 | & ... |
|
||||
| test.cpp:391:17:391:23 | source1 | test.cpp:391:10:391:13 | ref arg & ... |
|
||||
| test.cpp:391:17:391:23 | source1 | test.cpp:391:16:391:23 | & ... |
|
||||
| test.cpp:480:67:480:67 | s | test.cpp:481:21:481:21 | s |
|
||||
| test.cpp:480:67:480:67 | s | test.cpp:482:20:482:20 | s |
|
||||
| test.cpp:481:21:481:21 | s [post update] | test.cpp:482:20:482:20 | s |
|
||||
| test.cpp:481:24:481:30 | ref arg content | test.cpp:482:23:482:29 | content |
|
||||
| test.cpp:482:23:482:29 | content | test.cpp:483:9:483:17 | p_content |
|
||||
|
||||
@@ -470,3 +470,15 @@ void viaOutparam() {
|
||||
intOutparamSource(&x);
|
||||
sink(x); // $ ast,ir
|
||||
}
|
||||
|
||||
void writes_to_content(void*);
|
||||
|
||||
struct MyStruct {
|
||||
int* content;
|
||||
};
|
||||
|
||||
void local_field_flow_def_by_ref_steps_with_local_flow(MyStruct * s) {
|
||||
writes_to_content(s->content);
|
||||
int* p_content = s->content;
|
||||
sink(*p_content);
|
||||
}
|
||||
@@ -496,9 +496,13 @@
|
||||
| map.cpp:49:7:49:7 | f [post update] | map.cpp:51:7:51:7 | f | |
|
||||
| map.cpp:49:7:49:7 | f [post update] | map.cpp:53:30:53:30 | f | |
|
||||
| map.cpp:49:7:49:7 | f [post update] | map.cpp:59:6:59:6 | f | |
|
||||
| map.cpp:49:9:49:13 | ref arg first | map.cpp:54:9:54:13 | first | |
|
||||
| map.cpp:49:9:49:13 | ref arg first | map.cpp:60:9:60:13 | first | |
|
||||
| map.cpp:50:7:50:7 | f [post update] | map.cpp:51:7:51:7 | f | |
|
||||
| map.cpp:50:7:50:7 | f [post update] | map.cpp:53:30:53:30 | f | |
|
||||
| map.cpp:50:7:50:7 | f [post update] | map.cpp:59:6:59:6 | f | |
|
||||
| map.cpp:50:9:50:14 | ref arg second | map.cpp:55:9:55:14 | second | |
|
||||
| map.cpp:50:9:50:14 | ref arg second | map.cpp:61:9:61:14 | second | |
|
||||
| map.cpp:53:30:53:30 | f | map.cpp:54:7:54:7 | g | |
|
||||
| map.cpp:53:30:53:30 | f | map.cpp:55:7:55:7 | g | |
|
||||
| map.cpp:53:30:53:30 | f | map.cpp:56:7:56:7 | g | |
|
||||
@@ -3395,6 +3399,7 @@
|
||||
| smart_pointer.cpp:125:20:125:20 | call to operator-> [post update] | smart_pointer.cpp:125:18:125:19 | ref arg p1 | TAINT |
|
||||
| smart_pointer.cpp:125:22:125:22 | q | smart_pointer.cpp:125:18:125:22 | call to shared_ptr | |
|
||||
| smart_pointer.cpp:125:22:125:22 | ref arg q | smart_pointer.cpp:125:22:125:22 | q [inner post update] | |
|
||||
| smart_pointer.cpp:125:22:125:22 | ref arg q | smart_pointer.cpp:126:12:126:12 | q | |
|
||||
| smart_pointer.cpp:126:8:126:9 | p1 | smart_pointer.cpp:126:10:126:10 | call to operator-> | |
|
||||
| smart_pointer.cpp:126:8:126:9 | ref arg p1 | smart_pointer.cpp:124:48:124:49 | p1 | |
|
||||
| smart_pointer.cpp:126:10:126:10 | call to operator-> [post update] | smart_pointer.cpp:126:8:126:9 | ref arg p1 | TAINT |
|
||||
@@ -3432,6 +3437,7 @@
|
||||
| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | |
|
||||
| smart_pointer.cpp:133:23:133:24 | ref arg p1 | smart_pointer.cpp:134:8:134:9 | p1 | |
|
||||
| smart_pointer.cpp:133:25:133:25 | call to operator-> [post update] | smart_pointer.cpp:133:23:133:24 | ref arg p1 | TAINT |
|
||||
| smart_pointer.cpp:133:27:133:27 | ref arg q | smart_pointer.cpp:134:12:134:12 | q | |
|
||||
| smart_pointer.cpp:134:8:134:9 | p1 | smart_pointer.cpp:134:10:134:10 | call to operator-> | |
|
||||
| smart_pointer.cpp:134:8:134:9 | ref arg p1 | smart_pointer.cpp:132:53:132:54 | p1 | |
|
||||
| smart_pointer.cpp:134:10:134:10 | call to operator-> [post update] | smart_pointer.cpp:134:8:134:9 | ref arg p1 | TAINT |
|
||||
@@ -6435,6 +6441,7 @@
|
||||
| taint.cpp:669:18:669:18 | s [post update] | taint.cpp:671:7:671:7 | s | |
|
||||
| taint.cpp:669:18:669:18 | s [post update] | taint.cpp:672:7:672:7 | s | |
|
||||
| taint.cpp:669:18:669:18 | s [post update] | taint.cpp:673:7:673:7 | s | |
|
||||
| taint.cpp:669:20:669:20 | ref arg x | taint.cpp:672:9:672:9 | x | |
|
||||
| taint.cpp:672:7:672:7 | s [post update] | taint.cpp:673:7:673:7 | s | |
|
||||
| vector.cpp:16:43:16:49 | source1 | vector.cpp:17:26:17:32 | source1 | |
|
||||
| vector.cpp:16:43:16:49 | source1 | vector.cpp:31:38:31:44 | source1 | |
|
||||
@@ -7076,14 +7083,20 @@
|
||||
| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:200:3:200:4 | ee | |
|
||||
| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:201:8:201:9 | ee | |
|
||||
| vector.cpp:198:3:198:4 | ee [post update] | vector.cpp:202:2:202:2 | ee | |
|
||||
| vector.cpp:198:6:198:7 | ref arg vs | vector.cpp:199:11:199:12 | vs | |
|
||||
| vector.cpp:198:6:198:7 | ref arg vs | vector.cpp:200:6:200:7 | vs | |
|
||||
| vector.cpp:198:6:198:7 | ref arg vs | vector.cpp:201:11:201:12 | vs | |
|
||||
| vector.cpp:198:19:198:19 | 0 | vector.cpp:198:6:198:7 | ref arg vs | TAINT |
|
||||
| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:200:3:200:4 | ee | |
|
||||
| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:201:8:201:9 | ee | |
|
||||
| vector.cpp:199:8:199:9 | ee [post update] | vector.cpp:202:2:202:2 | ee | |
|
||||
| vector.cpp:199:11:199:12 | ref arg vs | vector.cpp:200:6:200:7 | vs | |
|
||||
| vector.cpp:199:11:199:12 | ref arg vs | vector.cpp:201:11:201:12 | vs | |
|
||||
| vector.cpp:199:11:199:12 | vs | vector.cpp:199:13:199:13 | call to operator[] | TAINT |
|
||||
| vector.cpp:200:3:200:4 | ee [post update] | vector.cpp:201:8:201:9 | ee | |
|
||||
| vector.cpp:200:3:200:4 | ee [post update] | vector.cpp:202:2:202:2 | ee | |
|
||||
| vector.cpp:200:3:200:21 | ... = ... | vector.cpp:200:8:200:8 | call to operator[] [post update] | |
|
||||
| vector.cpp:200:6:200:7 | ref arg vs | vector.cpp:201:11:201:12 | vs | |
|
||||
| vector.cpp:200:6:200:7 | vs | vector.cpp:200:8:200:8 | call to operator[] | TAINT |
|
||||
| vector.cpp:200:8:200:8 | call to operator[] [post update] | vector.cpp:200:6:200:7 | ref arg vs | TAINT |
|
||||
| vector.cpp:200:14:200:19 | call to source | vector.cpp:200:3:200:21 | ... = ... | |
|
||||
|
||||
@@ -592,6 +592,8 @@
|
||||
| test.c:654:9:654:9 | i | -2147483648 |
|
||||
| test.c:658:7:658:7 | u | 0 |
|
||||
| test.c:659:9:659:9 | u | 0 |
|
||||
| test.c:664:12:664:12 | s | -2147483648 |
|
||||
| test.c:665:7:665:8 | s2 | -4 |
|
||||
| test.cpp:10:7:10:7 | b | -2147483648 |
|
||||
| test.cpp:11:5:11:5 | x | -2147483648 |
|
||||
| test.cpp:13:10:13:10 | x | -2147483648 |
|
||||
|
||||
@@ -659,3 +659,8 @@ void guard_bound_out_of_range(void) {
|
||||
out(u); // unreachable [BUG: is 0 .. +max]
|
||||
}
|
||||
}
|
||||
|
||||
void test_mod(int s) {
|
||||
int s2 = s % 5;
|
||||
out(s2); // -4 .. 4
|
||||
}
|
||||
|
||||
@@ -592,6 +592,8 @@
|
||||
| test.c:654:9:654:9 | i | 2147483647 |
|
||||
| test.c:658:7:658:7 | u | 0 |
|
||||
| test.c:659:9:659:9 | u | 4294967295 |
|
||||
| test.c:664:12:664:12 | s | 2147483647 |
|
||||
| test.c:665:7:665:8 | s2 | 4 |
|
||||
| test.cpp:10:7:10:7 | b | 2147483647 |
|
||||
| test.cpp:11:5:11:5 | x | 2147483647 |
|
||||
| test.cpp:13:10:13:10 | x | 2147483647 |
|
||||
|
||||
@@ -43,10 +43,12 @@
|
||||
| Function | specifiers2pp.cpp:10:18:10:24 | MyClass | MyClass | public |
|
||||
| Function | specifiers2pp.cpp:12:14:12:22 | publicFun | publicFun | inline |
|
||||
| Function | specifiers2pp.cpp:12:14:12:22 | publicFun | publicFun | public |
|
||||
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | declared_virtual |
|
||||
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | extern |
|
||||
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | public |
|
||||
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | pure |
|
||||
| Function | specifiers2pp.cpp:13:21:13:26 | getInt | getInt | virtual |
|
||||
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | declared_virtual |
|
||||
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | extern |
|
||||
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | public |
|
||||
| Function | specifiers2pp.cpp:14:21:14:21 | f | f | virtual |
|
||||
@@ -71,6 +73,7 @@
|
||||
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | inline |
|
||||
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | public |
|
||||
| Function | specifiers2pp.cpp:24:7:24:7 | operator= | operator= | public |
|
||||
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | declared_virtual |
|
||||
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | extern |
|
||||
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | override |
|
||||
| Function | specifiers2pp.cpp:26:21:26:21 | f | f | public |
|
||||
|
||||
@@ -109,6 +109,7 @@
|
||||
| file://:0:0:0:0 | declaration of 1st parameter |
|
||||
| file://:0:0:0:0 | declared_constexpr |
|
||||
| file://:0:0:0:0 | declared_constinit |
|
||||
| file://:0:0:0:0 | declared_virtual |
|
||||
| file://:0:0:0:0 | decltype(nullptr) |
|
||||
| file://:0:0:0:0 | definition of fp_offset |
|
||||
| file://:0:0:0:0 | definition of gp_offset |
|
||||
@@ -121,12 +122,14 @@
|
||||
| file://:0:0:0:0 | explicit |
|
||||
| file://:0:0:0:0 | extern |
|
||||
| file://:0:0:0:0 | far |
|
||||
| file://:0:0:0:0 | final |
|
||||
| file://:0:0:0:0 | float |
|
||||
| file://:0:0:0:0 | forceinline |
|
||||
| file://:0:0:0:0 | fp_offset |
|
||||
| file://:0:0:0:0 | gp_offset |
|
||||
| file://:0:0:0:0 | grammar_helper_base & |
|
||||
| file://:0:0:0:0 | grammar_helper_base && |
|
||||
| file://:0:0:0:0 | has_trailing_return_type |
|
||||
| file://:0:0:0:0 | implicit_int |
|
||||
| file://:0:0:0:0 | inline |
|
||||
| file://:0:0:0:0 | int |
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
| file://:0:0:0:0 | const | Other |
|
||||
| file://:0:0:0:0 | declared_constexpr | Other |
|
||||
| file://:0:0:0:0 | declared_constinit | Other |
|
||||
| file://:0:0:0:0 | declared_virtual | Other |
|
||||
| file://:0:0:0:0 | decltype(nullptr) | Other |
|
||||
| file://:0:0:0:0 | definition of fp_offset | Other |
|
||||
| file://:0:0:0:0 | definition of gp_offset | Other |
|
||||
@@ -53,10 +54,12 @@
|
||||
| file://:0:0:0:0 | explicit | Other |
|
||||
| file://:0:0:0:0 | extern | Other |
|
||||
| file://:0:0:0:0 | far | Other |
|
||||
| file://:0:0:0:0 | final | Other |
|
||||
| file://:0:0:0:0 | float | Other |
|
||||
| file://:0:0:0:0 | forceinline | Other |
|
||||
| file://:0:0:0:0 | fp_offset | Other |
|
||||
| file://:0:0:0:0 | gp_offset | Other |
|
||||
| file://:0:0:0:0 | has_trailing_return_type | Other |
|
||||
| file://:0:0:0:0 | implicit_int | Other |
|
||||
| file://:0:0:0:0 | inline | Other |
|
||||
| file://:0:0:0:0 | int | Other |
|
||||
|
||||
@@ -3,12 +3,8 @@
|
||||
| tests.cpp:21:15:21:21 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
|
||||
| tests.cpp:21:15:21:21 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' |
|
||||
| tests.cpp:26:17:26:24 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| tests.cpp:27:17:27:24 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:29:17:29:23 | Hello | This argument should be of type 'wchar_t *' but is of type 'char *' |
|
||||
| tests.cpp:30:17:30:24 | Hello | This argument should be of type 'wchar_t *' but is of type 'char16_t *' |
|
||||
| tests.cpp:34:36:34:43 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| tests.cpp:35:36:35:43 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:37:36:37:42 | Hello | This argument should be of type 'char16_t *' but is of type 'char *' |
|
||||
| tests.cpp:39:36:39:43 | Hello | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
|
||||
| tests.cpp:42:37:42:44 | Hello | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| tests.cpp:43:37:43:44 | Hello | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
|
||||
@@ -24,17 +24,17 @@ void tests() {
|
||||
|
||||
wprintf(L"%s", "Hello"); // GOOD
|
||||
wprintf(L"%s", u"Hello"); // BAD: expecting char
|
||||
wprintf(L"%s", L"Hello"); // BAD: expecting char
|
||||
wprintf(L"%s", L"Hello"); // BAD: expecting char [NOT DETECTED; correct on Microsoft platforms]
|
||||
|
||||
wprintf(L"%S", "Hello"); // BAD: expecting wchar_t
|
||||
wprintf(L"%S", "Hello"); // BAD: expecting wchar_t [NOT DETECTED; correct on Microsoft platforms]
|
||||
wprintf(L"%S", u"Hello"); // BAD: expecting wchar_t
|
||||
wprintf(L"%S", L"Hello"); // GOOD
|
||||
|
||||
swprintf(buffer, BUF_SIZE, u"%s", "Hello"); // GOOD
|
||||
swprintf(buffer, BUF_SIZE, u"%s", u"Hello"); // BAD: expecting char
|
||||
swprintf(buffer, BUF_SIZE, u"%s", u"Hello"); // BAD: expecting char [NOT DETECTED; correct on Microsoft platforms]
|
||||
swprintf(buffer, BUF_SIZE, u"%s", L"Hello"); // BAD: expecting char
|
||||
|
||||
swprintf(buffer, BUF_SIZE, u"%S", "Hello"); // BAD: expecting char16_t
|
||||
swprintf(buffer, BUF_SIZE, u"%S", "Hello"); // BAD: expecting char16_t [NOT DETECTED; correct on Microsoft platforms]
|
||||
swprintf(buffer, BUF_SIZE, u"%S", u"Hello"); // GOOD
|
||||
swprintf(buffer, BUF_SIZE, u"%S", L"Hello"); // BAD: expecting char16_t
|
||||
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
| printf.cpp:31:31:31:37 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| printf.cpp:43:29:43:35 | test | This argument should be of type 'char *' but is of type 'char16_t *' |
|
||||
| printf.cpp:50:29:50:35 | test | This argument should be of type 'char16_t *' but is of type 'wchar_t *' |
|
||||
|
||||
@@ -28,7 +28,7 @@ int sprintf(char *dest, char *format, ...);
|
||||
void test1() {
|
||||
WCHAR string[20];
|
||||
|
||||
swprintf(string, u"test %s", u"test"); // BAD: `char16_t` string parameter read as `char` string
|
||||
swprintf(string, u"test %s", u"test"); // BAD: `char16_t` string parameter read as `char` string [NOT DETECTED; correct on Microsoft platforms]
|
||||
}
|
||||
|
||||
void test2() {
|
||||
|
||||
@@ -11,8 +11,6 @@
|
||||
| printf1.h:45:18:45:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:46:18:46:20 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:130:18:130:18 | 0 | This argument should be of type 'void *' but is of type 'int' |
|
||||
| printf1.h:154:18:154:19 | wc | This argument should be of type 'char *' but is of type 'wchar_t *' |
|
||||
| printf1.h:155:18:155:18 | c | This argument should be of type 'wchar_t *' but is of type 'char *' |
|
||||
| printf1.h:168:19:168:19 | i | This argument should be of type 'long long' but is of type 'int' |
|
||||
| printf1.h:169:19:169:20 | ui | This argument should be of type 'unsigned long long' but is of type 'unsigned int' |
|
||||
| real_world.h:61:21:61:22 | & ... | This argument should be of type 'int *' but is of type 'short *' |
|
||||
|
||||
@@ -151,8 +151,8 @@ void test_chars(char c, wchar_t wc, wint_t wt)
|
||||
void test_ws(char *c, wchar_t *wc)
|
||||
{
|
||||
wprintf(L"%s", c); // GOOD
|
||||
wprintf(L"%s", wc); // BAD
|
||||
wprintf(L"%S", c); // BAD
|
||||
wprintf(L"%s", wc); // BAD [NOT DETECTED; correct on Microsoft platforms]
|
||||
wprintf(L"%S", c); // BAD [NOT DETECTED; correct on Microsoft platforms]
|
||||
wprintf(L"%S", wc); // GOOD
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
| printf1.h:116:16:116:24 | myString3 | This argument should be of type '__wchar_t *' but is of type 'int *' |
|
||||
| printf1.h:117:16:117:24 | myString4 | This argument should be of type '__wchar_t *' but is of type 'int *' |
|
||||
| printf1.h:130:18:130:18 | 0 | This argument should be of type 'void *' but is of type 'int' |
|
||||
| printf1.h:153:18:153:18 | c | This argument should be of type '__wchar_t *' but is of type 'char *' |
|
||||
| printf1.h:156:18:156:19 | wc | This argument should be of type 'char *' but is of type '__wchar_t *' |
|
||||
| printf1.h:181:21:181:22 | ll | This argument should be of type 'int' but is of type 'long long' |
|
||||
| printf1.h:182:21:182:23 | ull | This argument should be of type 'unsigned int' but is of type 'unsigned long long' |
|
||||
| printf1.h:185:21:185:23 | i64 | This argument should be of type 'int' but is of type 'long long' |
|
||||
|
||||
@@ -150,10 +150,10 @@ void test_chars(char c, wchar_t wc, wint_t wt)
|
||||
|
||||
void test_ws(char *c, wchar_t *wc, wint_t *wt)
|
||||
{
|
||||
wprintf(L"%s", c); // BAD
|
||||
wprintf(L"%s", c); // BAD [NOT DETECTED; correct on non-Microsoft platforms]
|
||||
wprintf(L"%s", wc); // GOOD
|
||||
wprintf(L"%S", c); // GOOD
|
||||
wprintf(L"%S", wc); // BAD
|
||||
wprintf(L"%S", wc); // BAD [NOT DETECTED; correct on non-Microsoft platforms]
|
||||
}
|
||||
|
||||
void fun4()
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
| test.cpp:25:10:25:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:22:8:22:14 | buffer1 | buffer1 |
|
||||
| test.cpp:26:10:26:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:23:8:23:14 | buffer2 | buffer2 |
|
||||
| test.cpp:39:10:39:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:35:8:35:14 | buffer2 | buffer2 |
|
||||
| test.cpp:59:10:59:13 | ptr1 | Variable $@ may not be null terminated. | test.cpp:56:9:56:12 | ptr1 | ptr1 |
|
||||
| test.cpp:69:10:69:16 | buffer1 | Variable $@ may not be null terminated. | test.cpp:64:8:64:14 | buffer1 | buffer1 |
|
||||
| test.cpp:70:10:70:12 | ptr | Variable $@ may not be null terminated. | test.cpp:64:8:64:14 | buffer1 | buffer1 |
|
||||
| test.cpp:81:10:81:16 | buffer2 | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer2 | buffer2 |
|
||||
| test.cpp:82:10:82:12 | ptr | Variable $@ may not be null terminated. | test.cpp:65:8:65:14 | buffer2 | buffer2 |
|
||||
| test.cpp:93:10:93:15 | buffer | Variable $@ may not be null terminated. | test.cpp:86:8:86:13 | buffer | buffer |
|
||||
| test.cpp:116:10:116:15 | buffer | Variable $@ may not be null terminated. | test.cpp:109:8:109:13 | buffer | buffer |
|
||||
| test.cpp:130:14:130:19 | buffer | Variable $@ may not be null terminated. | test.cpp:127:7:127:12 | buffer | buffer |
|
||||
| test.cpp:139:10:139:15 | buffer | Variable $@ may not be null terminated. | test.cpp:136:8:136:13 | buffer | buffer |
|
||||
| test.cpp:147:14:147:19 | buffer | Variable $@ may not be null terminated. | test.cpp:143:8:143:13 | buffer | buffer |
|
||||
| test.cpp:170:10:170:15 | buffer | Variable $@ may not be null terminated. | test.cpp:166:8:166:13 | buffer | buffer |
|
||||
@@ -0,0 +1 @@
|
||||
Likely Bugs/Memory Management/ImproperNullTermination.ql
|
||||
@@ -0,0 +1,211 @@
|
||||
|
||||
typedef unsigned int size_t;
|
||||
typedef signed int ssize_t;
|
||||
|
||||
size_t strlen(const char *s);
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
char *strdup(const char *s1);
|
||||
|
||||
void *malloc(size_t size);
|
||||
|
||||
void *memset(void *s, int c, size_t n);
|
||||
void *memcpy(void *s1, const void *s2, size_t n);
|
||||
|
||||
ssize_t readlink(const char *path, char *buffer, size_t buffer_size);
|
||||
ssize_t readlinkat(int fd, const char *path, char *buffer, size_t buffer_size);
|
||||
|
||||
bool cond();
|
||||
|
||||
void test_unassigned()
|
||||
{
|
||||
{
|
||||
char buffer1[1024];
|
||||
char buffer2[1024];
|
||||
|
||||
strdup(buffer1); // BAD
|
||||
strdup(buffer2); // BAD
|
||||
|
||||
memcpy(buffer2, buffer1, sizeof(buffer2));
|
||||
strdup(buffer1); // BAD [NOT DETECTED]
|
||||
strdup(buffer2); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
{
|
||||
char buffer1[1024];
|
||||
char buffer2[1024];
|
||||
|
||||
strcpy(buffer1, "content");
|
||||
strdup(buffer1); // GOOD
|
||||
strdup(buffer2); // BAD
|
||||
|
||||
memcpy(buffer2, buffer1, sizeof(buffer2));
|
||||
strdup(buffer1); // GOOD
|
||||
strdup(buffer2); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer1[1024] = {0};
|
||||
char buffer2[1024];
|
||||
|
||||
memset(buffer2, 0, sizeof(buffer2));
|
||||
strdup(buffer1); // GOOD
|
||||
strdup(buffer2); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char *ptr1;
|
||||
char *ptr2 = "content";
|
||||
|
||||
strdup(ptr1); // BAD
|
||||
strdup(ptr2); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer1[1024];
|
||||
char buffer2[1024];
|
||||
char *ptr;
|
||||
|
||||
ptr = buffer1;
|
||||
strdup(buffer1); // BAD
|
||||
strdup(ptr); // BAD
|
||||
|
||||
strcpy(buffer1, "content");
|
||||
strdup(buffer1); // GOOD
|
||||
strdup(ptr); // GOOD
|
||||
|
||||
ptr = buffer1;
|
||||
strdup(buffer1); // GOOD
|
||||
strdup(ptr); // GOOD
|
||||
|
||||
ptr = buffer2;
|
||||
strdup(buffer2); // BAD
|
||||
strdup(ptr); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
if (cond())
|
||||
{
|
||||
strcpy(buffer, "content");
|
||||
strdup(buffer); // GOOD
|
||||
}
|
||||
strdup(buffer); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
if (cond())
|
||||
{
|
||||
strcpy(buffer, "content");
|
||||
} else {
|
||||
strcpy(buffer, "alternative");
|
||||
}
|
||||
strdup(buffer); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
while (cond())
|
||||
{
|
||||
strcpy(buffer, "content");
|
||||
strdup(buffer); // GOOD
|
||||
}
|
||||
strdup(buffer); // BAD
|
||||
}
|
||||
}
|
||||
|
||||
void test_callee(char *p1, char *p2)
|
||||
{
|
||||
strdup(p1);
|
||||
}
|
||||
|
||||
void test_caller()
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
test_callee("content", buffer); // GOOD
|
||||
test_callee(buffer, "content"); // BAD
|
||||
}
|
||||
|
||||
void test_readlink(int fd, const char *path, size_t sz)
|
||||
{
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
readlink(path, buffer, sizeof(buffer));
|
||||
strdup(buffer); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
int v;
|
||||
|
||||
readlinkat(fd, path, buffer, sizeof(buffer));
|
||||
v = strlen(buffer); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024] = {0};
|
||||
|
||||
readlink(path, buffer, sizeof(buffer) - 1);
|
||||
strdup(buffer); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
readlink(path, buffer, sizeof(buffer) - 1);
|
||||
strdup(buffer); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
readlink(path, buffer, sizeof(buffer));
|
||||
strdup(buffer); // BAD
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
readlink(path, buffer, sizeof(buffer));
|
||||
buffer[sizeof(buffer) - 1] = 0;
|
||||
strdup(buffer); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char *buffer = (char *)malloc(1024);
|
||||
|
||||
readlink(path, buffer, 1024);
|
||||
strdup(buffer); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
{
|
||||
char *buffer = (char *)malloc(1024);
|
||||
|
||||
buffer[1023] = 0;
|
||||
readlink(path, buffer, 1023);
|
||||
strdup(buffer); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char *buffer = (char *)malloc(sz);
|
||||
|
||||
readlink(path, buffer, sz);
|
||||
strdup(buffer); // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
{
|
||||
char *buffer = (char *)malloc(sz);
|
||||
|
||||
memset(buffer, 0, sz);
|
||||
readlink(path, buffer, sz - 1);
|
||||
strdup(buffer); // GOOD
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
edges
|
||||
| test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r |
|
||||
| test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r |
|
||||
| test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r |
|
||||
| test.c:75:13:75:19 | call to rand | test.c:77:9:77:9 | r |
|
||||
| test.c:75:13:75:19 | call to rand | test.c:77:9:77:9 | r |
|
||||
| test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r |
|
||||
| test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r |
|
||||
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
|
||||
| test.c:125:13:125:16 | call to rand | test.c:127:9:127:9 | r |
|
||||
| test.c:148:22:148:25 | call to rand | test.c:150:9:150:9 | r |
|
||||
| test.c:148:22:148:27 | (unsigned int)... | test.c:150:9:150:9 | r |
|
||||
| test.cpp:8:9:8:12 | Store | test.cpp:24:11:24:18 | call to get_rand |
|
||||
| test.cpp:8:9:8:12 | call to rand | test.cpp:8:9:8:12 | Store |
|
||||
| test.cpp:13:2:13:15 | Chi [[]] | test.cpp:30:13:30:14 | get_rand2 output argument [[]] |
|
||||
| test.cpp:13:10:13:13 | call to rand | test.cpp:13:2:13:15 | Chi [[]] |
|
||||
| test.cpp:18:2:18:14 | Chi [[]] | test.cpp:36:13:36:13 | get_rand3 output argument [[]] |
|
||||
| test.cpp:18:9:18:12 | call to rand | test.cpp:18:2:18:14 | Chi [[]] |
|
||||
| test.cpp:24:11:24:18 | call to get_rand | test.cpp:25:7:25:7 | r |
|
||||
| test.cpp:30:13:30:14 | Chi | test.cpp:31:7:31:7 | r |
|
||||
| test.cpp:30:13:30:14 | get_rand2 output argument [[]] | test.cpp:30:13:30:14 | Chi |
|
||||
| test.cpp:36:13:36:13 | Chi | test.cpp:37:7:37:7 | r |
|
||||
| test.cpp:36:13:36:13 | get_rand3 output argument [[]] | test.cpp:36:13:36:13 | Chi |
|
||||
| test.cpp:78:10:78:13 | call to rand | test.cpp:82:10:82:10 | x |
|
||||
| test.cpp:90:10:90:13 | call to rand | test.cpp:94:10:94:10 | x |
|
||||
| test.cpp:129:10:129:13 | call to rand | test.cpp:132:10:132:10 | b |
|
||||
| test.cpp:147:11:147:14 | call to rand | test.cpp:149:11:149:16 | (int)... |
|
||||
| test.cpp:147:11:147:14 | call to rand | test.cpp:149:16:149:16 | y |
|
||||
nodes
|
||||
| test.c:18:13:18:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:21:17:21:17 | r | semmle.label | r |
|
||||
| test.c:34:13:34:18 | call to rand | semmle.label | call to rand |
|
||||
| test.c:35:5:35:5 | r | semmle.label | r |
|
||||
| test.c:44:13:44:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:45:5:45:5 | r | semmle.label | r |
|
||||
| test.c:75:13:75:19 | call to rand | semmle.label | call to rand |
|
||||
| test.c:75:13:75:19 | call to rand | semmle.label | call to rand |
|
||||
| test.c:77:9:77:9 | r | semmle.label | r |
|
||||
| test.c:81:14:81:17 | call to rand | semmle.label | call to rand |
|
||||
| test.c:81:23:81:26 | call to rand | semmle.label | call to rand |
|
||||
| test.c:83:9:83:9 | r | semmle.label | r |
|
||||
| test.c:99:14:99:19 | call to rand | semmle.label | call to rand |
|
||||
| test.c:100:5:100:5 | r | semmle.label | r |
|
||||
| test.c:125:13:125:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:127:9:127:9 | r | semmle.label | r |
|
||||
| test.c:148:22:148:25 | call to rand | semmle.label | call to rand |
|
||||
| test.c:148:22:148:27 | (unsigned int)... | semmle.label | (unsigned int)... |
|
||||
| test.c:150:9:150:9 | r | semmle.label | r |
|
||||
| test.cpp:8:9:8:12 | Store | semmle.label | Store |
|
||||
| test.cpp:8:9:8:12 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:13:2:13:15 | Chi [[]] | semmle.label | Chi [[]] |
|
||||
| test.cpp:13:10:13:13 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:18:2:18:14 | Chi [[]] | semmle.label | Chi [[]] |
|
||||
| test.cpp:18:9:18:12 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:24:11:24:18 | call to get_rand | semmle.label | call to get_rand |
|
||||
| test.cpp:25:7:25:7 | r | semmle.label | r |
|
||||
| test.cpp:30:13:30:14 | Chi | semmle.label | Chi |
|
||||
| test.cpp:30:13:30:14 | get_rand2 output argument [[]] | semmle.label | get_rand2 output argument [[]] |
|
||||
| test.cpp:31:7:31:7 | r | semmle.label | r |
|
||||
| test.cpp:36:13:36:13 | Chi | semmle.label | Chi |
|
||||
| test.cpp:36:13:36:13 | get_rand3 output argument [[]] | semmle.label | get_rand3 output argument [[]] |
|
||||
| test.cpp:37:7:37:7 | r | semmle.label | r |
|
||||
| test.cpp:78:10:78:13 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:82:10:82:10 | x | semmle.label | x |
|
||||
| test.cpp:90:10:90:13 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:94:10:94:10 | x | semmle.label | x |
|
||||
| test.cpp:129:10:129:13 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:132:10:132:10 | b | semmle.label | b |
|
||||
| test.cpp:147:11:147:14 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:149:11:149:16 | (int)... | semmle.label | (int)... |
|
||||
| test.cpp:149:16:149:16 | y | semmle.label | y |
|
||||
#select
|
||||
| test.c:21:17:21:17 | r | test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value |
|
||||
| test.c:35:5:35:5 | r | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:34:13:34:18 | call to rand | Uncontrolled value |
|
||||
| test.c:45:5:45:5 | r | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:44:13:44:16 | call to rand | Uncontrolled value |
|
||||
| test.c:77:9:77:9 | r | test.c:75:13:75:19 | call to rand | test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:75:13:75:19 | call to rand | Uncontrolled value |
|
||||
| test.c:77:9:77:9 | r | test.c:75:13:75:19 | call to rand | test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:75:13:75:19 | call to rand | Uncontrolled value |
|
||||
| test.c:83:9:83:9 | r | test.c:81:14:81:17 | call to rand | test.c:83:9:83:9 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:81:14:81:17 | call to rand | Uncontrolled value |
|
||||
| test.c:83:9:83:9 | r | test.c:81:23:81:26 | call to rand | test.c:83:9:83:9 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:81:23:81:26 | call to rand | Uncontrolled value |
|
||||
| test.c:100:5:100:5 | r | test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:99:14:99:19 | call to rand | Uncontrolled value |
|
||||
| test.c:127:9:127:9 | r | test.c:125:13:125:16 | call to rand | test.c:127:9:127:9 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:125:13:125:16 | call to rand | Uncontrolled value |
|
||||
| test.c:150:9:150:9 | r | test.c:148:22:148:25 | call to rand | test.c:150:9:150:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:148:22:148:25 | call to rand | Uncontrolled value |
|
||||
| test.c:150:9:150:9 | r | test.c:148:22:148:27 | (unsigned int)... | test.c:150:9:150:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:148:22:148:25 | call to rand | Uncontrolled value |
|
||||
| test.cpp:25:7:25:7 | r | test.cpp:8:9:8:12 | call to rand | test.cpp:25:7:25:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:8:9:8:12 | call to rand | Uncontrolled value |
|
||||
| test.cpp:31:7:31:7 | r | test.cpp:13:10:13:13 | call to rand | test.cpp:31:7:31:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:13:10:13:13 | call to rand | Uncontrolled value |
|
||||
| test.cpp:37:7:37:7 | r | test.cpp:18:9:18:12 | call to rand | test.cpp:37:7:37:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:18:9:18:12 | call to rand | Uncontrolled value |
|
||||
| test.cpp:82:10:82:10 | x | test.cpp:78:10:78:13 | call to rand | test.cpp:82:10:82:10 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:78:10:78:13 | call to rand | Uncontrolled value |
|
||||
| test.cpp:94:10:94:10 | x | test.cpp:90:10:90:13 | call to rand | test.cpp:94:10:94:10 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:90:10:90:13 | call to rand | Uncontrolled value |
|
||||
| test.cpp:132:10:132:10 | b | test.cpp:129:10:129:13 | call to rand | test.cpp:132:10:132:10 | b | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:129:10:129:13 | call to rand | Uncontrolled value |
|
||||
| test.cpp:149:11:149:16 | (int)... | test.cpp:147:11:147:14 | call to rand | test.cpp:149:11:149:16 | (int)... | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:147:11:147:14 | call to rand | Uncontrolled value |
|
||||
| test.cpp:149:16:149:16 | y | test.cpp:147:11:147:14 | call to rand | test.cpp:149:16:149:16 | y | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:147:11:147:14 | call to rand | Uncontrolled value |
|
||||
@@ -74,30 +74,30 @@ void randomTester() {
|
||||
{
|
||||
int r = RAND2();
|
||||
|
||||
r = r - 100; // BAD
|
||||
r = r + 100; // BAD
|
||||
}
|
||||
|
||||
{
|
||||
int r = (rand() ^ rand());
|
||||
|
||||
r = r - 100; // BAD [NOT DETECTED]
|
||||
r = r + 100; // BAD
|
||||
}
|
||||
|
||||
{
|
||||
int r = RAND2() - 100; // BAD [NOT DETECTED]
|
||||
int r = RAND2() + 100; // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
{
|
||||
int r = RAND();
|
||||
int *ptr_r = &r;
|
||||
*ptr_r -= 100; // BAD [NOT DETECTED]
|
||||
*ptr_r += 100; // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
{
|
||||
int r = 0;
|
||||
int *ptr_r = &r;
|
||||
*ptr_r = RAND();
|
||||
r -= 100; // BAD
|
||||
r += 100; // BAD
|
||||
}
|
||||
|
||||
{
|
||||
@@ -109,4 +109,44 @@ void randomTester() {
|
||||
|
||||
void add_100(int r) {
|
||||
r += 100; // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
void randomTester2(int bound, int min, int max) {
|
||||
int r1 = rand() % bound;
|
||||
r1 += 100; // GOOD (`bound` may possibly be MAX_INT in which case this could
|
||||
// still overflow, but it's most likely fine)
|
||||
|
||||
int r2 = (rand() % (max - min + 1)) + min;
|
||||
r2 += 100; // GOOD (This is a common way to clamp the random value between [min, max])
|
||||
}
|
||||
|
||||
void moreTests() {
|
||||
{
|
||||
int r = rand();
|
||||
|
||||
r = r * 100; // BAD
|
||||
}
|
||||
{
|
||||
int r = rand();
|
||||
|
||||
r *= 100; // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
{
|
||||
int r = rand();
|
||||
|
||||
r <<= 8; // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
{
|
||||
int r = rand();
|
||||
|
||||
r = r - 100; // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
unsigned int r = rand();
|
||||
|
||||
r = r - 100; // BAD
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
// Semmle test case for rule ArithmeticUncontrolled.ql (Uncontrolled data in arithmetic expression).
|
||||
// Associated with CWE-190: Integer Overflow or Wraparound. http://cwe.mitre.org/data/definitions/190.html
|
||||
|
||||
int rand(void);
|
||||
|
||||
int get_rand()
|
||||
{
|
||||
return rand();
|
||||
}
|
||||
|
||||
void get_rand2(int *dest)
|
||||
{
|
||||
*dest = rand();
|
||||
}
|
||||
|
||||
void get_rand3(int &dest)
|
||||
{
|
||||
dest = rand();
|
||||
}
|
||||
|
||||
void randomTester2()
|
||||
{
|
||||
{
|
||||
int r = get_rand();
|
||||
r = r + 100; // BAD
|
||||
}
|
||||
|
||||
{
|
||||
int r;
|
||||
get_rand2(&r);
|
||||
r = r + 100; // BAD
|
||||
}
|
||||
|
||||
{
|
||||
int r;
|
||||
get_rand3(r);
|
||||
r = r + 100; // BAD
|
||||
}
|
||||
}
|
||||
|
||||
int rand(int min, int max);
|
||||
unsigned rand(int max);
|
||||
|
||||
void test_with_bounded_randomness() {
|
||||
int r = rand(0, 10);
|
||||
r++; // GOOD
|
||||
|
||||
unsigned unsigned_r = rand(10);
|
||||
unsigned_r++; // GOOD
|
||||
}
|
||||
|
||||
int test_remainder_subtract()
|
||||
{
|
||||
int x = rand();
|
||||
int y = x % 100; // y <= x
|
||||
|
||||
return x - y; // GOOD (as y <= x)
|
||||
}
|
||||
|
||||
typedef unsigned long size_t;
|
||||
int snprintf(char *s, size_t n, const char *format, ...);
|
||||
|
||||
int test_buffer(char *buf_start, char *buf_end)
|
||||
{
|
||||
int len = buf_end - buf_start;
|
||||
|
||||
return len * 2; // GOOD
|
||||
}
|
||||
|
||||
int test_snprintf(char *buf, size_t buf_sz)
|
||||
{
|
||||
snprintf(buf, buf_sz, "my random number: %i\n", rand());
|
||||
test_buffer(buf, buf + buf_sz);
|
||||
}
|
||||
|
||||
int test_else_1()
|
||||
{
|
||||
int x = rand();
|
||||
|
||||
if (x > 100)
|
||||
{
|
||||
return x * 10; // BAD
|
||||
} else {
|
||||
return x * 10; // GOOD (as x <= 100)
|
||||
}
|
||||
}
|
||||
|
||||
int test_else_2()
|
||||
{
|
||||
int x = rand();
|
||||
|
||||
if (x > 100)
|
||||
{
|
||||
return x * 10; // BAD
|
||||
}
|
||||
|
||||
return x * 10; // GOOD (as x <= 100)
|
||||
}
|
||||
|
||||
int test_conditional_assignment_1()
|
||||
{
|
||||
int x = rand();
|
||||
int y = 100;
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
y = x;
|
||||
return y * 10; // GOOD (as y <= 100)
|
||||
} else {
|
||||
return y * 10; // GOOD (as y = 100)
|
||||
}
|
||||
}
|
||||
|
||||
int test_conditional_assignment_2()
|
||||
{
|
||||
int x = rand();
|
||||
int y = 100;
|
||||
|
||||
if (x < y)
|
||||
{
|
||||
y = x;
|
||||
}
|
||||
|
||||
return y * 10; // GOOD (as y <= 100)
|
||||
}
|
||||
|
||||
int test_underflow()
|
||||
{
|
||||
int x = rand();
|
||||
int a = -x; // GOOD
|
||||
int b = 10 - x; // GOOD
|
||||
int c = b * 2; // BAD
|
||||
}
|
||||
|
||||
int test_cast()
|
||||
{
|
||||
int x = rand();
|
||||
short a = x; // BAD [NOT DETECTED]
|
||||
short b = -x; // BAD [NOT DETECTED]
|
||||
long long c = x; // GOOD
|
||||
long long d = -x; // GOOD
|
||||
}
|
||||
|
||||
void test_float()
|
||||
{
|
||||
{
|
||||
int x = rand();
|
||||
float y = x; // GOOD
|
||||
int z = (int)y * 5; // BAD
|
||||
}
|
||||
|
||||
{
|
||||
int x = rand();
|
||||
float y = x * 5.0f; // GOOD
|
||||
int z = y; // BAD [NOT DETECTED]
|
||||
}
|
||||
|
||||
{
|
||||
int x = rand();
|
||||
float y = x / 10.0f; // GOOD
|
||||
int z = (int)y * 5; // GOOD
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
edges
|
||||
| test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r |
|
||||
| test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r |
|
||||
| test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r |
|
||||
| test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r |
|
||||
| test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r |
|
||||
| test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r |
|
||||
| test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r |
|
||||
| test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r |
|
||||
| test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r |
|
||||
| test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r |
|
||||
| test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r |
|
||||
| test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r |
|
||||
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
|
||||
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
|
||||
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
|
||||
| test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r |
|
||||
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
|
||||
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
|
||||
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
|
||||
| test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r |
|
||||
| test.cpp:8:9:8:12 | Store | test.cpp:24:11:24:18 | call to get_rand |
|
||||
| test.cpp:8:9:8:12 | call to rand | test.cpp:8:9:8:12 | Store |
|
||||
| test.cpp:8:9:8:12 | call to rand | test.cpp:8:9:8:12 | Store |
|
||||
| test.cpp:13:2:13:15 | Chi [[]] | test.cpp:30:13:30:14 | get_rand2 output argument [[]] |
|
||||
| test.cpp:13:10:13:13 | call to rand | test.cpp:13:2:13:15 | Chi [[]] |
|
||||
| test.cpp:13:10:13:13 | call to rand | test.cpp:13:2:13:15 | Chi [[]] |
|
||||
| test.cpp:18:2:18:14 | Chi [[]] | test.cpp:36:13:36:13 | get_rand3 output argument [[]] |
|
||||
| test.cpp:18:9:18:12 | call to rand | test.cpp:18:2:18:14 | Chi [[]] |
|
||||
| test.cpp:18:9:18:12 | call to rand | test.cpp:18:2:18:14 | Chi [[]] |
|
||||
| test.cpp:24:11:24:18 | call to get_rand | test.cpp:25:7:25:7 | r |
|
||||
| test.cpp:24:11:24:18 | call to get_rand | test.cpp:25:7:25:7 | r |
|
||||
| test.cpp:30:13:30:14 | Chi | test.cpp:31:7:31:7 | r |
|
||||
| test.cpp:30:13:30:14 | Chi | test.cpp:31:7:31:7 | r |
|
||||
| test.cpp:30:13:30:14 | get_rand2 output argument [[]] | test.cpp:30:13:30:14 | Chi |
|
||||
| test.cpp:36:13:36:13 | Chi | test.cpp:37:7:37:7 | r |
|
||||
| test.cpp:36:13:36:13 | Chi | test.cpp:37:7:37:7 | r |
|
||||
| test.cpp:36:13:36:13 | get_rand3 output argument [[]] | test.cpp:36:13:36:13 | Chi |
|
||||
nodes
|
||||
| test.c:18:13:18:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:18:13:18:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:21:17:21:17 | r | semmle.label | r |
|
||||
| test.c:21:17:21:17 | r | semmle.label | r |
|
||||
| test.c:21:17:21:17 | r | semmle.label | r |
|
||||
| test.c:34:13:34:18 | call to rand | semmle.label | call to rand |
|
||||
| test.c:34:13:34:18 | call to rand | semmle.label | call to rand |
|
||||
| test.c:35:5:35:5 | r | semmle.label | r |
|
||||
| test.c:35:5:35:5 | r | semmle.label | r |
|
||||
| test.c:35:5:35:5 | r | semmle.label | r |
|
||||
| test.c:44:13:44:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:44:13:44:16 | call to rand | semmle.label | call to rand |
|
||||
| test.c:45:5:45:5 | r | semmle.label | r |
|
||||
| test.c:45:5:45:5 | r | semmle.label | r |
|
||||
| test.c:45:5:45:5 | r | semmle.label | r |
|
||||
| test.c:75:13:75:19 | ... ^ ... | semmle.label | ... ^ ... |
|
||||
| test.c:75:13:75:19 | ... ^ ... | semmle.label | ... ^ ... |
|
||||
| test.c:77:9:77:9 | r | semmle.label | r |
|
||||
| test.c:77:9:77:9 | r | semmle.label | r |
|
||||
| test.c:77:9:77:9 | r | semmle.label | r |
|
||||
| test.c:99:14:99:19 | call to rand | semmle.label | call to rand |
|
||||
| test.c:99:14:99:19 | call to rand | semmle.label | call to rand |
|
||||
| test.c:100:5:100:5 | r | semmle.label | r |
|
||||
| test.c:100:5:100:5 | r | semmle.label | r |
|
||||
| test.c:100:5:100:5 | r | semmle.label | r |
|
||||
| test.cpp:8:9:8:12 | Store | semmle.label | Store |
|
||||
| test.cpp:8:9:8:12 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:8:9:8:12 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:13:2:13:15 | Chi [[]] | semmle.label | Chi [[]] |
|
||||
| test.cpp:13:2:13:15 | ChiPartial | semmle.label | ChiPartial |
|
||||
| test.cpp:13:10:13:13 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:13:10:13:13 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:18:2:18:14 | Chi [[]] | semmle.label | Chi [[]] |
|
||||
| test.cpp:18:2:18:14 | ChiPartial | semmle.label | ChiPartial |
|
||||
| test.cpp:18:9:18:12 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:18:9:18:12 | call to rand | semmle.label | call to rand |
|
||||
| test.cpp:24:11:24:18 | call to get_rand | semmle.label | call to get_rand |
|
||||
| test.cpp:25:7:25:7 | r | semmle.label | r |
|
||||
| test.cpp:25:7:25:7 | r | semmle.label | r |
|
||||
| test.cpp:25:7:25:7 | r | semmle.label | r |
|
||||
| test.cpp:30:13:30:14 | Chi | semmle.label | Chi |
|
||||
| test.cpp:30:13:30:14 | get_rand2 output argument [[]] | semmle.label | get_rand2 output argument [[]] |
|
||||
| test.cpp:31:7:31:7 | r | semmle.label | r |
|
||||
| test.cpp:31:7:31:7 | r | semmle.label | r |
|
||||
| test.cpp:31:7:31:7 | r | semmle.label | r |
|
||||
| test.cpp:36:13:36:13 | Chi | semmle.label | Chi |
|
||||
| test.cpp:36:13:36:13 | get_rand3 output argument [[]] | semmle.label | get_rand3 output argument [[]] |
|
||||
| test.cpp:37:7:37:7 | r | semmle.label | r |
|
||||
| test.cpp:37:7:37:7 | r | semmle.label | r |
|
||||
| test.cpp:37:7:37:7 | r | semmle.label | r |
|
||||
#select
|
||||
| test.c:21:17:21:17 | r | test.c:18:13:18:16 | call to rand | test.c:21:17:21:17 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:18:13:18:16 | call to rand | Uncontrolled value |
|
||||
| test.c:35:5:35:5 | r | test.c:34:13:34:18 | call to rand | test.c:35:5:35:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:34:13:34:18 | call to rand | Uncontrolled value |
|
||||
| test.c:45:5:45:5 | r | test.c:44:13:44:16 | call to rand | test.c:45:5:45:5 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:44:13:44:16 | call to rand | Uncontrolled value |
|
||||
| test.c:77:9:77:9 | r | test.c:75:13:75:19 | ... ^ ... | test.c:77:9:77:9 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:75:13:75:19 | ... ^ ... | Uncontrolled value |
|
||||
| test.c:100:5:100:5 | r | test.c:99:14:99:19 | call to rand | test.c:100:5:100:5 | r | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:99:14:99:19 | call to rand | Uncontrolled value |
|
||||
| test.cpp:25:7:25:7 | r | test.cpp:8:9:8:12 | call to rand | test.cpp:25:7:25:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:8:9:8:12 | call to rand | Uncontrolled value |
|
||||
| test.cpp:31:7:31:7 | r | test.cpp:13:10:13:13 | call to rand | test.cpp:31:7:31:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:13:10:13:13 | call to rand | Uncontrolled value |
|
||||
| test.cpp:37:7:37:7 | r | test.cpp:18:9:18:12 | call to rand | test.cpp:37:7:37:7 | r | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.cpp:18:9:18:12 | call to rand | Uncontrolled value |
|
||||
@@ -1,50 +0,0 @@
|
||||
// Semmle test case for rule ArithmeticUncontrolled.ql (Uncontrolled data in arithmetic expression).
|
||||
// Associated with CWE-190: Integer Overflow or Wraparound. http://cwe.mitre.org/data/definitions/190.html
|
||||
|
||||
int rand(void);
|
||||
|
||||
int get_rand()
|
||||
{
|
||||
return rand();
|
||||
}
|
||||
|
||||
void get_rand2(int *dest)
|
||||
{
|
||||
*dest = rand();
|
||||
}
|
||||
|
||||
void get_rand3(int &dest)
|
||||
{
|
||||
dest = rand();
|
||||
}
|
||||
|
||||
void randomTester2()
|
||||
{
|
||||
{
|
||||
int r = get_rand();
|
||||
r = r + 100; // BAD
|
||||
}
|
||||
|
||||
{
|
||||
int r;
|
||||
get_rand2(&r);
|
||||
r = r + 100; // BAD
|
||||
}
|
||||
|
||||
{
|
||||
int r;
|
||||
get_rand3(r);
|
||||
r = r + 100; // BAD
|
||||
}
|
||||
}
|
||||
|
||||
int rand(int min, int max);
|
||||
unsigned rand(int max);
|
||||
|
||||
void test_with_bounded_randomness() {
|
||||
int r = rand(0, 10);
|
||||
r++; // GOOD
|
||||
|
||||
unsigned unsigned_r = rand(10);
|
||||
unsigned_r++; // GOOD
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
| test2.cpp:43:2:43:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:43:36:43:43 | password | this source. |
|
||||
| test2.cpp:44:2:44:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:44:37:44:45 | thepasswd | this source. |
|
||||
| test2.cpp:50:2:50:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:50:41:50:53 | passwd_config | this source. |
|
||||
| test2.cpp:54:2:54:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:54:41:54:52 | widepassword | this source. |
|
||||
| test2.cpp:55:2:55:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:55:40:55:51 | widepassword | this source. |
|
||||
| test2.cpp:57:2:57:8 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:57:39:57:49 | call to getPassword | this source. |
|
||||
| test2.cpp:65:3:65:9 | call to fprintf | This write into file 'log' may contain unencrypted data from $@ | test2.cpp:62:18:62:25 | password | this source. |
|
||||
| test.cpp:45:3:45:7 | call to fputs | This write into file 'file' may contain unencrypted data from $@ | test.cpp:45:9:45:19 | thePassword | this source. |
|
||||
| test.cpp:70:35:70:35 | call to operator<< | This write into file 'mystream' may contain unencrypted data from $@ | test.cpp:70:38:70:48 | thePassword | this source. |
|
||||
| test.cpp:73:37:73:41 | call to write | This write into file 'mystream' may contain unencrypted data from $@ | test.cpp:73:43:73:53 | thePassword | this source. |
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
|
||||
#define FILE int
|
||||
|
||||
typedef unsigned long size_t;
|
||||
|
||||
FILE *fopen(const char *filename, const char *mode);
|
||||
int snprintf(char *s, size_t n, const char *format, ...);
|
||||
int fprintf(FILE *stream, const char *format, ...);
|
||||
char *strcpy(char *s1, const char *s2);
|
||||
|
||||
char *crypt(char *input);
|
||||
|
||||
struct myStruct
|
||||
{
|
||||
// sensitive
|
||||
char *password;
|
||||
char *thepasswd;
|
||||
char *accountkey;
|
||||
wchar_t *widepassword;
|
||||
|
||||
// encrypted
|
||||
char password_hash[64];
|
||||
char *encrypted_passwd;
|
||||
|
||||
// not sensitive
|
||||
char *password_file;
|
||||
char *password_path;
|
||||
int num_passwords;
|
||||
int *password_tries;
|
||||
bool have_passwd;
|
||||
|
||||
// dubious
|
||||
char *passwd_config;
|
||||
char *passwd_config2;
|
||||
};
|
||||
|
||||
char *getPassword();
|
||||
char *getPasswordHash();
|
||||
int getPasswordMaxChars();
|
||||
|
||||
void tests(FILE *log, myStruct &s)
|
||||
{
|
||||
fprintf(log, "password = %s\n", s.password); // BAD
|
||||
fprintf(log, "thepasswd = %s\n", s.thepasswd); // BAD
|
||||
fprintf(log, "accountkey = %s\n", s.accountkey); // DUBIOUS [NOT REPORTED]
|
||||
fprintf(log, "password_hash = %s\n", s.password_hash); // GOOD
|
||||
fprintf(log, "encrypted_passwd = %s\n", s.encrypted_passwd); // GOOD
|
||||
fprintf(log, "password_file = %s\n", s.password_file); // GOOD
|
||||
fprintf(log, "password_path = %s\n", s.password_path); // GOOD
|
||||
fprintf(log, "passwd_config = %s\n", s.passwd_config); // DUBIOUS [REPORTED]
|
||||
fprintf(log, "num_passwords = %i\n", s.num_passwords); // GOOD
|
||||
fprintf(log, "password_tries = %i\n", *(s.password_tries)); // GOOD
|
||||
fprintf(log, "have_passwd = %i\n", s.have_passwd); // GOOD
|
||||
fprintf(log, "widepassword = %ls\n", s.widepassword); // BAD
|
||||
fprintf(log, "widepassword = %S\n", s.widepassword); // BAD
|
||||
|
||||
fprintf(log, "getPassword() = %s\n", getPassword()); // BAD
|
||||
fprintf(log, "getPasswordHash() = %s\n", getPasswordHash()); // GOOD
|
||||
fprintf(log, "getPasswordMaxChars() = %i\n", getPasswordMaxChars()); // GOOD
|
||||
|
||||
{
|
||||
char *cpy1 = s.password;
|
||||
char *cpy2 = crypt(s.password);
|
||||
|
||||
fprintf(log, "cpy1 = %s\n", cpy1); // BAD
|
||||
fprintf(log, "cpy2 = %s\n", cpy2); // GOOD
|
||||
}
|
||||
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
strcpy(buf, s.password);
|
||||
fprintf(log, "buf = %s\n", buf); // BAD [NOT DETECTED]
|
||||
|
||||
strcpy(buf, s.password_hash);
|
||||
fprintf(log, "buf = %s\n", buf); // GOOD
|
||||
}
|
||||
|
||||
fprintf(log, "password = %p\n", s.password); // GOOD
|
||||
|
||||
{
|
||||
if (fopen(s.passwd_config2, "rt") == 0)
|
||||
{
|
||||
fprintf(log, "could not open file '%s'.\n", s.passwd_config2); // GOOD
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
snprintf(buffer, 1024, "password = %s", s.password);
|
||||
fprintf(log, "log: %s", buffer); // BAD [NOT DETECTED]
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,13 @@
|
||||
| test.cpp:21:3:21:8 | call to remove | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test.cpp:21:10:21:14 | file1 | filename | test.cpp:19:7:19:12 | call to rename | checked |
|
||||
| test.cpp:35:3:35:8 | call to remove | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test.cpp:35:10:35:14 | file1 | filename | test.cpp:32:7:32:12 | call to rename | checked |
|
||||
| test.cpp:49:3:49:8 | call to remove | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test.cpp:49:10:49:14 | file1 | filename | test.cpp:47:7:47:12 | call to rename | checked |
|
||||
| test2.cpp:69:7:69:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:69:13:69:16 | path | filename | test2.cpp:67:6:67:9 | call to stat | checked |
|
||||
| test2.cpp:83:7:83:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:83:13:83:16 | path | filename | test2.cpp:81:6:81:8 | buf | checked |
|
||||
| test2.cpp:98:7:98:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:98:13:98:16 | path | filename | test2.cpp:96:6:96:12 | buf_ptr | checked |
|
||||
| test2.cpp:115:7:115:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:115:13:115:16 | path | filename | test2.cpp:113:22:113:24 | buf | checked |
|
||||
| test2.cpp:130:7:130:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:130:13:130:16 | path | filename | test2.cpp:128:21:128:27 | buf_ptr | checked |
|
||||
| test2.cpp:157:7:157:10 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:157:12:157:15 | path | filename | test2.cpp:155:6:155:9 | call to stat | checked |
|
||||
| test2.cpp:170:7:170:10 | call to open | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:170:12:170:15 | path | filename | test2.cpp:168:6:168:10 | call to lstat | checked |
|
||||
| test2.cpp:245:3:245:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:245:9:245:12 | path | filename | test2.cpp:238:6:238:10 | call to fopen | checked |
|
||||
| test2.cpp:277:7:277:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:277:13:277:16 | path | filename | test2.cpp:275:6:275:11 | call to access | checked |
|
||||
| test2.cpp:303:7:303:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:303:13:303:16 | path | filename | test2.cpp:301:7:301:12 | call to access | checked |
|
||||
| test2.cpp:317:7:317:11 | call to fopen | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:317:13:317:16 | path | filename | test2.cpp:313:6:313:11 | call to access | checked |
|
||||
| test2.cpp:348:3:348:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:348:9:348:12 | path | filename | test2.cpp:341:6:341:10 | call to fopen | checked |
|
||||
| test2.cpp:356:3:356:7 | call to chmod | The $@ being operated upon was previously $@, but the underlying file may have been changed since then. | test2.cpp:356:9:356:13 | path2 | filename | test2.cpp:354:7:354:12 | call to rename | checked |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user