mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Add NumericCastTaintedQuery
This commit is contained in:
@@ -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>;
|
||||
Reference in New Issue
Block a user