Skip to content

Commit e6d832c

Browse files
authored
Merge pull request github#14297 from aschackmull/java/additional-steps-and-nodes
Java: Add support for additional nodes, read steps, and store steps for QL models and model ThreadLocal.initialValue
2 parents 60b7840 + b11194e commit e6d832c

File tree

5 files changed

+120
-3
lines changed

5 files changed

+120
-3
lines changed

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ private module Frameworks {
2323
private import semmle.code.java.frameworks.InputStream
2424
private import semmle.code.java.frameworks.Properties
2525
private import semmle.code.java.frameworks.Protobuf
26+
private import semmle.code.java.frameworks.ThreadLocal
2627
private import semmle.code.java.frameworks.ratpack.RatpackExec
2728
private import semmle.code.java.frameworks.stapler.Stapler
2829
}
@@ -57,6 +58,22 @@ abstract class FluentMethod extends ValuePreservingMethod {
5758
override predicate returnsValue(int arg) { arg = -1 }
5859
}
5960

61+
/**
62+
* A unit class for adding additional data flow nodes.
63+
*
64+
* Extend this class to add additional data flow nodes for use in globally
65+
* applicable additional steps.
66+
*/
67+
class AdditionalDataFlowNode extends Unit {
68+
/**
69+
* Holds if an additional node is needed in relation to `e`. The pair `(e,id)`
70+
* must uniquely identify the node.
71+
* The added node can be selected for use in a predicate by the corresponding
72+
* `DataFlow::AdditionalNode.nodeAt(Expr e, string id)` predicate.
73+
*/
74+
abstract predicate nodeAt(Expr e, string id);
75+
}
76+
6077
/**
6178
* A unit class for adding additional taint steps.
6279
*
@@ -85,6 +102,36 @@ class AdditionalValueStep extends Unit {
85102
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
86103
}
87104

105+
/**
106+
* A unit class for adding additional store steps.
107+
*
108+
* Extend this class to add additional store steps that should apply to all
109+
* data flow configurations. A store step must be local, so non-local steps are
110+
* ignored.
111+
*/
112+
class AdditionalStoreStep extends Unit {
113+
/**
114+
* Holds if the step from `node1` to `node2` is a store step of `c` and should
115+
* apply to all data flow configurations.
116+
*/
117+
abstract predicate step(DataFlow::Node node1, DataFlow::Content c, DataFlow::Node node2);
118+
}
119+
120+
/**
121+
* A unit class for adding additional read steps.
122+
*
123+
* Extend this class to add additional read steps that should apply to all
124+
* data flow configurations. A read step must be local, so non-local steps are
125+
* ignored.
126+
*/
127+
class AdditionalReadStep extends Unit {
128+
/**
129+
* Holds if the step from `node1` to `node2` is a read step of `c` and should
130+
* apply to all data flow configurations.
131+
*/
132+
abstract predicate step(DataFlow::Node node1, DataFlow::Content c, DataFlow::Node node2);
133+
}
134+
88135
/**
89136
* A method or constructor that preserves taint.
90137
*

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ private import semmle.code.java.dataflow.InstanceAccess
33
private import semmle.code.java.dataflow.ExternalFlow
44
private import semmle.code.java.dataflow.FlowSummary
55
private import semmle.code.java.dataflow.TypeFlow
6+
private import semmle.code.java.dataflow.FlowSteps
67
private import DataFlowPrivate
78
private import DataFlowUtil
89
private import FlowSummaryImpl as FlowSummaryImpl
@@ -56,7 +57,8 @@ private module Cached {
5657
} or
5758
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
5859
TFieldValueNode(Field f) or
59-
TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn)
60+
TCaptureNode(CaptureFlow::SynthesizedCaptureNode cn) or
61+
TAdditionalNode(Expr e, string id) { any(AdditionalDataFlowNode adfn).nodeAt(e, id) }
6062

6163
cached
6264
newtype TContent =
@@ -133,6 +135,8 @@ module Public {
133135
result = this.(CaptureNode).getTypeImpl()
134136
or
135137
result = this.(FieldValueNode).getField().getType()
138+
or
139+
result instanceof TypeObject and this instanceof AdditionalNode
136140
}
137141

138142
/** Gets the callable in which this node occurs. */
@@ -335,6 +339,21 @@ module Public {
335339
/** Holds if this is an access to an object's own instance. */
336340
predicate isOwnInstanceAccess() { this.getInstanceAccess().isOwnInstanceAccess() }
337341
}
342+
343+
/** A node introduced by an extension of `AdditionalDataFlowNode`. */
344+
class AdditionalNode extends Node, TAdditionalNode {
345+
Expr e_;
346+
string id_;
347+
348+
AdditionalNode() { this = TAdditionalNode(e_, id_) }
349+
350+
override string toString() { result = e_.toString() + " (" + id_ + ")" }
351+
352+
override Location getLocation() { result = e_.getLocation() }
353+
354+
/** Holds if this node was introduced by `AdditionalDataFlowNode.nodeAt(e, id)`. */
355+
predicate nodeAt(Expr e, string id) { e = e_ and id = id_ }
356+
}
338357
}
339358

