Skip to content

Commit f4a6f55

Browse files
committed
Add NumericCastTaintedQuery
1 parent e65a54b commit f4a6f55

File tree

4 files changed

+139
-117
lines changed

4 files changed

+139
-117
lines changed
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/** Provides classes to reason about possible truncation from casting of a user-provided value. */
2+
3+
import java
4+
import semmle.code.java.arithmetic.Overflow
5+
import semmle.code.java.dataflow.SSA
6+
import semmle.code.java.controlflow.Guards
7+
import semmle.code.java.dataflow.RangeAnalysis
8+
import semmle.code.java.dataflow.FlowSources
9+
10+
/**
11+
* A `CastExpr` that is a narrowing cast.
12+
*/
13+
class NumericNarrowingCastExpr extends CastExpr {
14+
NumericNarrowingCastExpr() {
15+
exists(NumericType sourceType, NumericType targetType |
16+
sourceType = this.getExpr().getType() and targetType = this.getType()
17+
|
18+
not targetType.(NumType).widerThanOrEqualTo(sourceType)
19+
)
20+
}
21+
}
22+
23+
/**
24+
* An expression that performs a right shift operation.
25+
*/
26+
class RightShiftOp extends Expr {
27+
RightShiftOp() {
28+
this instanceof RightShiftExpr or
29+
this instanceof UnsignedRightShiftExpr or
30+
this instanceof AssignRightShiftExpr or
31+
this instanceof AssignUnsignedRightShiftExpr
32+
}
33+
34+
private Expr getLhs() {
35+
this.(BinaryExpr).getLeftOperand() = result or
36+
this.(Assignment).getDest() = result
37+
}
38+
39+
/**
40+
* Gets the expression that is shifted.
41+
*/
42+
Variable getShiftedVariable() {
43+
this.getLhs() = result.getAnAccess() or
44+
this.getLhs().(AndBitwiseExpr).getAnOperand() = result.getAnAccess()
45+
}
46+
}
47+
48+
private predicate boundedRead(RValue read) {
49+
exists(SsaVariable v, ConditionBlock cb, ComparisonExpr comp, boolean testIsTrue |
50+
read = v.getAUse() and
51+
cb.controls(read.getBasicBlock(), testIsTrue) and
52+
cb.getCondition() = comp
53+
|
54+
comp.getLesserOperand() = v.getAUse() and testIsTrue = true
55+
or
56+
comp.getGreaterOperand() = v.getAUse() and testIsTrue = false
57+
)
58+
}
59+
60+
private predicate castCheck(RValue read) {
61+
exists(EqualityTest eq, CastExpr cast |
62+
cast.getExpr() = read and
63+
eq.hasOperands(cast, read.getVariable().getAnAccess())
64+
)
65+
}
66+
67+
private class SmallType extends Type {
68+
SmallType() {
69+
this instanceof BooleanType or
70+
this.(PrimitiveType).hasName("byte") or
71+
this.(BoxedType).getPrimitiveType().hasName("byte")
72+
}
73+
}
74+
75+
private predicate smallExpr(Expr e) {
76+
exists(int low, int high |
77+
bounded(e, any(ZeroBound zb), low, false, _) and
78+
bounded(e, any(ZeroBound zb), high, true, _) and
79+
high - low < 256
80+
)
81+
}
82+
83+
/**
84+
* A taint-tracking configuration for reasoning about user input that is used in a
85+
* numeric cast.
86+
*/
87+
module NumericCastFlowConfig implements DataFlow::ConfigSig {
88+
predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
89+
90+
predicate isSink(DataFlow::Node sink) {
91+
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr() and
92+
sink.asExpr() instanceof VarAccess
93+
}
94+
95+
predicate isBarrier(DataFlow::Node node) {
96+
boundedRead(node.asExpr()) or
97+
castCheck(node.asExpr()) or
98+
node.getType() instanceof SmallType or
99+
smallExpr(node.asExpr()) or
100+
node.getEnclosingCallable() instanceof HashCodeMethod or
101+
exists(RightShiftOp e | e.getShiftedVariable().getAnAccess() = node.asExpr())
102+
}
103+
}
104+
105+
/**
106+
* Taint-tracking flow for user input that is used in a numeric cast.
107+
*/
108+
module NumericCastFlow = TaintTracking::Global<NumericCastFlowConfig>;
109+
110+
/**
111+
* A taint-tracking configuration for reasoning about local user input that is
112+
* used in a numeric cast.
113+
*/
114+
module NumericCastLocalFlowConfig implements DataFlow::ConfigSig {
115+
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
116+
117+
predicate isSink(DataFlow::Node sink) {
118+
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr()
119+
}
120+
121+
predicate isBarrier(DataFlow::Node node) {
122+
boundedRead(node.asExpr()) or
123+
castCheck(node.asExpr()) or
124+
node.getType() instanceof SmallType or
125+
smallExpr(node.asExpr()) or
126+
node.getEnclosingCallable() instanceof HashCodeMethod
127+
}
128+
}
129+
130+
/**
131+
* Taint-tracking flow for local user input that is used in a numeric cast.
132+
*/
133+
module NumericCastLocalFlow = TaintTracking::Global<NumericCastLocalFlowConfig>;

