About working with Java types
类Type
及其子类表示java中的类。
PrimitiveType
表示基础数据类型:
RefType
及其子类表示引用数据类型。其中,getASupertype
和getASubtype
返回它的直接父类和子类。
class A {}
interface I {}
class B extends A implements I {}
在上例中,A的父类为java.lang.Object
,子类为B,同样I也是。B有两个直接父类和子类A和I,没有子类。
例如,找到类B的所有祖先类:
import java
from Class B
where B.hasName("ShiroRealm")
select B.getASupertype+()
RefType类中提供了方法getAMember可以获得所有的member(包括field, method, constructor等),方法inherits(Method m)
判断是否声明或者继承了方法m。
eg,找到所有的member:
import java
from RefType R
where R.hasName("ShiroRealm")
select R.getAMember()
eg, 找到实现了方法doGetAuthorizationInfo的类:
import java
from RefType R, Method m
where m.hasName("doGetAuthorizationInfo") and R.inherits(m)
select R
Example: Finding problematic array casts
目标:查找array类型的downcast。(可能会导致ClassCastException)
import java
from CastExpr ce, Array source, Array target
where source = ce.getExpr().getType() and
target = ce.getType() and
target.getElementType().(RefType).getASupertype+() = source.getElementType()
select ce, "Potentially problematic array downcast."
注意:在CodeQL中,类型转换cast不会失败:如果一个类型不能转换到目标类型,那么它就不会被包含在这个查询结果里。
上例中,在如下的情况下,也会误报:
List l = new ArrayList();
// add some elements of type A to l
A[] as = (A[])l.toArray(new A[0]);
因此,可以优化查询,排除掉此种查询结果。
优化的codeQL查询如下所示:
import java
/** class representing java.util.Collection.toArray(T[]) */
class CollectionToArray extends Method {
CollectionToArray() {
this.getDeclaringType().hasQualifiedName("java.util", "Collection") and
this.hasName("toArray") and
this.getNumberOfParameters() = 1
}
}
/** class representing calls to java.util.Collection.toArray(T[]) */
class CollectionToArrayCall extends MethodAccess {
CollectionToArrayCall() {
exists(CollectionToArray m |
this.getMethod().getSourceDeclaration().overridesOrInstantiates*(m)
)
}
/** the call's actual return type, as determined from its argument */
Array getActualReturnType() {
result = this.getArgument(0).getType()
}
}
from CastExpr ce, Array source, Array target
where source = ce.getExpr().getType() and
target = ce.getType() and
target.getElementType().(RefType).getASupertype+() = source.getElementType() and
not ce.getExpr().(CollectionToArrayCall).getActualReturnType() = target
select ce, "Potentially problematic array downcast."
Example: Finding mismatched contains checks
import java
class JavaUtilCollection extends GenericInterface {
JavaUtilCollection() {
this.hasQualifiedName("java.util", "Collection")
}
}
class JavaUtilCollectionContains extends Method {
JavaUtilCollectionContains() {
this.getDeclaringType() instanceof JavaUtilCollection and
this.hasStringSignature("contains(Object)")
}
}
class JavaUtilCollectionContainsCall extends MethodAccess {
JavaUtilCollectionContainsCall() {
exists(JavaUtilCollectionContains jucc |
this.getMethod().getSourceDeclaration().overrides*(jucc)
)
}
Type getArgumentType() {
result = this.getArgument(0).getType()
}
Type getCollectionElementType() {
exists(RefType D, ParameterizedInterface S |
D = this.getMethod().getDeclaringType() and
D.hasSupertype*(S) and S.getSourceDeclaration() instanceof JavaUtilCollection and
result = S.getTypeArgument(0)
)
}
}
predicate haveCommonDescendant(RefType tp1, RefType tp2) {
exists(RefType commondesc | commondesc.hasSupertype*(tp1) and commondesc.hasSupertype*(tp2))
}
from JavaUtilCollectionContainsCall juccc, Type collEltType, Type argType
where collEltType = juccc.getCollectionElementType() and argType = juccc.getArgumentType() and
not haveCommonDescendant(collEltType, argType)
select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType
改进的完善版如下:
import java
class JavaUtilCollection extends GenericInterface {
JavaUtilCollection() {
this.hasQualifiedName("java.util", "Collection")
}
}
class JavaUtilCollectionContains extends Method {
JavaUtilCollectionContains() {
this.getDeclaringType() instanceof JavaUtilCollection and
this.hasStringSignature("contains(Object)")
}
}
class JavaUtilCollectionContainsCall extends MethodAccess {
JavaUtilCollectionContainsCall() {
exists(JavaUtilCollectionContains jucc |
this.getMethod().getSourceDeclaration().overrides*(jucc)
)
}
Type getArgumentType() {
result = this.getArgument(0).getType()
}
Type getCollectionElementType() {
exists(RefType D, ParameterizedInterface S |
D = this.getMethod().getDeclaringType() and
D.hasSupertype*(S) and S.getSourceDeclaration() instanceof JavaUtilCollection and
result = S.getTypeArgument(0)
)
}
}
predicate haveCommonDescendant(RefType tp1, RefType tp2) {
exists(RefType commondesc | commondesc.hasSupertype*(tp1) and commondesc.hasSupertype*(tp2))
}
from JavaUtilCollectionContainsCall juccc, Type collEltType, Type argType
where collEltType = juccc.getCollectionElementType() and argType = juccc.getArgumentType() and
not haveCommonDescendant(collEltType, argType) and
not collEltType instanceof TypeVariable and not argType instanceof TypeVariable and
not collEltType = argType.(PrimitiveType).getBoxedType() and
not argType.hasName("<nulltype>")
select juccc, "Element type " + collEltType + " is incompatible with argument type " + argType
References
https://help.semmle.com/QL/learn-ql/java/types-class-hierarchy.html