Skip to content

Commit 7522a2d

Browse files
authored
Merge pull request github#7832 from aschackmull/java/modelgen
Java: Simplify model generator query using flow state.
2 parents 1f01d80 + 7bde1cb commit 7522a2d

File tree

6 files changed

+91
-134
lines changed

6 files changed

+91
-134
lines changed

java/ql/src/utils/model-generator/CaptureSourceModels.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ string captureSource(TargetAPI api) {
4343
exists(DataFlow::Node source, DataFlow::Node sink, FromSourceConfiguration config, string kind |
4444
config.hasFlow(source, sink) and
4545
sourceNode(source, kind) and
46-
api = source.getEnclosingCallable() and
47-
result = asSourceModel(api, returnNodeAsOutput(api, sink), kind)
46+
api = sink.getEnclosingCallable() and
47+
result = asSourceModel(api, returnNodeAsOutput(sink), kind)
4848
)
4949
}
5050

java/ql/src/utils/model-generator/CaptureSummaryModels.ql

Lines changed: 60 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@ import ModelGeneratorUtils
1414

1515
string captureFlow(TargetAPI api) {
1616
result = captureQualifierFlow(api) or
17-
result = captureParameterFlowToReturnValue(api) or
18-
result = captureFieldFlowIn(api) or
19-
result = captureParameterToParameterFlow(api) or
20-
result = captureFieldFlow(api)
17+
result = captureThroughFlow(api)
2118
}
2219

