Skip to content

Commit d3389d6

Browse files
committed
Add list of constants sanitizer for java.util.Set
1 parent a1e1cb1 commit d3389d6

File tree

2 files changed

+91
-70
lines changed

2 files changed

+91
-70
lines changed

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

Lines changed: 91 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -63,53 +63,82 @@ predicate methodCallHasConstantArguments(MethodCall mc) {
6363
)
6464
}
6565

66-
/** Classes for `java.util.List`. */
67-
module JavaUtilList {
68-
private class JavaUtilListContainsCall extends MethodCall {
69-
JavaUtilListContainsCall() {
66+
class CollectionClass extends string {
67+
CollectionClass() { this = ["List", "Set"] }
68+
}
69+
70+
/** Classes for `java.util.List` and `java.util.Set`. */
71+
module Collection {
72+
private class CollectionContainsCall extends MethodCall {
73+
CollectionClass collectionClass;
74+
75+
/** Gets whether the collection is a "List" or a "Set". */
76+
CollectionClass getCollectionClass() { result = collectionClass }
77+
78+
CollectionContainsCall() {
7079
exists(Method m |
7180
this.getMethod() = m and
7281
m.hasName("contains") and
73-
m.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "List")
82+
m.getDeclaringType()
83+
.getSourceDeclaration()
84+
.getASourceSupertype*()
85+
.hasQualifiedName("java.util", collectionClass)
7486
)
7587
}
7688
}
7789

7890
private class NonConstantElementAddition extends Expr {
91+
CollectionClass collectionClass;
92+
93+
/** Gets whether the collection is a "List" or a "Set". */
94+
CollectionClass getCollectionClass() { result = collectionClass }
95+
7996
NonConstantElementAddition() {
8097
exists(Method m, RefType t, MethodCall mc |
8198
this = mc.getQualifier() and
8299
mc.getMethod() = m and
83100
t = m.getDeclaringType().getSourceDeclaration().getASourceSupertype*()
84101
|
102+
collectionClass = "List" and
85103
t.hasQualifiedName("java.util", "List") and
86104
m.getName() = ["add", "addFirst", "addLast"] and
87105
not mc.getArgument(m.getNumberOfParameters() - 1).isCompileTimeConstant()
88106
or
107+
collectionClass = "Set" and
108+
t.hasQualifiedName("java.util", "Set") and
109+
m.getName() = ["add"] and
110+
not mc.getArgument(0).isCompileTimeConstant()
111+
or
89112
// If a whole collection is added then we don't try to track if it contains
90113
// only compile-time constants, and conservatively assume that it does.
91-
t.hasQualifiedName("java.util", ["Collection", "List"]) and m.getName() = "addAll"
114+
t.hasQualifiedName("java.util", "Collection") and
115+
m.getName() = "addAll"
92116
)
93117
}
94118
}
95119

96-
private predicate javaUtilListOfConstantsLocalFlowTo(Expr e) {
97-
exists(JavaUtilListOfConstants loc | DataFlow::localExprFlow(loc, e) |
120+
private predicate collectionOfConstantsLocalFlowTo(CollectionClass collectionClass, Expr e) {
121+
exists(CollectionOfConstants loc |
122+
loc.getCollectionClass() = collectionClass and DataFlow::localExprFlow(loc, e)
123+
|
98124
loc.isImmutable()
99125
or
100126
not DataFlow::localExprFlow(any(NonConstantElementAddition ncea), e)
101127
)
102128
}
103129

104-
private predicate javaUtilListOfConstantsFlowsTo(Expr e) {
105-
javaUtilListOfConstantsLocalFlowTo(e)
130+
private predicate collectionOfConstantsFlowsTo(CollectionClass collectionClass, Expr e) {
131+
collectionOfConstantsLocalFlowTo(collectionClass, e)
106132
or
107133
// Access a static final field to get an immutable list of constants.
108134
exists(Field f |
109135
f.isStatic() and
110136
f.isFinal() and
111137
forall(Expr v | v = f.getInitializer() or v = f.getAnAccess().(FieldWrite).getASource() |
112-
v = any(JavaUtilListOfConstants loc | loc.isImmutable())
138+
v =
139+
any(CollectionOfConstants loc |
140+
loc.getCollectionClass() = collectionClass and loc.isImmutable()
141+
)
113142
)
114143
|
115144
DataFlow::localExprFlow(f.getAnAccess(), e)
@@ -120,60 +149,69 @@ module JavaUtilList {
120149
* An invocation of `java.util.List.contains` where the qualifier contains only
121150
* compile-time constants.
122151
*/
123-
private class JavaUtilListOfConstantsContains extends ListOfConstantsComparison {
124-
JavaUtilListOfConstantsContains() {
125-
exists(JavaUtilListContainsCall mc |
152+
private class CollectionOfConstantsContains extends ListOfConstantsComparison {
153+
CollectionOfConstantsContains() {
154+
exists(CollectionContainsCall mc |
126155
this = mc and
127156
e = mc.getArgument(0) and
128157
outcome = true and
129-
javaUtilListOfConstantsFlowsTo(mc.getQualifier())
158+
collectionOfConstantsFlowsTo(mc.getCollectionClass(), mc.getQualifier())
130159
)
131160
}
132161
}
133162

134163
/**
135164
* An instance of `java.util.List` which contains only compile-time constants.
136165
*/
137-
abstract class JavaUtilListOfConstants extends Call {
166+
abstract class CollectionOfConstants extends Call {
167+
CollectionClass collectionClass;
168+
169+
/** Gets whether the collection is a "List" or a "Set". */
170+
CollectionClass getCollectionClass() { result = collectionClass }
171+
138172
/** Holds if this list of constants is immutable. */
139173
abstract predicate isImmutable();
140174
}
141175

142176
/**
143-
* A invocation of a constructor of a type that extends `java.util.List`
144-
* which constructs an empty mutable list.
177+
* A invocation of a constructor of a type that extends `java.util.List` or
178+
* `java.util.Set` which constructs an empty mutable list.
145179
*/
146-
private class JavaUtilListOfConstantsEmptyConstructor extends ClassInstanceExpr,
147-
JavaUtilListOfConstants
180+
private class CollectionOfConstantsEmptyConstructor extends ClassInstanceExpr,
181+
CollectionOfConstants
148182
{
149-
JavaUtilListOfConstantsEmptyConstructor() {
183+
CollectionOfConstantsEmptyConstructor() {
150184
this.getConstructedType()
151185
.getSourceDeclaration()
152186
.getASourceSupertype*()
153-
.hasQualifiedName("java.util", "List") and
187+
.hasQualifiedName("java.util", collectionClass) and
154188
exists(Constructor c | c = this.getConstructor() |
155189
c.hasNoParameters()
156190
or
157191
c.getNumberOfParameters() = 1 and
158192
c.getParameter(0).getType().(PrimitiveType).hasName("int")
193+
or
194+
c.getNumberOfParameters() = 2 and
195+
c.getParameter(0).getType().(PrimitiveType).hasName("int") and
196+
c.getParameter(0).getType().(PrimitiveType).hasName("float")
159197
)
160198
}
161199

162200
override predicate isImmutable() { none() }
163201
}
164202

165203
/**
166-
* A invocation of a constructor of a type that extends `java.util.List`
167-
* which constructs an empty mutable list.
204+
* A invocation of a constructor of a type that extends `java.util.List` or
205+
* `java.util.Set` which constructs a non-empty mutable list.
168206
*/
169-
private class JavaUtilListOfConstantsNonEmptyConstructor extends ClassInstanceExpr,
170-
JavaUtilListOfConstants
207+
private class CollectionOfConstantsNonEmptyConstructor extends ClassInstanceExpr,
208+
CollectionOfConstants
171209
{
172-
JavaUtilListOfConstantsNonEmptyConstructor() {
210+
CollectionOfConstantsNonEmptyConstructor() {
173211
this.getConstructedType()
174212
.getSourceDeclaration()
175213
.getASourceSupertype*()
176-
.hasQualifiedName("java.util", "List") and
214+
.hasQualifiedName("java.util", collectionClass) and
177215
exists(Constructor c | c = this.getConstructor() |
178216
c.getNumberOfParameters() = 1 and
179217
c.getParameter(0)
@@ -182,7 +220,7 @@ module JavaUtilList {
182220
.getASourceSupertype*()
183221
.hasQualifiedName("java.util", "Collection")
184222
) and
185-
javaUtilListOfConstantsFlowsTo(this.getArgument(0))
223+
collectionOfConstantsFlowsTo(_, this.getArgument(0))
186224
}
187225

188226
override predicate isImmutable() { none() }
@@ -191,11 +229,15 @@ module JavaUtilList {
191229
/**
192230
* A invocation of `java.util.Arrays.asList` which constructs a mutable list.
193231
*/
194-
private class JavaUtilArraysAsList extends MethodCall, JavaUtilListOfConstants {
232+
private class JavaUtilArraysAsList extends MethodCall, CollectionOfConstants {
195233
JavaUtilArraysAsList() {
234+
collectionClass = "List" and
196235
exists(Method m | this.getMethod() = m |
197236
m.hasName("asList") and
198-
m.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Arrays")
237+
m.getDeclaringType()
238+
.getSourceDeclaration()
239+
.getASourceSupertype*()
240+
.hasQualifiedName("java.util", "Arrays")
199241
) and
200242
methodCallHasConstantArguments(this)
201243
}
@@ -207,12 +249,14 @@ module JavaUtilList {
207249
* An invocation of `java.util.List.of` which constructs an immutable list
208250
* which contains only compile-time constants.
209251
*/
210-
private class JavaUtilListOfConstantsCreatedWithListOf extends MethodCall, JavaUtilListOfConstants
211-
{
212-
JavaUtilListOfConstantsCreatedWithListOf() {
252+
private class CollectionOfConstantsCreatedWithOf extends MethodCall, CollectionOfConstants {
253+
CollectionOfConstantsCreatedWithOf() {
213254
exists(Method m | this.getMethod() = m |
214255
m.hasName("of") and
215-
m.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "List")
256+
m.getDeclaringType()
257+
.getSourceDeclaration()
258+
.getASourceSupertype*()
259+
.hasQualifiedName("java.util", collectionClass)
216260
) and
217261
methodCallHasConstantArguments(this)
218262
}
@@ -221,19 +265,23 @@ module JavaUtilList {
221265
}
222266

223267
/**
224-
* An invocation of `java.util.Collections.unmodifiableList` which constructs an immutable list
225-
* which contains only compile-time constants.
268+
* An invocation of `java.util.Collections.unmodifiableList` or
269+
* `java.util.Collections.unmodifiableSet` which constructs an immutable
270+
* list/set which contains only compile-time constants.
226271
*/
227-
private class JavaUtilListOfConstantsCreatedWithCollectionsUnmodifiableList extends MethodCall,
228-
JavaUtilListOfConstants
272+
private class CollectionOfConstantsCreatedWithCollectionsUnmodifiableList extends MethodCall,
273+
CollectionOfConstants
229274
{
230-
JavaUtilListOfConstantsCreatedWithCollectionsUnmodifiableList() {
275+
CollectionOfConstantsCreatedWithCollectionsUnmodifiableList() {
231276
exists(Method m |
232-
m.hasName("unmodifiableList") and
233-
m.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Collections") and
277+
m.hasName("unmodifiable" + collectionClass) and
278+
m.getDeclaringType()
279+
.getSourceDeclaration()
280+
.getASourceSupertype*()
281+
.hasQualifiedName("java.util", "Collections") and
234282
this.getMethod() = m
235283
|
236-
javaUtilListOfConstantsFlowsTo(this.getArgument(0))
284+
collectionOfConstantsFlowsTo(collectionClass, this.getArgument(0))
237285
)
238286
}
239287

0 commit comments

Comments
 (0)