java/ql/src/Security/CWE/CWE-681/NumericCastCommon.qll

Lines changed: 0 additions & 69 deletions
This file was deleted.

java/ql/src/Security/CWE/CWE-681/NumericCastTainted.ql

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,7 @@
1313
*/
1414

1515
import java
16-
import semmle.code.java.dataflow.FlowSources
17-
import NumericCastCommon
18-
19-
module NumericCastFlowConfig implements DataFlow::ConfigSig {
20-
predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
21-
22-
predicate isSink(DataFlow::Node sink) {
23-
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr() and
24-
sink.asExpr() instanceof VarAccess
25-
}
26-
27-
predicate isBarrier(DataFlow::Node node) {
28-
boundedRead(node.asExpr()) or
29-
castCheck(node.asExpr()) or
30-
node.getType() instanceof SmallType or
31-
smallExpr(node.asExpr()) or
32-
node.getEnclosingCallable() instanceof HashCodeMethod or
33-
exists(RightShiftOp e | e.getShiftedVariable().getAnAccess() = node.asExpr())
34-
}
35-
}
36-
37-
module NumericCastFlow = TaintTracking::Global<NumericCastFlowConfig>;
38-
16+
import semmle.code.java.security.NumericCastTaintedQuery
3917
import NumericCastFlow::PathGraph
4018

4119
from NumericCastFlow::PathNode source, NumericCastFlow::PathNode sink, NumericNarrowingCastExpr exp

java/ql/src/Security/CWE/CWE-681/NumericCastTaintedLocal.ql

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,36 +13,16 @@
1313
*/
1414

1515
import java
16-
import semmle.code.java.dataflow.FlowSources
17-
import NumericCastCommon
18-
19-
module NumericCastFlowConfig implements DataFlow::ConfigSig {
20-
predicate isSource(DataFlow::Node src) { src instanceof LocalUserInput }
21-
22-
predicate isSink(DataFlow::Node sink) {
23-
sink.asExpr() = any(NumericNarrowingCastExpr cast).getExpr()
24-
}
25-
26-
predicate isBarrier(DataFlow::Node node) {
27-
boundedRead(node.asExpr()) or
28-
castCheck(node.asExpr()) or
29-
node.getType() instanceof SmallType or
30-
smallExpr(node.asExpr()) or
31-
node.getEnclosingCallable() instanceof HashCodeMethod
32-
}
33-
}
34-
35-
module NumericCastFlow = TaintTracking::Global<NumericCastFlowConfig>;
36-
37-
import NumericCastFlow::PathGraph
16+
import semmle.code.java.security.NumericCastTaintedQuery
17+
import NumericCastLocalFlow::PathGraph
3818

3919
from
40-
NumericCastFlow::PathNode source, NumericCastFlow::PathNode sink, NumericNarrowingCastExpr exp,
41-
VarAccess tainted
20+
NumericCastLocalFlow::PathNode source, NumericCastLocalFlow::PathNode sink,
21+
NumericNarrowingCastExpr exp, VarAccess tainted
4222
where
4323
exp.getExpr() = tainted and
4424
sink.getNode().asExpr() = tainted and
45-
NumericCastFlow::flowPath(source, sink) and
25+
NumericCastLocalFlow::flowPath(source, sink) and
4626
not exists(RightShiftOp e | e.getShiftedVariable() = tainted.getVariable())
4727
select exp, source, sink,
4828
"This cast to a narrower type depends on a $@, potentially causing truncation.", source.getNode(),

0 commit comments

Comments
 (0)