mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Java: Deduplicate unification code as a parameterised module.
This commit is contained in:
@@ -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<unificationTargetLeft/1, unificationTargetRight/1>;
|
||||
|
||||
private int parameterPosition() { result in [-1, any(Parameter p).getPosition()] }
|
||||
|
||||
|
||||
@@ -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<unificationTargetLeft/1, unificationTargetRight/1>;
|
||||
|
||||
84
java/ql/lib/semmle/code/java/dispatch/Unification.qll
Normal file
84
java/ql/lib/semmle/code/java/dispatch/Unification.qll
Normal file
@@ -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<unificationTarget/1 targetLeft, unificationTarget/1 targetRight> {
|
||||
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
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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<unificationTargetLeft_v2/1, unificationTargetRight/1>;
|
||||
|
||||
/**
|
||||
* 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<unificationTargetLeft_v1/1, unificationTargetRight/1>;
|
||||
|
||||
private RefType getPreciseType(Expr e) {
|
||||
result = e.(FunctionalExpr).getConstructedType()
|
||||
or
|
||||
|
||||
Reference in New Issue
Block a user