Skip to content

Commit 00e0e5a

Browse files
committed
Java: Add taint step for InputStream wrappers
1 parent 69ea7d9 commit 00e0e5a

File tree

5 files changed

+140
-1
lines changed

5 files changed

+140
-1
lines changed

java/ql/lib/semmle/code/java/JDK.qll

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import Member
66
import semmle.code.java.security.ExternalProcess
7+
private import semmle.code.java.dataflow.DataFlow
78
private import semmle.code.java.dataflow.FlowSteps
89

910
// --- Standard types ---
@@ -177,6 +178,11 @@ class TypeObjectInputStream extends RefType {
177178
TypeObjectInputStream() { this.hasQualifiedName("java.io", "ObjectInputStream") }
178179
}
179180

181+
/** The class `java.io.InputStream`. */
182+
class TypeInputStream extends RefType {
183+
TypeInputStream() { this.hasQualifiedName("java.io", "InputStream") }
184+
}
185+
180186
/** The class `java.nio.file.Paths`. */
181187
class TypePaths extends Class {
182188
TypePaths() { this.hasQualifiedName("java.nio.file", "Paths") }
@@ -197,6 +203,48 @@ class TypeFile extends Class {
197203
TypeFile() { this.hasQualifiedName("java.io", "File") }
198204
}
199205

206+
/**
207+
* A taint step from an update of the `bytes[]` parameter in an override of the `InputStream.read` method
208+
* to a class instance expression of the type extending `InputStream`.
209+
*
210+
* This models how a subtype of `InputStream` could be tainted by the definition of its methods, which will
211+
* normally only happen in anonymous classes.
212+
*/
213+
private class InputStreamWrapperAnonymousStep extends AdditionalTaintStep {
214+
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
215+
exists(Method m, AnonymousClass wrapper |
216+
m.hasName("read") and
217+
m.getDeclaringType() = wrapper and
218+
wrapper.getASourceSupertype+() instanceof TypeInputStream
219+
|
220+
n1.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() = m.getParameter(0).getAnAccess() and
221+
n2.asExpr() = wrapper.getClassInstanceExpr()
222+
)
223+
}
224+
}
225+
226+
/**
227+
* A taint step from an `InputStream` argument of the constructor of an `InputStream` subtype
228+
* to the call of the constructor, only if the argument is assigned to a class field.
229+
*
230+
* This models how it's assumed that an `InputStream` wrapper is tainted by the wrapped stream,
231+
* and is a workaround to low `fieldFlowBranchLimit`s in dataflow configurations.
232+
*/
233+
private class InputStreamWrapperConstructorStep extends AdditionalTaintStep {
234+
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
235+
exists(ClassInstanceExpr cc, Argument a, AssignExpr ae |
236+
cc.getConstructedType().getASourceSupertype+() instanceof TypeInputStream and
237+
cc.getAnArgument() = a and
238+
cc.getCallee().getParameter(a.getParameterPos()).getAnAccess() = ae.getRhs() and
239+
ae.getDest().(FieldWrite).getField().getType().(RefType).getASourceSupertype*() instanceof
240+
TypeInputStream
241+
|
242+
n1.asExpr() = a and
243+
n2.asExpr() = cc
244+
)
245+
}
246+
}
247+
200248
// --- Standard methods ---
201249
/**
202250
* DEPRECATED: Any constructor of class `java.lang.ProcessBuilder`.

java/ql/lib/semmle/code/java/dataflow/RangeAnalysis.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ private predicate baseBound(Expr e, int b, boolean upper) {
757757
or
758758
exists(Method read |
759759
e.(MethodAccess).getMethod().overrides*(read) and
760-
read.getDeclaringType().hasQualifiedName("java.io", "InputStream") and
760+
read.getDeclaringType() instanceof TypeInputStream and
761761
read.hasName("read") and
762762
read.getNumberOfParameters() = 0
763763
|
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import java.io.InputStream;
2+
import java.io.IOException;
3+
4+
public class A {
5+
6+
private static InputStream source() {
7+
return null;
8+
}
9+
10+
private static void sink(Object s) {}
11+
12+
static class MyStream extends InputStream {
13+
private InputStream wrapped;
14+
15+
MyStream(InputStream wrapped) {
16+
this.wrapped = wrapped;
17+
}
18+
19+
@Override
20+
public int read() throws IOException {
21+
return 0;
22+
}
23+
24+
@Override
25+
public int read(byte[] b) throws IOException {
26+
return wrapped.read(b);
27+
}
28+
}
29+
30+
public static void testSeveralWrappers() {
31+
InputStream src = source();
32+
InputStream wrapper1 = new MyStream(src);
33+
sink(wrapper1); // $ hasTaintFlow
34+
InputStream wrapper2 = new MyStream(wrapper1);
35+
sink(wrapper2); // $ hasTaintFlow
36+
InputStream wrapper3 = new MyStream(wrapper2);
37+
sink(wrapper3); // $ hasTaintFlow
38+
39+
InputStream wrapper4 = new InputStream() {
40+
@Override
41+
public int read() throws IOException {
42+
return 0;
43+
}
44+
45+
@Override
46+
public int read(byte[] b) throws IOException {
47+
return wrapper3.read(b);
48+
49+
}
50+
};
51+
sink(wrapper4); // $ hasTaintFlow
52+
}
53+
54+
public static void testAnonymous() throws Exception {
55+
InputStream wrapper = new InputStream() {
56+
@Override
57+
public int read() throws IOException {
58+
return 0;
59+
}
60+
61+
@Override
62+
public int read(byte[] b) throws IOException {
63+
InputStream in = source();
64+
return in.read(b);
65+
}
66+
};
67+
sink(wrapper); // $ hasTaintFlow
68+
}
69+
70+
public static void testAnonymousVarCapture() throws Exception {
71+
InputStream in = source();
72+
InputStream wrapper = new InputStream() {
73+
@Override
74+
public int read() throws IOException {
75+
return 0;
76+
}
77+
78+
@Override
79+
public int read(byte[] b) throws IOException {
80+
return in.read(b);
81+
82+
}
83+
};
84+
sink(wrapper); // $ hasTaintFlow
85+
}
86+
87+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
failures
2+
testFailures
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import TestUtilities.InlineFlowTest
2+
import DefaultFlowTest

0 commit comments

Comments
 (0)