Skip to content

Commit 12c906c

Browse files
authored
Merge pull request github#1503 from aschackmull/java/object-tostring-dispatch
Java: Restrict Object.toString() dispatch based on a more closed-world assumption.
2 parents 24b596d + 3588066 commit 12c906c

File tree

11 files changed

+677
-102
lines changed

11 files changed

+677
-102
lines changed

change-notes/1.22/analysis-java.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,8 @@
88

99
## Changes to QL libraries
1010

11+
* The virtual dispatch library has been updated to give more precise dispatch
12+
targets for `Object.toString()` calls. This affects all security queries and
13+
removes false positives that arose from paths through impossible `toString()`
14+
calls.
15+

java/ql/src/Likely Bugs/Serialization/NonSerializableField.ql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ predicate serializableType(RefType t) {
3434
// Collection interfaces are not serializable, but their implementations are
3535
// likely to be.
3636
collectionOrMapType(t) and
37+
not t instanceof RawType and
3738
forall(RefType param | param = t.(ParameterizedType).getATypeArgument() | serializableType(param))
3839
or
3940
exists(BoundedType bt | bt = t | serializableType(bt.getUpperBoundType()))

java/ql/src/semmle/code/java/Collections.qll

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ import java
1010
*/
1111
predicate instantiates(RefType t, GenericType g, int i, RefType arg) {
1212
t = g.getAParameterizedType() and
13+
arg = t.(ParameterizedType).getTypeArgument(i)
14+
or
15+
t = g.getRawType() and
1316
exists(g.getTypeParameter(i)) and
14-
(
15-
arg = t.(ParameterizedType).getTypeArgument(i)
16-
or
17-
t instanceof RawType and arg instanceof TypeObject
18-
)
17+
arg instanceof TypeObject
1918
}
2019

2120
/**
@@ -32,42 +31,18 @@ predicate instantiates(RefType t, GenericType g, int i, RefType arg) {
3231
* with the `0`-th type parameter being `Integer` and the `1`-th type parameter being `V`.
3332
*/
3433
predicate indirectlyInstantiates(RefType t, GenericType g, int i, RefType arg) {
35-
exists(RefType tsrc | tsrc = t.getSourceDeclaration() |
36-
// base case: `t` directly instantiates `g`
37-
tsrc = g and instantiates(t, g, i, arg)
38-
or
39-
// inductive step
40-
exists(RefType sup, RefType suparg |
41-
// follow `extends`/`implements`
42-
(extendsReftype(tsrc, sup) or implInterface(tsrc, sup)) and
43-
// check whether the subtype instantiates `g`
44-
indirectlyInstantiates(sup, g, i, suparg)
45-
|
46-
// if `t` is itself an instantiation of `tsrc` and `sup` instantiates
47-
// `g` to one of the type parameters of `tsrc`, we return the corresponding
48-
// instantiation in `t`
49-
exists(int j | suparg = tsrc.(GenericType).getTypeParameter(j) |
50-
instantiates(t, tsrc, j, arg)
51-
)
52-
or
53-
// otherwise, we directly return `suparg`
54-
not (
55-
t = tsrc.(GenericType).getAParameterizedType() and
56-
suparg = tsrc.(GenericType).getATypeParameter()
57-
) and
58-
arg = suparg
59-
)
34+
instantiates(t, g, i, arg)
35+
or
36+
exists(RefType sup |
37+
t.extendsOrImplements(sup) and
38+
indirectlyInstantiates(sup, g, i, arg)
6039
)
6140
}
6241

6342
/** A reference type that extends a parameterization of `java.util.Collection`. */
6443
class CollectionType extends RefType {
6544
CollectionType() {
66-
exists(ParameterizedInterface coll |
67-
coll.getSourceDeclaration().hasQualifiedName("java.util", "Collection")
68-
|
69-
this.hasSupertype*(coll)
70-
)
45+
this.getSourceDeclaration().getASourceSupertype*().hasQualifiedName("java.util", "Collection")
7146
}
7247

7348
/** Gets the type of elements stored in this collection. */

java/ql/src/semmle/code/java/Maps.qll

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import Collections
44
/** A reference type that extends a parameterization of `java.util.Map`. */
55
class MapType extends RefType {
66
MapType() {
7-
exists(ParameterizedInterface coll |
8-
coll.getSourceDeclaration().hasQualifiedName("java.util", "Map")
9-
|
10-
this.hasSupertype*(coll)
11-
)
7+
this.getSourceDeclaration().getASourceSupertype*().hasQualifiedName("java.util", "Map")
128
}
139

1410
/** Gets the type of keys stored in this map. */

java/ql/src/semmle/code/java/dataflow/TaintTracking.qll

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ private import semmle.code.java.frameworks.android.Intent
1515
private import semmle.code.java.frameworks.Guice
1616
private import semmle.code.java.frameworks.Protobuf
1717
private import semmle.code.java.Maps
18+
private import semmle.code.java.dataflow.internal.ContainerFlow
1819

1920
module TaintTracking {
2021
/**
@@ -218,6 +219,8 @@ module TaintTracking {
218219
v.getAFirstUse() = sink
219220
)
220221
or
222+
containerStep(src, sink)
223+
or
221224
constructorStep(src, sink)
222225
or
223226
qualifierToMethodStep(src, sink)
@@ -353,10 +356,6 @@ module TaintTracking {
353356

354357
/** Methods that passes tainted data from qualifier to argument. */
355358
private predicate taintPreservingQualifierToArgument(Method m, int arg) {
356-
m instanceof CollectionMethod and
357-
m.hasName("toArray") and
358-
arg = 1
359-
or
360359
m.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
361360
m.hasName("writeTo") and
362361
arg = 0
@@ -427,50 +426,6 @@ module TaintTracking {
427426
or
428427
m instanceof IntentGetExtraMethod
429428
or
430-
m
431-
.getDeclaringType()
432-
.getSourceDeclaration()
433-
.getASourceSupertype*()
434-
.hasQualifiedName("java.util", "Map<>$Entry") and
435-
m.hasName("getValue")
436-
or
437-
m
438-
.getDeclaringType()
439-
.getSourceDeclaration()
440-
.getASourceSupertype*()
441-
.hasQualifiedName("java.lang", "Iterable") and
442-
m.hasName("iterator")
443-
or
444-
m
445-
.getDeclaringType()
446-
.getSourceDeclaration()
447-
.getASourceSupertype*()
448-
.hasQualifiedName("java.util", "Iterator") and
449-
m.hasName("next")
450-
or
451-
m.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Enumeration") and
452-
m.hasName("nextElement")
453-
or
454-
m.(MapMethod).hasName("entrySet")
455-
or
456-
m.(MapMethod).hasName("get")
457-
or
458-
m.(MapMethod).hasName("remove")
459-
or
460-
m.(MapMethod).hasName("values")
461-
or
462-
m.(CollectionMethod).hasName("toArray")
463-
or
464-
m.(CollectionMethod).hasName("get")
465-
or
466-
m.(CollectionMethod).hasName("remove") and m.getParameterType(0).(PrimitiveType).hasName("int")
467-
or
468-
m.(CollectionMethod).hasName("subList")
469-
or
470-
m.(CollectionMethod).hasName("firstElement")
471-
or
472-
m.(CollectionMethod).hasName("lastElement")
473-
or
474429
m.getDeclaringType().hasQualifiedName("java.nio", "ByteBuffer") and
475430
m.hasName("get")
476431
or
@@ -656,18 +611,6 @@ module TaintTracking {
656611
method.getDeclaringType().hasQualifiedName("java.io", "ByteArrayOutputStream") and
657612
method.hasName("write") and
658613
arg = 0
659-
or
660-
method.(MapMethod).hasName("put") and arg = 1
661-
or
662-
method.(MapMethod).hasName("putAll") and arg = 0
663-
or
664-
method.(CollectionMethod).hasName("add") and arg = method.getNumberOfParameters() - 1
665-
or
666-
method.(CollectionMethod).hasName("addAll") and arg = method.getNumberOfParameters() - 1
667-
or
668-
method.(CollectionMethod).hasName("addElement") and arg = 0
669-
or
670-
method.(CollectionMethod).hasName("set") and arg = 1
671614
}
672615

673616
/** A comparison or equality test with a constant. */

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

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,139 @@ private module SsaImpl {
298298
)
299299
}
300300
}
301+
302+
private module AdjacentUsesImpl {
303+
/**
304+
* Holds if `rankix` is the rank the index `i` at which there is an SSA definition or explicit use of
305+
* `v` in the basic block `b`.
306+
*/
307+
private predicate defUseRank(BaseSsaSourceVariable v, BasicBlock b, int rankix, int i) {
308+
i = rank[rankix](int j | any(TrackedSsaDef def).definesAt(v, b, j) or variableUse(v, _, b, j))
309+
}
310+
311+
/** Gets the maximum rank index for the given variable and basic block. */
312+
private int lastRank(BaseSsaSourceVariable v, BasicBlock b) {
313+
result = max(int rankix | defUseRank(v, b, rankix, _))
314+
}
315+
316+
/** Holds if `v` is defined or used in `b`. */
317+
private predicate varOccursInBlock(BaseSsaSourceVariable v, BasicBlock b) {
318+
defUseRank(v, b, _, _)
319+
}
320+
321+
/** Holds if `v` occurs in `b` or one of `b`'s transitive successors. */
322+
private predicate blockPrecedesVar(BaseSsaSourceVariable v, BasicBlock b) {
323+
varOccursInBlock(v, b.getABBSuccessor*())
324+
}
325+
326+
/**
327+
* Holds if `b2` is a transitive successor of `b1` and `v` occurs in `b1` and
328+
* in `b2` or one of its transitive successors but not in any block on the path
329+
* between `b1` and `b2`.
330+
*/
331+
private predicate varBlockReaches(BaseSsaSourceVariable v, BasicBlock b1, BasicBlock b2) {
332+
varOccursInBlock(v, b1) and b2 = b1.getABBSuccessor()
333+
or
334+
exists(BasicBlock mid |
335+
varBlockReaches(v, b1, mid) and
336+
b2 = mid.getABBSuccessor() and
337+
not varOccursInBlock(v, mid) and
338+
blockPrecedesVar(v, b2)
339+
)
340+
}
341+
342+
/**
343+
* Holds if `b2` is a transitive successor of `b1` and `v` occurs in `b1` and
344+
* `b2` but not in any block on the path between `b1` and `b2`.
345+
*/
346+
private predicate varBlockStep(BaseSsaSourceVariable v, BasicBlock b1, BasicBlock b2) {
347+
varBlockReaches(v, b1, b2) and
348+
varOccursInBlock(v, b2)
349+
}
350+
351+
/**
352+
* Holds if `v` occurs at index `i1` in `b1` and at index `i2` in `b2` and
353+
* there is a path between them without any occurrence of `v`.
354+
*/
355+
predicate adjacentVarRefs(BaseSsaSourceVariable v, BasicBlock b1, int i1, BasicBlock b2, int i2) {
356+
exists(int rankix |
357+
b1 = b2 and
358+
defUseRank(v, b1, rankix, i1) and
359+
defUseRank(v, b2, rankix + 1, i2)
360+
)
361+
or
362+
defUseRank(v, b1, lastRank(v, b1), i1) and
363+
varBlockStep(v, b1, b2) and
364+
defUseRank(v, b2, 1, i2)
365+
}
366+
}
367+
private import AdjacentUsesImpl
368+
369+
/**
370+
* Holds if the value defined at `def` can reach `use` without passing through
371+
* any other uses, but possibly through phi nodes.
372+
*/
373+
cached
374+
predicate firstUse(TrackedSsaDef def, RValue use) {
375+
exists(BaseSsaSourceVariable v, BasicBlock b1, int i1, BasicBlock b2, int i2 |
376+
adjacentVarRefs(v, b1, i1, b2, i2) and
377+
def.definesAt(v, b1, i1) and
378+
variableUse(v, use, b2, i2)
379+
)
380+
or
381+
exists(
382+
BaseSsaSourceVariable v, TrackedSsaDef redef, BasicBlock b1, int i1, BasicBlock b2, int i2
383+
|
384+
redef instanceof BaseSsaPhiNode
385+
|
386+
adjacentVarRefs(v, b1, i1, b2, i2) and
387+
def.definesAt(v, b1, i1) and
388+
redef.definesAt(v, b2, i2) and
389+
firstUse(redef, use)
390+
)
391+
}
392+
393+
cached
394+
module SsaPublic {
395+
/**
396+
* Holds if `use1` and `use2` form an adjacent use-use-pair of the same SSA
397+
* variable, that is, the value read in `use1` can reach `use2` without passing
398+
* through any other use or any SSA definition of the variable.
399+
*/
400+
cached
401+
predicate baseSsaAdjacentUseUseSameVar(RValue use1, RValue use2) {
402+
exists(BaseSsaSourceVariable v, BasicBlock b1, int i1, BasicBlock b2, int i2 |
403+
adjacentVarRefs(v, b1, i1, b2, i2) and
404+
variableUse(v, use1, b1, i1) and
405+
variableUse(v, use2, b2, i2)
406+
)
407+
}
408+
409+
/**
410+
* Holds if `use1` and `use2` form an adjacent use-use-pair of the same
411+
* `SsaSourceVariable`, that is, the value read in `use1` can reach `use2`
412+
* without passing through any other use or any SSA definition of the variable
413+
* except for phi nodes.
414+
*/
415+
cached
416+
predicate baseSsaAdjacentUseUse(RValue use1, RValue use2) {
417+
baseSsaAdjacentUseUseSameVar(use1, use2)
418+
or
419+
exists(
420+
BaseSsaSourceVariable v, TrackedSsaDef def, BasicBlock b1, int i1, BasicBlock b2, int i2
421+
|
422+
adjacentVarRefs(v, b1, i1, b2, i2) and
423+
variableUse(v, use1, b1, i1) and
424+
def.definesAt(v, b2, i2) and
425+
firstUse(def, use2) and
426+
def instanceof BaseSsaPhiNode
427+
)
428+
}
429+
}
301430
}
302431
private import SsaImpl
303432
private import SsaDefReaches
433+
import SsaPublic
304434

305435
private newtype TBaseSsaVariable =
306436
TSsaPhiNode(BaseSsaSourceVariable v, BasicBlock b) { phiNode(v, b) } or
@@ -354,6 +484,16 @@ class BaseSsaVariable extends TBaseSsaVariable {
354484
/** Gets an access of this SSA variable. */
355485
RValue getAUse() { ssaDefReachesUse(_, this, result) }
356486

487+
/**
488+
* Gets an access of the SSA source variable underlying this SSA variable
489+
* that can be reached from this SSA variable without passing through any
490+
* other uses, but potentially through phi nodes.
491+
*
492+
* Subsequent uses can be found by following the steps defined by
493+
* `baseSsaAdjacentUseUse`.
494+
*/
495+
RValue getAFirstUse() { firstUse(this, result) }
496+
357497
/** Holds if this SSA variable is live at the end of `b`. */
358498
predicate isLiveAtEndOfBlock(BasicBlock b) { ssaDefReachesEndOfBlock(_, this, b) }
359499

0 commit comments

Comments
 (0)