Add NumericCastTaintedQuery

This commit is contained in:
Ed Minnix
2023-04-10 12:00:04 -04:00
parent e65a54b85f
commit f4a6f555b4
4 changed files with 139 additions and 117 deletions

View File

@@ -0,0 +1,133 @@
/** Provides classes to reason about possible truncation from casting of a user-provided value. */
import java
import semmle.code.java.arithmetic.Overflow
import semmle.code.java.dataflow.SSA
import semmle.code.java.controlflow.Guards
import semmle.code.java.dataflow.RangeAnalysis
import semmle.code.java.dataflow.FlowSources
/**
* A `CastExpr` that is a narrowing cast.
*/
class NumericNarrowingCastExpr extends CastExpr {
NumericNarrowingCastExpr() {
exists(NumericType sourceType, NumericType targetType |
sourceType = this.getExpr().getType() and targetType = this.getType()
|
not targetType.(NumType).widerThanOrEqualTo(sourceType)
)
}
}
/**
* An expression that performs a right shift operation.
*/
class RightShiftOp extends Expr {
RightShiftOp() {
this instanceof RightShiftExpr or
this instanceof UnsignedRightShiftExpr or
this instanceof AssignRightShiftExpr or
this instanceof AssignUnsignedRightShiftExpr
}
private Expr getLhs() {
this.(BinaryExpr).getLeftOperand() = result or
this.(Assignment).getDest() = result
}
/**
* Gets the expression that is shifted.
*/
Variable getShiftedVariable() {
this.getLhs() = result.getAnAccess() or
this.getLhs().(AndBitwiseExpr).getAnOperand() = result.getAnAccess()
}
}
private predicate boundedRead(RValue read) {
exists(SsaVariable v, ConditionBlock cb, ComparisonExpr comp, boolean testIsTrue |
read = v.getAUse() and
cb.controls(read.getBasicBlock(), testIsTrue) and
cb.getCondition() = comp
|
comp.getLesserOperand() = v.getAUse() and testIsTrue = true
or
comp.getGreaterOperand() = v.getAUse() and testIsTrue = false
)
}
private predicate castCheck(RValue read) {
exists(EqualityTest eq, CastExpr cast |
cast.getExpr() = read and
eq.hasOperands(cast, read.getVariable().getAnAccess())
)
}
private class SmallType extends Type {
SmallType() {
this instanceof BooleanType or
this.(PrimitiveType).hasName("byte") or
this.(BoxedType).getPrimitiveType().hasName("byte")
}
}
private predicate smallExpr(Expr e) {
exists(int low, int high |
bounded(e, any(ZeroBound zb), low, false, _) and
bounded(e, any(ZeroBound zb), high, true, _) and
high - low < 256
)
}
/**
* A taint-tracking configuration for reasoning about user input that is used in a
* numeric cast.
*/
module NumericCastFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
predicate isSink(DataFlow::Node sink) {
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr() and
sink.asExpr() instanceof VarAccess
}
predicate isBarrier(DataFlow::Node node) {
boundedRead(node.asExpr()) or
castCheck(node.asExpr()) or
node.getType() instanceof SmallType or
smallExpr(node.asExpr()) or
node.getEnclosingCallable() instanceof HashCodeMethod or
exists(RightShiftOp e | e.getShiftedVariable().getAnAccess() = node.asExpr())
}
}
/**
* Taint-tracking flow for user input that is used in a numeric cast.
*/
module NumericCastFlow = TaintTracking::Global<NumericCastFlowConfig>;
/**
* A taint-tracking configuration for reasoning about local user input that is
* used in a numeric cast.
*/
module NumericCastLocalFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
predicate isSink(DataFlow::Node sink) {
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr()
}
predicate isBarrier(DataFlow::Node node) {
boundedRead(node.asExpr()) or
castCheck(node.asExpr()) or
node.getType() instanceof SmallType or
smallExpr(node.asExpr()) or
node.getEnclosingCallable() instanceof HashCodeMethod
}
}
/**
* Taint-tracking flow for local user input that is used in a numeric cast.
*/
module NumericCastLocalFlow = TaintTracking::Global<NumericCastLocalFlowConfig>;