Kotlin: implement super-method calls

If we only look at the dispatch receiver, these show up like `this` references rather than `super` references, preventing flow through super-calls. The super-interface case requires properly noting that interface methods with a body get a `default` modifier in order to avoid QL discarding the method as a possible callee.
This commit is contained in:
Chris Smowton
2022-07-26 17:03:46 +01:00
parent edc8f6f0f2
commit 5086841b46
4 changed files with 84 additions and 8 deletions

View File

@@ -888,6 +888,10 @@ open class KotlinFileExtractor(
if (shortName.nameInDB != shortName.kotlinName) { if (shortName.nameInDB != shortName.kotlinName) {
tw.writeKtFunctionOriginalNames(methodId, shortName.kotlinName) tw.writeKtFunctionOriginalNames(methodId, shortName.kotlinName)
} }
if (f.hasInterfaceParent() && f.body != null) {
addModifiers(id, "default") // The actual output class file may or may not have this modifier, depending on the -Xjvm-default setting.
}
} }
tw.writeHasLocation(id, locId) tw.writeHasLocation(id, locId)
@@ -1386,7 +1390,8 @@ open class KotlinFileExtractor(
dispatchReceiver: IrExpression?, dispatchReceiver: IrExpression?,
extensionReceiver: IrExpression?, extensionReceiver: IrExpression?,
typeArguments: List<IrType> = listOf(), typeArguments: List<IrType> = listOf(),
extractClassTypeArguments: Boolean = false) { extractClassTypeArguments: Boolean = false,
superQualifierSymbol: IrClassSymbol? = null) {
val locId = tw.getLocation(callsite) val locId = tw.getLocation(callsite)
@@ -1404,7 +1409,8 @@ open class KotlinFileExtractor(
dispatchReceiver?.let { { callId -> extractExpressionExpr(dispatchReceiver, enclosingCallable, callId, -1, enclosingStmt) } }, dispatchReceiver?.let { { callId -> extractExpressionExpr(dispatchReceiver, enclosingCallable, callId, -1, enclosingStmt) } },
extensionReceiver?.let { { argParent -> extractExpressionExpr(extensionReceiver, enclosingCallable, argParent, 0, enclosingStmt) } }, extensionReceiver?.let { { argParent -> extractExpressionExpr(extensionReceiver, enclosingCallable, argParent, 0, enclosingStmt) } },
typeArguments, typeArguments,
extractClassTypeArguments extractClassTypeArguments,
superQualifierSymbol
) )
} }
@@ -1424,7 +1430,8 @@ open class KotlinFileExtractor(
extractDispatchReceiver: ((Label<out DbExpr>) -> Unit)?, extractDispatchReceiver: ((Label<out DbExpr>) -> Unit)?,
extractExtensionReceiver: ((Label<out DbExpr>) -> Unit)?, extractExtensionReceiver: ((Label<out DbExpr>) -> Unit)?,
typeArguments: List<IrType> = listOf(), typeArguments: List<IrType> = listOf(),
extractClassTypeArguments: Boolean = false) { extractClassTypeArguments: Boolean = false,
superQualifierSymbol: IrClassSymbol? = null) {
val callTarget = syntacticCallTarget.target.realOverrideTarget val callTarget = syntacticCallTarget.target.realOverrideTarget
val id = tw.getFreshIdLabel<DbMethodaccess>() val id = tw.getFreshIdLabel<DbMethodaccess>()
@@ -1483,6 +1490,8 @@ open class KotlinFileExtractor(
if (callTarget.shouldExtractAsStatic) { if (callTarget.shouldExtractAsStatic) {
extractStaticTypeAccessQualifier(callTarget, id, locId, enclosingCallable, enclosingStmt) extractStaticTypeAccessQualifier(callTarget, id, locId, enclosingCallable, enclosingStmt)
} else if (superQualifierSymbol != null) {
extractSuperAccess(superQualifierSymbol.typeWith(), enclosingCallable, id, -1, enclosingStmt, locId)
} else if (extractDispatchReceiver != null) { } else if (extractDispatchReceiver != null) {
extractDispatchReceiver(id) extractDispatchReceiver(id)
} }
@@ -1744,7 +1753,7 @@ open class KotlinFileExtractor(
else else
listOf() listOf()
extractRawMethodAccess(syntacticCallTarget, c, callable, parent, idx, enclosingStmt, (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, c.dispatchReceiver, c.extensionReceiver, typeArgs, extractClassTypeArguments) extractRawMethodAccess(syntacticCallTarget, c, callable, parent, idx, enclosingStmt, (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, c.dispatchReceiver, c.extensionReceiver, typeArgs, extractClassTypeArguments, c.superQualifierSymbol)
} }
fun extractSpecialEnumFunction(fnName: String){ fun extractSpecialEnumFunction(fnName: String){
@@ -3066,6 +3075,17 @@ open class KotlinFileExtractor(
} }
} }
private fun extractSuperAccess(irType: IrType, callable: Label<out DbCallable>, parent: Label<out DbExprparent>, idx: Int, enclosingStmt: Label<out DbStmt>, locId: Label<out DbLocation>) =
tw.getFreshIdLabel<DbSuperaccess>().also {
val type = useType(irType)
tw.writeExprs_superaccess(it, type.javaResult.id, parent, idx)
tw.writeExprsKotlinType(it, type.kotlinResult.id)
tw.writeHasLocation(it, locId)
tw.writeCallableEnclosingExpr(it, callable)
tw.writeStatementEnclosingExpr(it, enclosingStmt)
extractTypeAccessRecursive(irType, locId, it, 0)
}
private fun extractThisAccess(e: IrGetValue, exprParent: ExprParent, callable: Label<out DbCallable>) { private fun extractThisAccess(e: IrGetValue, exprParent: ExprParent, callable: Label<out DbCallable>) {
val containingDeclaration = declarationStack.peek() val containingDeclaration = declarationStack.peek()
val locId = tw.getLocation(e) val locId = tw.getLocation(e)
@@ -4020,7 +4040,7 @@ open class KotlinFileExtractor(
/** /**
* Extracts a single wildcard type access expression with no enclosing callable and statement. * Extracts a single wildcard type access expression with no enclosing callable and statement.
*/ */
private fun extractWildcardTypeAccess(type: TypeResults, location: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int): Label<out DbExpr> { private fun extractWildcardTypeAccess(type: TypeResults, location: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int): Label<out DbExpr> {
val id = tw.getFreshIdLabel<DbWildcardtypeaccess>() val id = tw.getFreshIdLabel<DbWildcardtypeaccess>()
tw.writeExprs_wildcardtypeaccess(id, type.javaResult.id, parent, idx) tw.writeExprs_wildcardtypeaccess(id, type.javaResult.id, parent, idx)
tw.writeExprsKotlinType(id, type.kotlinResult.id) tw.writeExprsKotlinType(id, type.kotlinResult.id)
@@ -4031,7 +4051,7 @@ open class KotlinFileExtractor(
/** /**
* Extracts a single type access expression with no enclosing callable and statement. * Extracts a single type access expression with no enclosing callable and statement.
*/ */
private fun extractTypeAccess(type: TypeResults, location: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int): Label<out DbExpr> { private fun extractTypeAccess(type: TypeResults, location: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int): Label<out DbExpr> {
// TODO: elementForLocation allows us to give some sort of // TODO: elementForLocation allows us to give some sort of
// location, but a proper location for the type access will // location, but a proper location for the type access will
// require upstream changes // require upstream changes
@@ -4057,7 +4077,7 @@ open class KotlinFileExtractor(
* `extractTypeAccessRecursive` if the argument is invariant. * `extractTypeAccessRecursive` if the argument is invariant.
* No enclosing callable and statement is extracted, this is useful for type access extraction in field declarations. * No enclosing callable and statement is extracted, this is useful for type access extraction in field declarations.
*/ */
private fun extractWildcardTypeAccessRecursive(t: IrTypeArgument, location: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int) { private fun extractWildcardTypeAccessRecursive(t: IrTypeArgument, location: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int) {
val typeLabels by lazy { TypeResults(getTypeArgumentLabel(t), TypeResult(fakeKotlinType(), "TODO", "TODO")) } val typeLabels by lazy { TypeResults(getTypeArgumentLabel(t), TypeResult(fakeKotlinType(), "TODO", "TODO")) }
when (t) { when (t) {
is IrStarProjection -> extractWildcardTypeAccess(typeLabels, location, parent, idx) is IrStarProjection -> extractWildcardTypeAccess(typeLabels, location, parent, idx)
@@ -4077,7 +4097,7 @@ open class KotlinFileExtractor(
* Extracts a type access expression and its child type access expressions in case of a generic type. Nested generics are also handled. * Extracts a type access expression and its child type access expressions in case of a generic type. Nested generics are also handled.
* No enclosing callable and statement is extracted, this is useful for type access extraction in field declarations. * No enclosing callable and statement is extracted, this is useful for type access extraction in field declarations.
*/ */
private fun extractTypeAccessRecursive(t: IrType, location: Label<DbLocation>, parent: Label<out DbExprparent>, idx: Int, typeContext: TypeContext = TypeContext.OTHER): Label<out DbExpr> { private fun extractTypeAccessRecursive(t: IrType, location: Label<out DbLocation>, parent: Label<out DbExprparent>, idx: Int, typeContext: TypeContext = TypeContext.OTHER): Label<out DbExpr> {
val typeAccessId = extractTypeAccess(useType(t, typeContext), location, parent, idx) val typeAccessId = extractTypeAccess(useType(t, typeContext), location, parent, idx)
if (t is IrSimpleType) { if (t is IrSimpleType) {
t.arguments.forEachIndexed { argIdx, arg -> t.arguments.forEachIndexed { argIdx, arg ->

View File

@@ -0,0 +1,2 @@
| test.kt:31:17:31:24 | source(...) | test.kt:31:15:31:25 | f(...) |
| test.kt:32:17:32:24 | source(...) | test.kt:32:15:32:25 | g(...) |

View File

@@ -0,0 +1,36 @@
open class A {
open fun f(x: String) = x
}
interface B {
fun g(x: String) = x
}
interface C {
fun g(x: String) = x
}
class User : A(), B, C {
override fun f(x: String) = super.f(x)
override fun g(x: String) = super<B>.g(x)
fun source() = "tainted"
fun sink(s: String) { }
fun test() {
sink(this.f(source()))
sink(this.g(source()))
}
}

View File

@@ -0,0 +1,18 @@
import java
import semmle.code.java.dataflow.DataFlow
class Config extends DataFlow::Configuration {
Config() { this = "abc" }
override predicate isSource(DataFlow::Node n) {
n.asExpr().(MethodAccess).getMethod().getName() = "source"
}
override predicate isSink(DataFlow::Node n) {
n.asExpr().(Argument).getCall().getCallee().getName() = "sink"
}
}
from Config c, DataFlow::Node n1, DataFlow::Node n2
where c.hasFlow(n1, n2)
select n1, n2