Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 96 additions & 80 deletions java/ql/src/semmle/code/java/dispatch/DispatchFlow.qll
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,98 @@ private Callable dispatchCand(Call c) {
result = viableImpl_inp(c)
}

/**
* Holds if `t` and all its enclosing types are public.
*/
private predicate veryPublic(RefType t) {
t.isPublic() and
(
not t instanceof NestedType or
veryPublic(t.(NestedType).getEnclosingType())
)
}

/**
* Holds if `cie` occurs as the initializer of a public static field.
*/
private predicate publicStaticFieldInit(ClassInstanceExpr cie) {
exists(Field f |
f.isStatic() and
f.isPublic() and
veryPublic(f.getDeclaringType()) and
f.getInitializer() = cie
)
}

/**
* Holds if a `ClassInstanceExpr` constructing `t` occurs as the initializer of
* a public static field.
*/
private predicate publicThroughField(RefType t) {
exists(ClassInstanceExpr cie |
cie.getConstructedType() = t and
publicStaticFieldInit(cie)
)
}

/**
* Holds if `t` and its subtypes are private or anonymous.
*/
private predicate privateConstruction(RefType t) {
(t.isPrivate() or t instanceof AnonymousClass) and
not publicThroughField(t) and
forall(SrcRefType sub | sub.getASourceSupertype+() = t.getSourceDeclaration() |
(sub.isPrivate() or sub instanceof AnonymousClass) and
not publicThroughField(sub)
)
}

/**
* Holds if `m` is declared on a type that we will track all instantiations of
* for the purpose of virtual dispatch to `m`. This holds in particular for
* lambda methods and methods on other anonymous classes.
*/
private predicate trackedMethod(Method m) {
privateConstruction(m.getDeclaringType().getSourceDeclaration())
}

/**
* Holds if `t` declares or inherits the tracked method `m`.
*/
private predicate trackedMethodOnType(Method m, SrcRefType t) {
exists(Method m0 |
t.hasMethod(m0, _, _) and
m = m0.getSourceDeclaration() and
trackedMethod(m)
)
}

/**
* Holds if `ma` may dispatch to the tracked method `m` declared or inherited
* by the type constructed by `cie`. Thus the dispatch from `ma` to `m` will
* only be included if `cie` flows to the qualifier of `ma`.
*/
private predicate dispatchOrigin(ClassInstanceExpr cie, MethodAccess ma, Method m) {
m = viableImpl_inp(ma) and
not m = ma.getMethod().getSourceDeclaration() and
trackedMethodOnType(m, cie.getConstructedType().getSourceDeclaration())
}

/** Holds if `t` is a type that is relevant for dispatch flow. */
private predicate relevant(RefType t) {
exists(ClassInstanceExpr cie | dispatchOrigin(cie, _, _) and t = cie.getConstructedType().getSourceDeclaration()) or
relevant(t.getErasure()) or
exists(RefType r | relevant(r) and t = r.getASourceSupertype()) or
relevant(t.(Array).getComponentType()) or
t instanceof MapType or
t instanceof CollectionType
}

/** A node with a type that is relevant for dispatch flow. */
private class RelevantNode extends Node {
RelevantNode() { relevant(this.getType()) }
}

/**
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
* The instance parameter is considered to have index `-1`.
Expand All @@ -26,7 +118,8 @@ pragma[nomagic]
private predicate viableParamCand(Call call, int i, ParameterNode p) {
exists(Callable callable |
callable = dispatchCand(call) and
p.isParameterOf(callable, i)
p.isParameterOf(callable, i) and
p instanceof RelevantNode
)
}

Expand All @@ -43,7 +136,7 @@ private predicate viableArgParamCand(ArgumentNode arg, ParameterNode p) {
/**
* Holds if data may flow from `n1` to `n2` in a single step through a call or a return.
*/
private predicate callFlowStepCand(Node n1, Node n2) {
private predicate callFlowStepCand(RelevantNode n1, RelevantNode n2) {
exists(ReturnStmt ret, Method m |
ret.getEnclosingCallable() = m and
ret.getResult() = n1.asExpr() and
Expand All @@ -56,7 +149,7 @@ private predicate callFlowStepCand(Node n1, Node n2) {
* Holds if data may flow from `n1` to `n2` in a single step that does not go
* through a call or a return.
*/
private predicate flowStep(Node n1, Node n2) {
private predicate flowStep(RelevantNode n1, RelevantNode n2) {
exists(BaseSsaVariable v, BaseSsaVariable def |
def.(BaseSsaUpdate).getDefiningExpr().(VariableAssign).getSource() = n1.asExpr() or
def.(BaseSsaImplicitInit).isParameterDefinition(n1.asParameter()) or
Expand Down Expand Up @@ -126,83 +219,6 @@ private predicate flowStep(Node n1, Node n2) {
)
}

/**
* Holds if `t` and all its enclosing types are public.
*/
private predicate veryPublic(RefType t) {
t.isPublic() and
(
not t instanceof NestedType or
veryPublic(t.(NestedType).getEnclosingType())
)
}

/**
* Holds if `cie` occurs as the initializer of a public static field.
*/
private predicate publicStaticFieldInit(ClassInstanceExpr cie) {
exists(Field f |
f.isStatic() and
f.isPublic() and
veryPublic(f.getDeclaringType()) and
f.getInitializer() = cie
)
}

/**
* Holds if a `ClassInstanceExpr` constructing `t` occurs as the initializer of
* a public static field.
*/
private predicate publicThroughField(RefType t) {
exists(ClassInstanceExpr cie |
cie.getConstructedType() = t and
publicStaticFieldInit(cie)
)
}

/**
* Holds if `t` and its subtypes are private or anonymous.
*/
private predicate privateConstruction(RefType t) {
(t.isPrivate() or t instanceof AnonymousClass) and
not publicThroughField(t) and
forall(SrcRefType sub | sub.getASourceSupertype+() = t.getSourceDeclaration() |
(sub.isPrivate() or sub instanceof AnonymousClass) and
not publicThroughField(sub)
)
}

/**
* Holds if `m` is declared on a type that we will track all instantiations of
* for the purpose of virtual dispatch to `m`. This holds in particular for
* lambda methods and methods on other anonymous classes.
*/
private predicate trackedMethod(Method m) {
privateConstruction(m.getDeclaringType().getSourceDeclaration())
}

/**
* Holds if `t` declares or inherits the tracked method `m`.
*/
private predicate trackedMethodOnType(Method m, SrcRefType t) {
exists(Method m0 |
t.hasMethod(m0, _, _) and
m = m0.getSourceDeclaration() and
trackedMethod(m)
)
}

/**
* Holds if `ma` may dispatch to the tracked method `m` declared or inherited
* by the type constructed by `cie`. Thus the dispatch from `ma` to `m` will
* only be included if `cie` flows to the qualifier of `ma`.
*/
private predicate dispatchOrigin(ClassInstanceExpr cie, MethodAccess ma, Method m) {
m = viableImpl_inp(ma) and
not m = ma.getMethod().getSourceDeclaration() and
trackedMethodOnType(m, cie.getConstructedType().getSourceDeclaration())
}

/**
* Holds if `n` is forward-reachable from a relevant `ClassInstanceExpr`.
*/
Expand Down