diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll index c61c256505a..a57d1ca32be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll @@ -8,9 +8,27 @@ private import semmle.code.java.dataflow.TypeFlow private import semmle.code.java.dispatch.internal.Unification private module DispatchImpl { + private predicate hasHighConfidenceTarget(Call c) { + exists(SummarizedCallable sc | + sc = c.getCallee().getSourceDeclaration() and not sc.isAutoGenerated() + ) + or + exists(Callable srcTgt | + srcTgt = VirtualDispatch::viableCallable(c) and + not VirtualDispatch::lowConfidenceDispatchTarget(c, srcTgt) + ) + } + + private Callable sourceDispatch(Call c) { + result = VirtualDispatch::viableCallable(c) and + if VirtualDispatch::lowConfidenceDispatchTarget(c, result) + then not hasHighConfidenceTarget(c) + else any() + } + /** Gets a viable implementation of the target of the given `Call`. */ DataFlowCallable viableCallable(DataFlowCall c) { - result.asCallable() = VirtualDispatch::viableCallable(c.asCall()) + result.asCallable() = sourceDispatch(c.asCall()) or result.asSummarizedCallable() = c.asCall().getCallee().getSourceDeclaration() } @@ -22,7 +40,7 @@ private module DispatchImpl { */ private predicate mayBenefitFromCallContext(MethodAccess ma, Callable c, int i) { exists(Parameter p | - 2 <= strictcount(VirtualDispatch::viableImpl(ma)) and + 2 <= strictcount(sourceDispatch(ma)) and ma.getQualifier().(VarAccess).getVariable() = p and p.getPosition() = i and c.getAParameter() = p and @@ -31,7 +49,7 @@ private module DispatchImpl { ) or exists(OwnInstanceAccess ia | - 2 <= strictcount(VirtualDispatch::viableImpl(ma)) and + 2 <= strictcount(sourceDispatch(ma)) and (ia.isExplicit(ma.getQualifier()) or ia.isImplicitMethodQualifier(ma)) and i = -1 and c = ma.getEnclosingCallable() @@ -47,7 +65,7 @@ private module DispatchImpl { private predicate relevantContext(Call ctx, int i) { exists(Callable c | mayBenefitFromCallContext(_, c, i) and - c = VirtualDispatch::viableCallable(ctx) + c = sourceDispatch(ctx) ) } diff --git a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll index 38208db9b71..dd1406ef352 100644 --- a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll +++ b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll @@ -54,6 +54,16 @@ private module Dispatch { cached Method viableImpl(MethodAccess ma) { result = ObjFlow::viableImpl_out(ma) } + /** + * Holds if `m` is a viable implementation of the method called in `ma` for + * which we only have imprecise open-world type-based dispatch resolution, and + * the dispatch type is likely to yield implausible dispatch targets. + */ + cached + predicate lowConfidenceDispatchTarget(MethodAccess ma, Method m) { + m = viableImpl(ma) and lowConfidenceDispatch(ma) + } + /** * INTERNAL: Use `viableImpl` instead. * @@ -62,6 +72,42 @@ private module Dispatch { cached Method viableImpl_v3(MethodAccess ma) { result = DispatchFlow::viableImpl_out(ma) } + /** + * Holds if the best type bounds for the qualifier of `ma` are likely to + * contain implausible dispatch targets. + */ + private predicate lowConfidenceDispatch(VirtualMethodAccess ma) { + exists(RefType t | hasQualifierType(ma, t, false) | + lowConfidenceDispatchType(t.getSourceDeclaration()) + ) and + ( + not qualType(ma, _, _) + or + exists(RefType t | qualType(ma, t, false) | + lowConfidenceDispatchType(t.getSourceDeclaration()) + ) + ) and + ( + not qualUnionType(ma, _, _) + or + exists(RefType t | qualUnionType(ma, t, false) | + lowConfidenceDispatchType(t.getSourceDeclaration()) + ) + ) + } + + private predicate lowConfidenceDispatchType(SrcRefType t) { + t instanceof TypeObject + or + t instanceof FunctionalInterface + or + t.hasQualifiedName("java.io", "Serializable") + or + t.hasQualifiedName("java.lang", "Cloneable") + or + t.getPackage().hasName("java.util") and t instanceof Interface + } + /** * INTERNAL: Use `viableImpl` instead. *