2320
/**
@@ -40,21 +37,51 @@ string captureQualifierFlow(TargetAPI api) {
4037
result = asValueModel(api, "Argument[-1]", "ReturnValue")
4138
}
4239

43-
class FieldToReturnConfig extends TaintTracking::Configuration {
44-
FieldToReturnConfig() { this = "FieldToReturnConfig" }
40+
class TaintRead extends DataFlow::FlowState {
41+
TaintRead() { this = "TaintRead" }
42+
}
43+
44+
class TaintStore extends DataFlow::FlowState {
45+
TaintStore() { this = "TaintStore" }
46+
}
4547

46-
override predicate isSource(DataFlow::Node source) {
47-
source instanceof DataFlow::InstanceParameterNode
48+
class ThroughFlowConfig extends TaintTracking::Configuration {
49+
ThroughFlowConfig() { this = "ThroughFlowConfig" }
50+
51+
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
52+
source instanceof DataFlow::ParameterNode and
53+
source.getEnclosingCallable() instanceof TargetAPI and
54+
state instanceof TaintRead
4855
}
4956

50-
override predicate isSink(DataFlow::Node sink) {
57+
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
5158
sink instanceof ReturnNodeExt and
5259
not sink.(ReturnNode).asExpr().(ThisAccess).isOwnInstanceAccess() and
53-
not exists(captureQualifierFlow(sink.asExpr().getEnclosingCallable()))
60+
not exists(captureQualifierFlow(sink.asExpr().getEnclosingCallable())) and
61+
(state instanceof TaintRead or state instanceof TaintStore)
62+
}
63+
64+
override predicate isAdditionalFlowStep(
65+
DataFlow::Node node1, DataFlow::FlowState state1, DataFlow::Node node2,
66+
DataFlow::FlowState state2
67+
) {
68+
exists(TypedContent tc |
69+
store(node1, tc, node2, _) and
70+
isRelevantContent(tc.getContent()) and
71+
(state1 instanceof TaintRead or state1 instanceof TaintStore) and
72+
state2 instanceof TaintStore
73+
)
74+
or
75+
exists(DataFlow::Content c |
76+
readStep(node1, c, node2) and
77+
isRelevantContent(c) and
78+
state1 instanceof TaintRead and
79+
state2 instanceof TaintRead
80+
)
5481
}
5582

56-
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
57-
isRelevantTaintStep(node1, node2)
83+
override predicate isSanitizer(DataFlow::Node n) {
84+
exists(Type t | t = n.getType() and not isRelevantType(t))
5885
}
5986

6087
override DataFlow::FlowFeature getAFeature() {
@@ -63,8 +90,12 @@ class FieldToReturnConfig extends TaintTracking::Configuration {
6390
}
6491

6592
/**
66-
* Capture APIs that return tainted instance data.
67-
* Example of an API that returns tainted instance data:
93+
* Capture APIs that transfer taint from an input parameter to an output return
94+
* value or parameter.
95+
* Allows a sequence of read steps followed by a sequence of store steps.
96+
*
97+
* Examples:
98+
*
6899
* ```
69100
* public class Foo {
70101
* private String tainted;
@@ -83,48 +114,7 @@ class FieldToReturnConfig extends TaintTracking::Configuration {
83114
* p;Foo;true;returnsTainted;;Argument[-1];ReturnValue;taint
84115
* p;Foo;true;putsTaintIntoParameter;(List);Argument[-1];Argument[0];taint
85116
* ```
86-
*/
87-
string captureFieldFlow(TargetAPI api) {
88-
exists(FieldToReturnConfig config, ReturnNodeExt returnNodeExt |
89-
config.hasFlow(_, returnNodeExt) and
90-
returnNodeExt.getEnclosingCallable() = api and
91-
not api.getDeclaringType() instanceof EnumType and
92-
isRelevantType(returnNodeExt.getType())
93-
|
94-
result = asTaintModel(api, "Argument[-1]", returnNodeAsOutput(api, returnNodeExt))
95-
)
96-
}
97-
98-
class ParameterToFieldConfig extends TaintTracking::Configuration {
99-
ParameterToFieldConfig() { this = "ParameterToFieldConfig" }
100-
101-
override predicate isSource(DataFlow::Node source) {
102-
source instanceof DataFlow::ParameterNode and
103-
isRelevantType(source.getType())
104-
}
105-
106-
override predicate isSink(DataFlow::Node sink) {
107-
thisAccess(sink.(DataFlow::PostUpdateNode).getPreUpdateNode())
108-
}
109-
110-
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
111-
store(node1, _, node2, _)
112-
}
113-
114-
override DataFlow::FlowFeature getAFeature() {
115-
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
116-
}
117-
}
118-
119-
private predicate thisAccess(DataFlow::Node n) {
120-
n.asExpr().(InstanceAccess).isOwnInstanceAccess()
121-
or
122-
n.(DataFlow::ImplicitInstanceAccess).getInstanceAccess() instanceof OwnInstanceAccess
123-
}
124-
125-
/**
126-
* Captures APIs that accept input and store them in a field.
127-
* Example:
117+
*
128118
* ```
129119
* public class Foo {
130120
* private String tainted;
@@ -134,96 +124,38 @@ private predicate thisAccess(DataFlow::Node n) {
134124
* ```
135125
* Captured Model:
136126
* `p;Foo;true;doSomething;(String);Argument[0];Argument[-1];taint`
137-
*/
138-
string captureFieldFlowIn(TargetAPI api) {
139-
exists(DataFlow::Node source, ParameterToFieldConfig config |
140-
config.hasFlow(source, _) and
141-
source.asParameter().getCallable() = api
142-
|
143-
result =
144-
asTaintModel(api, "Argument[" + source.asParameter().getPosition() + "]", "Argument[-1]")
145-
)
146-
}
147-
148-
class ParameterToReturnValueTaintConfig extends TaintTracking::Configuration {
149-
ParameterToReturnValueTaintConfig() { this = "ParameterToReturnValueTaintConfig" }
150-
151-
override predicate isSource(DataFlow::Node source) {
152-
exists(TargetAPI api |
153-
api = source.asParameter().getCallable() and
154-
isRelevantType(api.getReturnType()) and
155-
isRelevantType(source.asParameter().getType())
156-
)
157-
}
158-
159-
override predicate isSink(DataFlow::Node sink) { sink instanceof ReturnNode }
160-
161-
// consider store steps to track taint across objects to model factory methods returning tainted objects
162-
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
163-
store(node1, _, node2, _)
164-
}
165-
166-
override DataFlow::FlowFeature getAFeature() {
167-
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
168-
}
169-
}
170-
171-
predicate paramFlowToReturnValueExists(Parameter p) {
172-
exists(ParameterToReturnValueTaintConfig config, ReturnStmt rtn |
173-
config.hasFlow(DataFlow::parameterNode(p), DataFlow::exprNode(rtn.getResult()))
174-
)
175-
}
176-
177-
/**
178-
* Capture APIs that return (parts of) data passed in as a parameter.
179-
* Example:
127+
*
180128
* ```
181129
* public class Foo {
182-
*
183130
* public String returnData(String tainted) {
184131
* return tainted.substring(0,10)
185132
* }
186133
* }
187134
* ```
188135
* Captured Model:
189-
* ```
190-
* p;Foo;true;returnData;;Argument[0];ReturnValue;taint
191-
* ```
192-
*/
193-
string captureParameterFlowToReturnValue(TargetAPI api) {
194-
exists(Parameter p |
195-
p = api.getAParameter() and
196-
paramFlowToReturnValueExists(p)
197-
|
198-
result = asTaintModel(api, parameterAccess(p), "ReturnValue")
199-
)
200-
}
201-
202-
/**
203-
* Capture APIs that pass tainted data from a parameter to a parameter.
204-
* Example:
136+
* `p;Foo;true;returnData;;Argument[0];ReturnValue;taint`
137+
*
205138
* ```
206139
* public class Foo {
207-
*
208140
* public void addToList(String tainted, List<String> foo) {
209141
* foo.add(tainted);
210142
* }
211143
* }
212144
* ```
213145
* Captured Model:
214-
* ```
215-
* p;Foo;true;addToList;;Argument[0];Argument[1];taint
216-
* ```
146+
* `p;Foo;true;addToList;;Argument[0];Argument[1];taint`
217147
*/
218-
string captureParameterToParameterFlow(TargetAPI api) {
219-
exists(DataFlow::ParameterNode source, DataFlow::PostUpdateNode sink |
220-
source.getEnclosingCallable() = api and
221-
sink.getPreUpdateNode().asExpr() = api.getAParameter().getAnAccess() and
222-
TaintTracking::localTaint(source, sink)
148+
string captureThroughFlow(TargetAPI api) {
149+
exists(
150+
ThroughFlowConfig config, DataFlow::ParameterNode p, ReturnNodeExt returnNodeExt, string input,
151+
string output
223152
|
224-
result =
225-
asTaintModel(api, parameterAccess(source.asParameter()),
226-
parameterAccess(sink.getPreUpdateNode().asExpr().(VarAccess).getVariable()))
153+
config.hasFlow(p, returnNodeExt) and
154+
returnNodeExt.getEnclosingCallable() = api and
155+
input = parameterNodeAsInput(p) and
156+
output = returnNodeAsOutput(returnNodeExt) and
157+
input != output and
158+
result = asTaintModel(api, input, output)
227159
)
228160
}
229161

java/ql/src/utils/model-generator/ModelGeneratorUtils.qll

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,10 @@ predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
140140
readStep(node1, f, node2) and
141141
if f instanceof DataFlow::FieldContent
142142
then isRelevantType(f.(DataFlow::FieldContent).getField().getType())
143-
else any()
143+
else
144+
if f instanceof DataFlow::SyntheticFieldContent
145+
then isRelevantType(f.(DataFlow::SyntheticFieldContent).getField().getType())
146+
else any()
144147
)
145148
or
146149
exists(DataFlow::Content f | storeStep(node1, f, node2) |
@@ -151,11 +154,29 @@ predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
151154
)
152155
}
153156

