Skip to content

Commit c629f6b

Browse files
authored
Merge pull request github#3869 from aibaars/util-collections
Java: model java.util.Collections
2 parents 687bb4d + 5fff41f commit c629f6b

File tree

4 files changed

+113
-4
lines changed

4 files changed

+113
-4
lines changed

java/ql/src/semmle/code/java/dataflow/internal/ContainerFlow.qll

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,44 @@ private predicate taintPreservingArgumentToQualifier(Method method, int arg) {
159159
method.(CollectionMethod).hasName("offer") and arg = 0
160160
}
161161

162+
/**
163+
* Holds if `method` is a library method that returns tainted data if its
164+
* `arg`th argument is tainted.
165+
*/
166+
private predicate taintPreservingArgumentToMethod(Method method, int arg) {
167+
method.getDeclaringType().hasQualifiedName("java.util", "Collections") and
168+
(
169+
method
170+
.hasName(["checkedCollection", "checkedList", "checkedMap", "checkedNavigableMap",
171+
"checkedNavigableSet", "checkedSet", "checkedSortedMap", "checkedSortedSet",
172+
"enumeration", "list", "max", "min", "singleton", "singletonList",
173+
"synchronizedCollection", "synchronizedList", "synchronizedMap",
174+
"synchronizedNavigableMap", "synchronizedNavigableSet", "synchronizedSet",
175+
"synchronizedSortedMap", "synchronizedSortedSet", "unmodifiableCollection",
176+
"unmodifiableList", "unmodifiableMap", "unmodifiableNavigableMap",
177+
"unmodifiableNavigableSet", "unmodifiableSet", "unmodifiableSortedMap",
178+
"unmodifiableSortedSet"]) and
179+
arg = 0
180+
or
181+
method.hasName(["nCopies", "singletonMap"]) and arg = 1
182+
)
183+
}
184+
185+
/**
186+
* Holds if `method` is a library method that writes tainted data to the
187+
* `output`th argument if the `input`th argument is tainted.
188+
*/
189+
private predicate taintPreservingArgToArg(Method method, int input, int output) {
190+
method.getDeclaringType().hasQualifiedName("java.util", "Collections") and
191+
(
192+
method.hasName(["copy", "fill"]) and
193+
input = 1 and
194+
output = 0
195+
or
196+
method.hasName("replaceAll") and input = 2 and output = 0
197+
)
198+
}
199+
162200
private predicate argToQualifierStep(Expr tracked, Expr sink) {
163201
exists(Method m, int i, MethodAccess ma |
164202
taintPreservingArgumentToQualifier(m, i) and
@@ -168,13 +206,44 @@ private predicate argToQualifierStep(Expr tracked, Expr sink) {
168206
)
169207
}
170208

209+
/** Access to a method that passes taint from an argument. */
210+
private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
211+
exists(Method m, int i |
212+
m = sink.getMethod() and
213+
taintPreservingArgumentToMethod(m, i) and
214+
tracked = sink.getArgument(i)
215+
)
216+
}
217+
218+
/**
219+
* Holds if `tracked` and `sink` are arguments to a method that transfers taint
220+
* between arguments.
221+
*/
222+
private predicate argToArgStep(Expr tracked, Expr sink) {
223+
exists(MethodAccess ma, Method method, int input, int output |
224+
ma.getMethod() = method and
225+
ma.getArgument(input) = tracked and
226+
ma.getArgument(output) = sink and
227+
(
228+
taintPreservingArgToArg(method, input, output)
229+
or
230+
method.getDeclaringType().hasQualifiedName("java.util", "Collections") and
231+
method.hasName("addAll") and
232+
input >= 1 and
233+
output = 0
234+
)
235+
)
236+
}
237+
171238
/**
172239
* Holds if the step from `n1` to `n2` is either extracting a value from a
173240
* container, inserting a value into a container, or transforming one container
174241
* to another. This is restricted to cases where `n2` is the returned value of
175242
* a call.
176243
*/
177-
predicate containerReturnValueStep(Expr n1, Expr n2) { qualifierToMethodStep(n1, n2) }
244+
predicate containerReturnValueStep(Expr n1, Expr n2) {
245+
qualifierToMethodStep(n1, n2) or argToMethodStep(n1, n2)
246+
}
178247

