Java: Remove low confidence dispatch for which we have a manual summary.

This commit is contained in:
Anders Schack-Mulligen
2022-08-23 15:50:30 +02:00
parent d713910714
commit ba3ebeec2c
2 changed files with 68 additions and 4 deletions

View File

@@ -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)
)
}

View File

@@ -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.
*