154-
string returnNodeAsOutput(TargetAPI api, ReturnNodeExt node) {
157+
predicate isRelevantContent(DataFlow::Content f) {
158+
isRelevantType(f.(DataFlow::FieldContent).getField().getType()) or
159+
f instanceof DataFlow::ArrayContent or
160+
f instanceof DataFlow::CollectionContent or
161+
f instanceof DataFlow::MapKeyContent or
162+
f instanceof DataFlow::MapValueContent
163+
}
164+
165+
string parameterNodeAsInput(DataFlow::ParameterNode p) {
166+
result = parameterAccess(p.asParameter())
167+
or
168+
result = "Argument[-1]" and p instanceof DataFlow::InstanceParameterNode
169+
}
170+
171+
string returnNodeAsOutput(ReturnNodeExt node) {
155172
if node.getKind() instanceof ValueReturnKind
156173
then result = "ReturnValue"
157174
else
158-
result = parameterAccess(api.getParameter(node.getKind().(ParamUpdateReturnKind).getPosition()))
175+
exists(int pos | pos = node.getKind().(ParamUpdateReturnKind).getPosition() |
176+
result = parameterAccess(node.getEnclosingCallable().getParameter(pos))
177+
or
178+
result = "Argument[-1]" and pos = -1
179+
)
159180
}
160181

161182
string parameterAccess(Parameter p) {

java/ql/test/utils/model-generator/CaptureSourceModels.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
| p;Sources;true;socketStream;();;ReturnValue;remote |
33
| p;Sources;true;sourceToParameter;(InputStream[],List);;Argument[0].ArrayElement;remote |
44
| p;Sources;true;sourceToParameter;(InputStream[],List);;Argument[1].Element;remote |
5+
| p;Sources;true;wrappedSocketStream;();;ReturnValue;remote |

java/ql/test/utils/model-generator/CaptureSummaryModels.expected

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
| p;Joiner;false;merge;(Joiner);;Argument[-1];ReturnValue;value |
2222
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[-1];ReturnValue;value |
2323
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[0];Argument[-1];taint |
24-
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[0];ReturnValue;taint |
2524
| p;Joiner;false;toString;();;Argument[-1];ReturnValue;taint |
2625
| p;MultipleImpls$Strat2;true;getValue;();;Argument[-1];ReturnValue;taint |
2726
| p;MultipleImpls$Strategy;true;doSomething;(String);;Argument[0];Argument[-1];taint |

java/ql/test/utils/model-generator/p/Sources.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ public InputStream socketStream() throws IOException {
1919
return socket.accept().getInputStream();
2020
}
2121

22+
public InputStream wrappedSocketStream() throws IOException {
23+
return socketStream();
24+
}
25+
2226
public void sourceToParameter(InputStream[] streams, List<InputStream> otherStreams) throws IOException {
2327
ServerSocket socket = new ServerSocket(123);
2428
streams[0] = socket.accept().getInputStream();

0 commit comments

Comments
 (0)