340359
private import Public
@@ -378,7 +397,8 @@ module Private {
378397
result = nodeGetEnclosingCallable(n.(ImplicitPostUpdateNode).getPreUpdateNode()) or
379398
result.asSummarizedCallable() = n.(FlowSummaryNode).getSummarizedCallable() or
380399
result.asCallable() = n.(CaptureNode).getSynthesizedCaptureNode().getEnclosingCallable() or
381-
result.asFieldScope() = n.(FieldValueNode).getField()
400+
result.asFieldScope() = n.(FieldValueNode).getField() or
401+
result.asCallable() = any(Expr e | n.(AdditionalNode).nodeAt(e, _)).getEnclosingCallable()
382402
}
383403

384404
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */

java/ql/lib/semmle/code/java/dataflow/internal/DataFlowPrivate.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ predicate storeStep(Node node1, ContentSet f, Node node2) {
228228
node2.(FlowSummaryNode).getSummaryNode())
229229
or
230230
captureStoreStep(node1, f, node2)
231+
or
232+
any(AdditionalStoreStep a).step(node1, f, node2) and
233+
pragma[only_bind_out](node1.getEnclosingCallable()) =
234+
pragma[only_bind_out](node2.getEnclosingCallable())
231235
}
232236

233237
/**
@@ -262,6 +266,10 @@ predicate readStep(Node node1, ContentSet f, Node node2) {
262266
node2.(FlowSummaryNode).getSummaryNode())
263267
or
264268
captureReadStep(node1, f, node2)
269+
or
270+
any(AdditionalReadStep a).step(node1, f, node2) and
271+
pragma[only_bind_out](node1.getEnclosingCallable()) =
272+
pragma[only_bind_out](node2.getEnclosingCallable())
265273
}
266274

267275
/**
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/** Definitions related to `java.lang.ThreadLocal`. */
2+
3+
import java
4+
private import semmle.code.java.dataflow.DataFlow
5+
private import semmle.code.java.dataflow.FlowSteps
6+
7+
/**
8+
* Holds if `cie` construct a `ThreadLocal` object with an overridden
9+
* `initialValue` method with a return value of `init`, such that `init` is the
10+
* initial value of the `ThreadLocal` object.
11+
*/
12+
private predicate threadLocalInitialValue(ClassInstanceExpr cie, Method initialValue, Expr init) {
13+
exists(RefType t, ReturnStmt ret |
14+
cie.getConstructedType().getSourceDeclaration() = t and
15+
t.getASourceSupertype+().hasQualifiedName("java.lang", "ThreadLocal") and
16+
ret.getResult() = init and
17+
ret.getEnclosingCallable() = initialValue and
18+
initialValue.hasName("initialValue") and
19+
initialValue.getDeclaringType() = t
20+
)
21+
}
22+
23+
private class ThreadLocalInitialValueStore extends AdditionalStoreStep {
24+
override predicate step(DataFlow::Node node1, DataFlow::Content c, DataFlow::Node node2) {
25+
exists(Method initialValue, Expr init |
26+
threadLocalInitialValue(_, initialValue, init) and
27+
node1.asExpr() = init and
28+
node2.(DataFlow::InstanceParameterNode).getCallable() = initialValue and
29+
c.(DataFlow::SyntheticFieldContent).getField() = "java.lang.ThreadLocal.value"
30+
)
31+
}
32+
}
33+
34+
private class ThreadLocalInitialValueStep extends AdditionalValueStep {
35+
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
36+
exists(ClassInstanceExpr cie, Method initialValue |
37+
threadLocalInitialValue(cie, initialValue, _) and
38+
node1.(DataFlow::InstanceParameterNode).getCallable() = initialValue and
39+
node2.asExpr() = cie
40+
)
41+
}
42+
}

java/ql/test/query-tests/security/CWE-611/DocumentBuilderTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ protected DocumentBuilder initialValue() {
129129

130130
public void disableExternalEntities2(Socket sock) throws Exception {
131131
DocumentBuilder builder = XML_DOCUMENT_BUILDER.get();
132-
builder.parse(sock.getInputStream()); // $ SPURIOUS: hasTaintFlow
132+
builder.parse(sock.getInputStream()); // safe
133133
}
134134

135135
}

0 commit comments

Comments
 (0)