Skip to content

Commit d1427fc

Browse files
Java: Add modelling for Guava's collection classes
1 parent 4601eb9 commit d1427fc

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
/** Defenitions of flow steps through the collection types in the Guava framework */
2+
3+
import java
4+
private import semmle.code.java.dataflow.FlowSteps
5+
private import semmle.code.java.Collections
6+
7+
private string guavaCollectPackage() { result = "com.google.common.collect" }
8+
9+
/** A reference types that extends a parameterization of one of the various immutable container types. */
10+
private class ImmutableContainerType extends RefType {
11+
string kind;
12+
13+
ImmutableContainerType() {
14+
this.getASourceSupertype().hasQualifiedName(guavaCollectPackage(), kind) and
15+
kind = ["ImmutableCollection", "ImmutableMap", "ImmutableMultimap", "ImmutableTable"]
16+
}
17+
18+
/**
19+
* Gets the name of the most general superclass of this type
20+
* from among `ImmutableCollection`, `ImmutableMap`, `ImmutableMultimap`, and `ImmutableTable`.
21+
*/
22+
string getKind() { result = kind }
23+
}
24+
25+
/** A nested `Builder` class of one of the various immutable container classes */
26+
private class CollectionBuilder extends NestedType {
27+
CollectionBuilder() {
28+
this.hasName("Builder") and
29+
this.getEnclosingType() instanceof ImmutableContainerType
30+
}
31+
}
32+
33+
private class BuilderBuildMethod extends TaintPreservingCallable {
34+
BuilderBuildMethod() {
35+
this.getDeclaringType().getASourceSupertype*() instanceof CollectionBuilder and
36+
// abstract ImmutableCollection<E> build()
37+
// similar for other builder types
38+
this.hasName("build")
39+
}
40+
41+
override predicate returnsTaintFrom(int arg) { arg = -1 }
42+
}
43+
44+
/** A method on a `Builder` class that adds elements to the container being built */
45+
private class BuilderAddMethod extends TaintPreservingCallable {
46+
int argument;
47+
48+
BuilderAddMethod() {
49+
this.getDeclaringType().getASourceSupertype*() instanceof CollectionBuilder and
50+
// @CanIgnoreReturnValue abstract ImmutableCollection.Builder<E> add(E element)
51+
// @CanIgnoreReturnValue ImmutableCollection.Builder<E> add(E... elements)
52+
// @CanIgnoreReturnValue ImmutableCollection.Builder<E> addAll(Iterable<? extends E> elements)
53+
// @CanIgnoreReturnValue ImmutableCollection.Builder<E> addAll(Iterator<? extends E> elements)
54+
// @CanIgnoreReturnValue ImmutableMultiset.Builder<E> addCopies(E element, int occurrences)
55+
// @CanIgnoreReturnValue ImmutableMultiset.Builder<E> setCount(E element, int count)
56+
this.hasName(["add", "addAll", "addCopies", "setCount"]) and
57+
argument = 0
58+
or
59+
// @CanIgnoreReturnValue ImmutableMap.Builder<K,V> put(K key, V value)
60+
// @CanIgnoreReturnValue ImmutableMap.Builder<K,V> put(Map.Entry<? extends K,? extends V> entry)
61+
// @CanIgnoreReturnValue ImmutableMap.Builder<K,V> putAll(Map<? extends K,? extends V> map)
62+
// @CanIgnoreReturnValue ImmutableMap.Builder<K,V> putAll(Iterable<? extends Map.Entry<? extends K,? extends V>> entries)
63+
// @CanIgnoreReturnValue ImmutableMultimap.Builder<K,V> put(K key, V value)
64+
// @CanIgnoreReturnValue ImmutableMultimap.Builder<K,V> put(Map.Entry<? extends K,? extends V> entry)
65+
// @CanIgnoreReturnValue ImmutableMultimap.Builder<K,V> putAll(Iterable<? extends Map.Entry<? extends K,? extends V>> entries)
66+
// @CanIgnoreReturnValue ImmutableMultimap.Builder<K,V> putAll(K key, Iterable<? extends V> values)
67+
// @CanIgnoreReturnValue ImmutableMultimap.Builder<K,V> putAll(K key, V... values)
68+
// @CanIgnoreReturnValue ImmutableMultimap.Builder<K,V> putAll(Multimap<? extends K,? extends V> multimap)
69+
// @CanIgnoreReturnValue ImmutableTable.Builder<R,C,V> put(R rowKey, C columnKey, V value)
70+
// @CanIgnoreReturnValue ImmutableTable.Builder<R,C,V> put(Table.Cell<? extends R,? extends C,? extends V> cell)
71+
// @CanIgnoreReturnValue ImmutableTable.Builder<R,C,V> putAll(Table<? extends R,? extends C,? extends V> table)
72+
this.hasName(["put", "putAll"]) and
73+
argument = getNumberOfParameters() - 1
74+
}
75+
76+
override predicate returnsTaintFrom(int arg) { arg = [-1, argument] }
77+
78+
override predicate transfersTaint(int src, int sink) { src = argument and sink = -1 }
79+
}
80+
81+
/**
82+
* A reference type that extends a parameterization of `com.google.common.collect.Multimap`.
83+
*/
84+
class MultimapType extends RefType {
85+
MultimapType() { this.getASourceSupertype*().hasQualifiedName(guavaCollectPackage(), "Multimap") }
86+
87+
/** Gets the type of keys stored in this map. */
88+
RefType getKeyType() {
89+
exists(GenericInterface map | map.hasQualifiedName(guavaCollectPackage(), "Multimap") |
90+
indirectlyInstantiates(this, map, 0, result)
91+
)
92+
}
93+
94+
/** Gets the type of values stored in this map. */
95+
RefType getValueType() {
96+
exists(GenericInterface map | map.hasQualifiedName(guavaCollectPackage(), "Multimap") |
97+
indirectlyInstantiates(this, map, 1, result)
98+
)
99+
}
100+
}
101+
102+
private class MultimapWriteMethod extends TaintPreservingCallable {
103+
MultimapWriteMethod() {
104+
this.getDeclaringType() instanceof MultimapType and
105+
// @CanIgnoreReturnValue boolean put(@Nullable K key, @Nullable V value)
106+
// @CanIgnoreReturnValue boolean putAll(@Nullable K key, Iterable<? extends V> values)
107+
// @CanIgnoreReturnValue boolean putAll(Multimap<? extends K,? extends V> multimap)
108+
// @CanIgnoreReturnValue Collection<V> replaceValues(@Nullable K key, Iterable<? extends V> values)
109+
this.hasName(["put", "putAll", "replaceValues"])
110+
}
111+
112+
override predicate transfersTaint(int src, int sink) {
113+
src = getNumberOfParameters() - 1 and
114+
sink = -1
115+
}
116+
}
117+
118+
private class MultimapReadMethod extends TaintPreservingCallable {
119+
MultimapReadMethod() {
120+
this.getDeclaringType() instanceof MultimapType and
121+
// @CanIgnoreReturnValue Collection<V> replaceValues(@Nullable K key, Iterable<? extends V> values)
122+
// @CanIgnoreReturnValue Collection<V> removeAll(@CompatibleWith("K") @Nullable Object key)
123+
// Collection<V> get(@Nullable K key)
124+
// Collection<V> values()
125+
// Collection<Map.Entry<K,V>> entries()
126+
// Map<K,Collection<V>> asMap()
127+
this.hasName(["replaceValues", "removeAll", "get", "values", "entries", "asMap"])
128+
}
129+
130+
override predicate returnsTaintFrom(int arg) { arg = -1 }
131+
// Not implemented: Some of these methods return "views", which when modified will modify the map itself.
132+
// However, taint flow from these views to the map is not implemented.
133+
}
134+
135+
/**
136+
* A reference type that extends a parameterization of `com.google.common.collect.Table`.
137+
*/
138+
class TableType extends RefType {
139+
TableType() { this.getASourceSupertype*().hasQualifiedName(guavaCollectPackage(), "Table") }
140+
141+
/** Gets the type of row keys stored in this table. */
142+
RefType getRowType() {
143+
exists(GenericInterface table | table.hasQualifiedName(guavaCollectPackage(), "Table") |
144+
indirectlyInstantiates(this, table, 0, result)
145+
)
146+
}
147+
148+
/** Gets the type of row keys stored in this table. */
149+
RefType getColumnType() {
150+
exists(GenericInterface table | table.hasQualifiedName(guavaCollectPackage(), "Table") |
151+
indirectlyInstantiates(this, table, 1, result)
152+
)
153+
}
154+
155+
/** Gets the type of values stored in this table. */
156+
RefType getValueType() {
157+
exists(GenericInterface table | table.hasQualifiedName(guavaCollectPackage(), "Table") |
158+
indirectlyInstantiates(this, table, 2, result)
159+
)
160+
}
161+
}
162+
163+
private class TableWriteMethod extends TaintPreservingCallable {
164+
TableWriteMethod() {
165+
this.getDeclaringType() instanceof TableType and
166+
// @CanIgnoreReturnValue @Nullable V put(R rowKey, C columnKey, V value)
167+
// void putAll(Table<? extends R,? extends C,? extends V> table)
168+
this.hasName(["put", "putAll"])
169+
}
170+
171+
override predicate transfersTaint(int src, int sink) {
172+
src = getNumberOfParameters() - 1 and
173+
sink = -1
174+
}
175+
}
176+
177+
private class TableReadMethod extends TaintPreservingCallable {
178+
TableReadMethod() {
179+
this.getDeclaringType() instanceof TableType and
180+
// @CanIgnoreReturnValue @Nullable V put(R rowKey, C columnKey, V value)
181+
// @CanIgnoreReturnValue @Nullable V remove(@CompatibleWith("R") @Nullable Object rowKey, @CompatibleWith("C") @Nullable Object columnKey)
182+
// @Nullable V get(@CompatibleWith("R") @Nullable Object rowKey, @CompatibleWith("C") @Nullable Object columnKey)
183+
// Map<C,V> row(R rowKey)
184+
// Map<R,V> column(C columnKey)
185+
// Set<Table.Cell<R,C,V>> cellSet()
186+
// Collection<V> values()
187+
// Map<R,Map<C,V>> rowMap()
188+
// Map<C,Map<R,V>> columnMap()
189+
this
190+
.hasName(["put", "remove", "get", "row", "column", "cellSet", "values", "rowMap",
191+
"columnMap"])
192+
}
193+
194+
override predicate returnsTaintFrom(int arg) { arg = -1 }
195+
// Not implemented: Some of these methods return "views", which when modified will modify the table itself.
196+
// However, taint flow from these views to the table is not implemented.
197+
}
198+
199+
private class TableCellReadMethod extends TaintPreservingCallable {
200+
TableCellReadMethod() {
201+
exists(NestedType cell |
202+
cell.getEnclosingType() instanceof TableType and
203+
cell.hasName("Cell") and
204+
this.getDeclaringType().getASourceSupertype*() = cell and
205+
// @Nullable V getValue()
206+
this.hasName("getValue")
207+
)
208+
}
209+
210+
override predicate returnsTaintFrom(int arg) { arg = -1 }
211+
}
212+
213+
/**
214+
* An `of` static method on the various immutable collection types.
215+
*/
216+
private class OfMethod extends TaintPreservingCallable {
217+
string kind;
218+
219+
OfMethod() {
220+
this.getDeclaringType().(ImmutableContainerType).getKind() = kind and
221+
// static <E> ImmutableList<E> of(E e1, E e2, E e3, E e4, E e5, E e6)
222+
// static <K,V> ImmutableMap<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4)
223+
// static <K,V> ImmutableMultimap<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4)
224+
// static <R,C,V> ImmutableTable<R,C,V> of(R rowKey, C columnKey, V value)
225+
// etc for other types and numbers of parameters
226+
this.hasName("of") and
227+
this.isStatic()
228+
}
229+
230+
override predicate returnsTaintFrom(int arg) {
231+
arg = [0 .. getNumberOfParameters()] and
232+
(kind.matches("%Map") implies arg % 2 = 1) and
233+
(kind = "ImmutableTable" implies arg % 3 = 2)
234+
}
235+
}
236+
237+
/**
238+
* A `copyOf` or `sortedCopyOf` static method on the varios immutable collection types.
239+
*/
240+
private class CopyOfMethod extends TaintPreservingCallable {
241+
CopyOfMethod() {
242+
this.getDeclaringType() instanceof ImmutableContainerType and
243+
// static <E> ImmutableList<E> copyOf(E[] elements)
244+
// static <E> ImmutableList<E> copyOf(Iterable<? extends E> elements)
245+
// static <E> ImmutableList<E> copyOf(Collection<? extends E> elements)
246+
// static <E> ImmutableList<E> copyOf(Iterator<? extends E> elements)
247+
// static <E extends Comparable<? super E>> ImmutableList<E> sortedCopyOf(Iterable<? extends E> elements)
248+
// static <E> ImmutableList<E> sortedCopyOf(Comparator<? super E> comparator, Iterable<? extends E> elements)
249+
// static <K,V> ImmutableMap<K,V> copyOf(Map<? extends K,? extends V> map)
250+
// static <K,V> ImmutableMap<K,V> copyOf(Iterable<? extends Map.Entry<? extends K,? extends V>> entries)
251+
// static <K,V> ImmutableMultimap<K,V> copyOf(Multimap<? extends K,? extends V> multimap)
252+
// static <K,V> ImmutableMultimap<K,V> copyOf(Iterable<? extends Map.Entry<? extends K,? extends V>> entries)
253+
// static <R,C,V> ImmutableTable<R,C,V> copyOf (Table<? extends R,? extends C,? extends V> table)
254+
this.hasName(["copyOf", "sortedCopyOf"]) and
255+
this.isStatic()
256+
}
257+
258+
override predicate returnsTaintFrom(int arg) { arg = getNumberOfParameters() - 1 }
259+
}

java/ql/src/semmle/code/java/frameworks/guava/Guava.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44

55
import java
66
import StringUtils
7+
import Collections

0 commit comments

Comments
 (0)