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 543c392817c..73247d47698 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll @@ -4,6 +4,7 @@ private import DataFlowUtil private import semmle.code.java.dataflow.InstanceAccess private import semmle.code.java.dataflow.FlowSummary private import semmle.code.java.dispatch.VirtualDispatch as VirtualDispatch +private import semmle.code.java.dispatch.Unification private module DispatchImpl { /** Gets a viable implementation of the target of the given `Call`. */ @@ -115,74 +116,20 @@ private module DispatchImpl { exact = false and exists(RefType t2 | result.asCallable() = VirtualDispatch::viableMethodImpl(def, t.getSourceDeclaration(), t2) and - not failsUnification(t, t2) + not Unification::failsUnification(t, t2) ) or result.asSummarizedCallable() = def ) } - pragma[noinline] - private predicate unificationTargetLeft(ParameterizedType t1, GenericType g) { - contextArgHasType(_, _, t1, _) and t1.getGenericType() = g + private predicate unificationTargetLeft(ParameterizedType t1) { contextArgHasType(_, _, t1, _) } + + private predicate unificationTargetRight(ParameterizedType t2) { + exists(VirtualDispatch::viableMethodImpl(_, _, t2)) } - pragma[noinline] - private predicate unificationTargetRight(ParameterizedType t2, GenericType g) { - exists(VirtualDispatch::viableMethodImpl(_, _, t2)) and t2.getGenericType() = g - } - - private predicate unificationTargets(Type t1, Type t2) { - exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g)) - or - exists(Array a1, Array a2 | - unificationTargets(a1, a2) and - t1 = a1.getComponentType() and - t2 = a2.getComponentType() - ) - or - exists(ParameterizedType pt1, ParameterizedType pt2, int pos | - unificationTargets(pt1, pt2) and - not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and - t1 = pt1.getTypeArgument(pos) and - t2 = pt2.getTypeArgument(pos) - ) - } - - pragma[noinline] - private predicate typeArgsOfUnificationTargets( - ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2 - ) { - unificationTargets(t1, t2) and - arg1 = t1.getTypeArgument(pos) and - arg2 = t2.getTypeArgument(pos) - } - - private predicate failsUnification(Type t1, Type t2) { - unificationTargets(t1, t2) and - ( - exists(RefType arg1, RefType arg2 | - typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and - failsUnification(arg1, arg2) - ) - or - failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType()) - or - not ( - t1 instanceof Array and t2 instanceof Array - or - t1.(PrimitiveType) = t2.(PrimitiveType) - or - t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration() - or - t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration() - or - t1 instanceof BoundedType and t2 instanceof RefType - or - t1 instanceof RefType and t2 instanceof BoundedType - ) - ) - } + private module Unification = MkUnification; private int parameterPosition() { result in [-1, any(Parameter p).getPosition()] } diff --git a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll index a05553eb373..757f7d1d2fb 100644 --- a/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll +++ b/java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll @@ -15,6 +15,7 @@ private import semmle.code.java.dataflow.internal.DataFlowUtil private import semmle.code.java.dataflow.internal.DataFlowPrivate private import semmle.code.java.dataflow.internal.ContainerFlow private import semmle.code.java.dataflow.InstanceAccess +private import semmle.code.java.dispatch.Unification /** * Gets a viable dispatch target for `ma`. This is the input dispatch relation. @@ -266,67 +267,10 @@ Method viableImpl_out(MethodAccess ma) { ) } -private module Unification { - pragma[noinline] - private predicate unificationTargetLeft(ParameterizedType t1, GenericType g) { - objectToStringQualType(_, t1) and t1.getGenericType() = g - } +private predicate unificationTargetLeft(ParameterizedType t1) { objectToStringQualType(_, t1) } - pragma[noinline] - private predicate unificationTargetRight(ParameterizedType t2, GenericType g) { - exists(viableMethodImpl(_, _, t2)) and t2.getGenericType() = g - } - - private predicate unificationTargets(Type t1, Type t2) { - exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g)) - or - exists(Array a1, Array a2 | - unificationTargets(a1, a2) and - t1 = a1.getComponentType() and - t2 = a2.getComponentType() - ) - or - exists(ParameterizedType pt1, ParameterizedType pt2, int pos | - unificationTargets(pt1, pt2) and - not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and - t1 = pt1.getTypeArgument(pos) and - t2 = pt2.getTypeArgument(pos) - ) - } - - pragma[noinline] - private predicate typeArgsOfUnificationTargets( - ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2 - ) { - unificationTargets(t1, t2) and - arg1 = t1.getTypeArgument(pos) and - arg2 = t2.getTypeArgument(pos) - } - - pragma[nomagic] - predicate failsUnification(Type t1, Type t2) { - unificationTargets(t1, t2) and - ( - exists(RefType arg1, RefType arg2 | - typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and - failsUnification(arg1, arg2) - ) - or - failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType()) - or - not ( - t1 instanceof Array and t2 instanceof Array - or - t1.(PrimitiveType) = t2.(PrimitiveType) - or - t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration() - or - t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration() - or - t1 instanceof BoundedType and t2 instanceof RefType - or - t1 instanceof RefType and t2 instanceof BoundedType - ) - ) - } +private predicate unificationTargetRight(ParameterizedType t2) { + exists(viableMethodImpl(_, _, t2)) } + +private module Unification = MkUnification; diff --git a/java/ql/lib/semmle/code/java/dispatch/Unification.qll b/java/ql/lib/semmle/code/java/dispatch/Unification.qll new file mode 100644 index 00000000000..08858946772 --- /dev/null +++ b/java/ql/lib/semmle/code/java/dispatch/Unification.qll @@ -0,0 +1,84 @@ +/** + * Provides a module to check whether two `ParameterizedType`s are unifiable. + */ + +import java + +/** Holds if `t` is a relevant type to consider for unification. */ +signature predicate unificationTarget(ParameterizedType t); + +/** + * Given two sets of parameterised types to consider for unification, returns + * the set of pairs that are not unifiable. + */ +module MkUnification { + pragma[noinline] + private predicate unificationTargetLeft(ParameterizedType t1, GenericType g) { + targetLeft(t1) and t1.getGenericType() = g + } + + pragma[noinline] + private predicate unificationTargetRight(ParameterizedType t2, GenericType g) { + targetRight(t2) and t2.getGenericType() = g + } + + private predicate unificationTargets(Type t1, Type t2) { + exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g)) + or + exists(Array a1, Array a2 | + unificationTargets(a1, a2) and + t1 = a1.getComponentType() and + t2 = a2.getComponentType() + ) + or + exists(ParameterizedType pt1, ParameterizedType pt2, int pos | + unificationTargets(pt1, pt2) and + not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and + t1 = pt1.getTypeArgument(pos) and + t2 = pt2.getTypeArgument(pos) + ) + } + + pragma[noinline] + private predicate typeArgsOfUnificationTargets( + ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2 + ) { + unificationTargets(t1, t2) and + arg1 = t1.getTypeArgument(pos) and + arg2 = t2.getTypeArgument(pos) + } + + /** + * Holds if `t1` and `t2` are not unifiable. + * + * Restricted to only consider pairs of types such that `targetLeft(t1)`, + * `targetRight(t2)`, and that both are parameterised instances of the same + * generic type. + */ + pragma[nomagic] + predicate failsUnification(Type t1, Type t2) { + unificationTargets(t1, t2) and + ( + exists(RefType arg1, RefType arg2 | + typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and + failsUnification(arg1, arg2) + ) + or + failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType()) + or + not ( + t1 instanceof Array and t2 instanceof Array + or + t1.(PrimitiveType) = t2.(PrimitiveType) + or + t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration() + or + t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration() + or + t1 instanceof BoundedType and t2 instanceof RefType + or + t1 instanceof RefType and t2 instanceof BoundedType + ) + ) + } +} diff --git a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll index 7d4b617ca19..10850a908bf 100644 --- a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll +++ b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll @@ -9,6 +9,7 @@ private import DispatchFlow as DispatchFlow private import ObjFlow as ObjFlow private import semmle.code.java.dataflow.internal.BaseSSA private import semmle.code.java.controlflow.Guards +private import semmle.code.java.dispatch.Unification /** * A conservative analysis that returns a single method - if we can establish @@ -91,69 +92,10 @@ private module Dispatch { ) } - private module Unification_v2 { - pragma[noinline] - private predicate unificationTargetLeft(ParameterizedType t1, GenericType g) { - qualType(_, t1, _) and t1.getGenericType() = g - } + private predicate unificationTargetLeft_v2(ParameterizedType t1) { qualType(_, t1, _) } - pragma[noinline] - private predicate unificationTargetRight(ParameterizedType t2, GenericType g) { - exists(viableMethodImpl(_, _, t2)) and t2.getGenericType() = g - } - - private predicate unificationTargets(Type t1, Type t2) { - exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g)) - or - exists(Array a1, Array a2 | - unificationTargets(a1, a2) and - t1 = a1.getComponentType() and - t2 = a2.getComponentType() - ) - or - exists(ParameterizedType pt1, ParameterizedType pt2, int pos | - unificationTargets(pt1, pt2) and - not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and - t1 = pt1.getTypeArgument(pos) and - t2 = pt2.getTypeArgument(pos) - ) - } - - pragma[noinline] - private predicate typeArgsOfUnificationTargets( - ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2 - ) { - unificationTargets(t1, t2) and - arg1 = t1.getTypeArgument(pos) and - arg2 = t2.getTypeArgument(pos) - } - - predicate failsUnification(Type t1, Type t2) { - unificationTargets(t1, t2) and - ( - exists(RefType arg1, RefType arg2 | - typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and - failsUnification(arg1, arg2) - ) - or - failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType()) - or - not ( - t1 instanceof Array and t2 instanceof Array - or - t1.(PrimitiveType) = t2.(PrimitiveType) - or - t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration() - or - t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration() - or - t1 instanceof BoundedType and t2 instanceof RefType - or - t1 instanceof RefType and t2 instanceof BoundedType - ) - ) - } - } + private module Unification_v2 = + MkUnification; /** * INTERNAL: Use `viableImpl` instead. @@ -203,70 +145,15 @@ private module Dispatch { else result = source.getMethod().getSourceDeclaration() } - private module Unification_v1 { - pragma[noinline] - private predicate unificationTargetLeft(ParameterizedType t1, GenericType g) { - hasQualifierType(_, t1, _) and t1.getGenericType() = g - } + private predicate unificationTargetLeft_v1(ParameterizedType t1) { hasQualifierType(_, t1, _) } - pragma[noinline] - private predicate unificationTargetRight(ParameterizedType t2, GenericType g) { - exists(viableMethodImpl(_, _, t2)) and t2.getGenericType() = g - } - - private predicate unificationTargets(Type t1, Type t2) { - exists(GenericType g | unificationTargetLeft(t1, g) and unificationTargetRight(t2, g)) - or - exists(Array a1, Array a2 | - unificationTargets(a1, a2) and - t1 = a1.getComponentType() and - t2 = a2.getComponentType() - ) - or - exists(ParameterizedType pt1, ParameterizedType pt2, int pos | - unificationTargets(pt1, pt2) and - not pt1.getSourceDeclaration() != pt2.getSourceDeclaration() and - t1 = pt1.getTypeArgument(pos) and - t2 = pt2.getTypeArgument(pos) - ) - } - - pragma[noinline] - private predicate typeArgsOfUnificationTargets( - ParameterizedType t1, ParameterizedType t2, int pos, RefType arg1, RefType arg2 - ) { - unificationTargets(t1, t2) and - arg1 = t1.getTypeArgument(pos) and - arg2 = t2.getTypeArgument(pos) - } - - predicate failsUnification(Type t1, Type t2) { - unificationTargets(t1, t2) and - ( - exists(RefType arg1, RefType arg2 | - typeArgsOfUnificationTargets(t1, t2, _, arg1, arg2) and - failsUnification(arg1, arg2) - ) - or - failsUnification(t1.(Array).getComponentType(), t2.(Array).getComponentType()) - or - not ( - t1 instanceof Array and t2 instanceof Array - or - t1.(PrimitiveType) = t2.(PrimitiveType) - or - t1.(Class).getSourceDeclaration() = t2.(Class).getSourceDeclaration() - or - t1.(Interface).getSourceDeclaration() = t2.(Interface).getSourceDeclaration() - or - t1 instanceof BoundedType and t2 instanceof RefType - or - t1 instanceof RefType and t2 instanceof BoundedType - ) - ) - } + private predicate unificationTargetRight(ParameterizedType t2) { + exists(viableMethodImpl(_, _, t2)) } + private module Unification_v1 = + MkUnification; + private RefType getPreciseType(Expr e) { result = e.(FunctionalExpr).getConstructedType() or