179248
/**
180249
* Holds if the step from `n1` to `n2` is either extracting a value from a
@@ -183,7 +252,8 @@ predicate containerReturnValueStep(Expr n1, Expr n2) { qualifierToMethodStep(n1,
183252
*/
184253
predicate containerUpdateStep(Expr n1, Expr n2) {
185254
qualifierToArgumentStep(n1, n2) or
186-
argToQualifierStep(n1, n2)
255+
argToQualifierStep(n1, n2) or
256+
argToArgStep(n1, n2)
187257
}
188258

189259
/**

java/ql/src/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,9 +383,9 @@ private predicate unsafeEscape(MethodAccess ma) {
383383
/** Access to a method that passes taint from an argument. */
384384
private predicate argToMethodStep(Expr tracked, MethodAccess sink) {
385385
exists(Method m, int i |
386-
m = sink.(MethodAccess).getMethod() and
386+
m = sink.getMethod() and
387387
taintPreservingArgumentToMethod(m, i) and
388-
tracked = sink.(MethodAccess).getArgument(i)
388+
tracked = sink.getArgument(i)
389389
)
390390
or
391391
exists(MethodAccess ma |
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import java.util.Collections;
2+
import java.util.Enumeration;
3+
import java.util.List;
4+
5+
class CollectionsTest {
6+
public static void taintSteps(List<String> list, List<String> other, Enumeration enumeration) {
7+
Collections.addAll(list);
8+
Collections.addAll(list, "one");
9+
Collections.addAll(list, "two", "three");
10+
Collections.addAll(list, new String[]{ "four" });
11+
12+
Collections.checkedList(list, String.class);
13+
Collections.min(list);
14+
Collections.enumeration(list);
15+
Collections.list(enumeration);
16+
Collections.singletonMap("key", "value");
17+
Collections.copy(list, other);
18+
Collections.nCopies(10, "item");
19+
Collections.replaceAll(list, "search", "replace");
20+
}
21+
}
22+

java/ql/test/library-tests/dataflow/local-additional-taint/localAdditionalTaintStep.expected

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
| CollectionsTest.java:8:28:8:32 | "one" | CollectionsTest.java:8:3:8:33 | new ..[] { .. } |
2+
| CollectionsTest.java:8:28:8:32 | "one" | CollectionsTest.java:8:22:8:25 | list [post update] |
3+
| CollectionsTest.java:9:28:9:32 | "two" | CollectionsTest.java:9:3:9:42 | new ..[] { .. } |
4+
| CollectionsTest.java:9:28:9:32 | "two" | CollectionsTest.java:9:22:9:25 | list [post update] |
5+
| CollectionsTest.java:9:35:9:41 | "three" | CollectionsTest.java:9:3:9:42 | new ..[] { .. } |
6+
| CollectionsTest.java:9:35:9:41 | "three" | CollectionsTest.java:9:22:9:25 | list [post update] |
7+
| CollectionsTest.java:10:28:10:49 | new String[] | CollectionsTest.java:10:22:10:25 | list [post update] |
8+
| CollectionsTest.java:10:28:10:49 | {...} | CollectionsTest.java:10:28:10:49 | new String[] |
9+
| CollectionsTest.java:10:42:10:47 | "four" | CollectionsTest.java:10:28:10:49 | {...} |
10+
| CollectionsTest.java:12:27:12:30 | list | CollectionsTest.java:12:3:12:45 | checkedList(...) |
11+
| CollectionsTest.java:13:19:13:22 | list | CollectionsTest.java:13:3:13:23 | min(...) |
12+
| CollectionsTest.java:14:27:14:30 | list | CollectionsTest.java:14:3:14:31 | enumeration(...) |
13+
| CollectionsTest.java:15:20:15:30 | enumeration | CollectionsTest.java:15:3:15:31 | list(...) |
14+
| CollectionsTest.java:16:35:16:41 | "value" | CollectionsTest.java:16:3:16:42 | singletonMap(...) |
15+
| CollectionsTest.java:17:26:17:30 | other | CollectionsTest.java:17:20:17:23 | list [post update] |
16+
| CollectionsTest.java:18:27:18:32 | "item" | CollectionsTest.java:18:3:18:33 | nCopies(...) |
17+
| CollectionsTest.java:19:42:19:50 | "replace" | CollectionsTest.java:19:26:19:29 | list [post update] |
118
| Test.java:24:32:24:38 | string2 | Test.java:24:17:24:39 | decode(...) |
219
| Test.java:25:46:25:51 | bytes2 | Test.java:25:31:25:52 | encode(...) |
320
| Test.java:27:34:27:40 | string2 | Test.java:27:13:27:41 | decode(...) |

0 commit comments

Comments
 (0)