Merge branch 'main' into weak_crypto

This commit is contained in:
Geoffrey White
2021-05-19 11:19:08 +01:00
138 changed files with 5341 additions and 660 deletions

View File

@@ -15,34 +15,99 @@ import cpp
import semmle.code.cpp.security.Overflow
import semmle.code.cpp.security.Security
import semmle.code.cpp.security.TaintTracking
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import TaintedWithPath
predicate isRandCall(FunctionCall fc) { fc.getTarget().getName() = "rand" }
predicate isRandCallOrParent(Expr e) {
isRandCall(e) or
isRandCallOrParent(e.getAChild())
predicate isUnboundedRandCall(FunctionCall fc) {
fc.getTarget().getName() = "rand" and not bounded(fc)
}
predicate isRandValue(Expr e) {
isRandCall(e)
/**
* 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]
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]
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
* bounded by some number that is less than the maximum integer allowed by the result type of `andExpr`.
*/
pragma[inline]
predicate boundedBitwiseAnd(Expr e, Expr andExpr, Expr operand1, Expr operand2) {
operand1 != operand2 and
e = operand1 and
upperBound(operand2.getFullyConverted()) < exprMaxVal(andExpr.getFullyConverted())
}
/**
* Holds if `fc` is a part of the left operand of a binary operation that greatly reduces the range
* of possible values.
*/
predicate bounded(Expr e) {
// 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()))
or
exists(AssignRemExpr rem | boundedRem(e, rem, rem.getLValue(), rem.getRValue()))
or
exists(BitwiseAndExpr andExpr |
boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand())
)
or
exists(AssignAndExpr andExpr |
boundedBitwiseAnd(e, andExpr, andExpr.getAnOperand(), andExpr.getAnOperand())
)
or
// Optimitically assume that a division always yields a much smaller value.
boundedDiv(e, any(DivExpr div).getLeftOperand())
or
boundedDiv(e, any(AssignDivExpr div).getLValue())
}
predicate isUnboundedRandCallOrParent(Expr e) {
isUnboundedRandCall(e)
or
isUnboundedRandCallOrParent(e.getAChild())
}
predicate isUnboundedRandValue(Expr e) {
isUnboundedRandCall(e)
or
exists(MacroInvocation mi |
e = mi.getExpr() and
isRandCallOrParent(e)
isUnboundedRandCallOrParent(e)
)
}
class SecurityOptionsArith extends SecurityOptions {
override predicate isUserInput(Expr expr, string cause) {
isRandValue(expr) and
cause = "rand" and
not expr.getParent*() instanceof DivExpr
isUnboundedRandValue(expr) and
cause = "rand"
}
}
predicate isDiv(VariableAccess va) { exists(AssignDivExpr div | div.getLValue() = va) }
predicate missingGuard(VariableAccess va, string effect) {
exists(Operation op | op.getAnOperand() = va |
missingGuardAgainstUnderflow(op, va) and effect = "underflow"
@@ -52,29 +117,15 @@ predicate missingGuard(VariableAccess va, string effect) {
}
class Configuration extends TaintTrackingConfiguration {
override predicate isSink(Element e) {
isDiv(e)
or
missingGuard(e, _)
}
}
override predicate isSink(Element e) { missingGuard(e, _) }
/**
* A value that undergoes division is likely to be bounded within a safe
* range.
*/
predicate guardedByAssignDiv(Expr origin) {
exists(VariableAccess va |
taintedWithPath(origin, va, _, _) and
isDiv(va)
)
override predicate isBarrier(Expr e) { super.isBarrier(e) or bounded(e) }
}
from Expr origin, VariableAccess va, string effect, PathNode sourceNode, PathNode sinkNode
where
taintedWithPath(origin, va, sourceNode, sinkNode) and
missingGuard(va, effect) and
not guardedByAssignDiv(origin)
missingGuard(va, effect)
select va, sourceNode, sinkNode,
"$@ flows to here and is used in arithmetic, potentially causing an " + effect + ".", origin,
"Uncontrolled value"

View File

@@ -12,6 +12,7 @@
*/
import cpp
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import semmle.code.cpp.security.TaintTracking
import TaintedWithPath
@@ -27,6 +28,27 @@ predicate allocSink(Expr alloc, Expr tainted) {
class TaintedAllocationSizeConfiguration extends TaintTrackingConfiguration {
override predicate isSink(Element tainted) { allocSink(_, tainted) }
override predicate isBarrier(Expr e) {
super.isBarrier(e)
or
// There can be two separate reasons for `convertedExprMightOverflow` not holding:
// 1. `e` really cannot overflow.
// 2. `e` isn't analyzable.
// If we didn't rule out case 2 we would place barriers on anything that isn't analyzable.
(
e instanceof UnaryArithmeticOperation or
e instanceof BinaryArithmeticOperation or
e instanceof AssignArithmeticOperation
) and
not convertedExprMightOverflow(e)
or
// Subtracting two pointers is either well-defined (and the result will likely be small), or
// terribly undefined and dangerous. Here, we assume that the programmer has ensured that the
// result is well-defined (i.e., the two pointers point to the same object), and thus the result
// will likely be small.
e = any(PointerDiffExpr diff).getAnOperand()
}
}
predicate taintedAllocSize(