From 7501ac83ae3ed76cf40908247ae954872289554d Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Fri, 15 Mar 2024 11:27:44 +0100 Subject: [PATCH] Handle supertypes properly, extend test --- .../passes/inference/DFGFunctionSummaries.kt | 39 ++++++++--- .../enhancements/DFGFunctionSummariesTest.kt | 67 +++++++++++++++++-- 2 files changed, 93 insertions(+), 13 deletions(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt index 74c6032b6a..31d429dab7 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/inference/DFGFunctionSummaries.kt @@ -37,6 +37,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.* import de.fraunhofer.aisec.cpg.graph.objectType import de.fraunhofer.aisec.cpg.graph.parseName import de.fraunhofer.aisec.cpg.graph.types.Type +import de.fraunhofer.aisec.cpg.isDerivedFrom import java.io.File /** @@ -111,14 +112,36 @@ class DFGFunctionSummaries { // to match to the one of the FunctionDeclaration, null indicates that we accept everything. val matchingEntries = functionToDFGEntryMap.keys.filter { - it.language == languageName && - methodName.lastPartsMatch(it.methodName) && - (it.signature == null || - functionDecl.hasSignature( - it.signature.map { signatureType -> - functionDecl.objectType(signatureType) - } - )) + // The language has to match otherwise the remaining comparison is useless + if (it.language == languageName) { + // Split the name if we have a FQN + val entryMethodName = language.parseName(it.methodName) + val entryRecord = + entryMethodName.parent?.let { + functionDecl.objectType(entryMethodName.parent) + } + methodName.lastPartsMatch( + entryMethodName.localName + ) && // The local name has to match + // If it's a method, the record declaration has to be compatible with the + // type of the entry's record declaration. We take the type of the method + // name's parent and generate a type from it. We then check if this type is + // a supertype + (entryRecord == null || + (functionDecl as? MethodDeclaration) + ?.recordDeclaration + ?.toType() + ?.isDerivedFrom(entryRecord) == true) && + // The parameter types have to match + (it.signature == null || + functionDecl.hasSignature( + it.signature.map { signatureType -> + functionDecl.objectType(signatureType) + } + )) + } else { + false + } } return if (matchingEntries.size == 1) { // Only one entry => We take this one. diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt index db16e05afc..647c5445ed 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/DFGFunctionSummariesTest.kt @@ -39,8 +39,10 @@ import de.fraunhofer.aisec.cpg.graph.functions import de.fraunhofer.aisec.cpg.graph.pointer import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference +import de.fraunhofer.aisec.cpg.graph.types.recordDeclaration import de.fraunhofer.aisec.cpg.passes.ControlFlowSensitiveDFGPass import de.fraunhofer.aisec.cpg.passes.inference.DFGFunctionSummaries +import de.fraunhofer.aisec.cpg.passes.inference.startInference import java.io.File import kotlin.test.Test import kotlin.test.assertEquals @@ -91,6 +93,30 @@ class DFGFunctionSummariesTest { literal(1, t("int")) construct("test.Object") } + val specialListType = t("test.SpecialList") + ctx?.let { + val recordDecl = + specialListType + .startInference(it) + ?.inferRecordDeclaration( + specialListType, + this@translationUnit + ) + specialListType.recordDeclaration = recordDecl + val listType = t("test.List") + recordDecl?.addSuperClass(listType) + specialListType.superTypes.add(listType) + } + + memberCall("addAll", ref("a", specialListType)) { + literal(1, t("int")) + construct("test.Object") + } + + memberCall("addAll", ref("a", t("random.Type"))) { + literal(1, t("int")) + construct("test.Object") + } returnStmt { ref("a") } } @@ -99,12 +125,43 @@ class DFGFunctionSummariesTest { } } - val methodAddAllTwoArgs = code.methods["addAll"] - assertNotNull(methodAddAllTwoArgs) - assertEquals(2, methodAddAllTwoArgs.parameters.size) + // Explicitly specified + val listAddAllTwoArgs = code.methods["test.List.addAll"] + assertNotNull(listAddAllTwoArgs) + assertEquals(2, listAddAllTwoArgs.parameters.size) + assertEquals( + setOf(listAddAllTwoArgs.receiver!!), + listAddAllTwoArgs.parameters[1].nextDFG + ) + // No flow from param0 or receiver specified => Should be empty and differ from default + // behavior + assertEquals(setOf(), listAddAllTwoArgs.parameters[0].nextDFG) + assertEquals(setOf(), listAddAllTwoArgs.prevDFG) + + // Specified by parent class' method List.addAll + val specialListAddAllTwoArgs = code.methods["test.SpecialList.addAll"] + assertNotNull(specialListAddAllTwoArgs) + assertEquals(2, specialListAddAllTwoArgs.parameters.size) + assertEquals( + setOf(specialListAddAllTwoArgs.receiver!!), + specialListAddAllTwoArgs.parameters[1].nextDFG + ) + // No flow from param0 or receiver specified => Should be empty and differ from default + // behavior + assertEquals(setOf(), specialListAddAllTwoArgs.parameters[0].nextDFG) + assertEquals(setOf(), specialListAddAllTwoArgs.prevDFG) + + // Not specified => Default behavior (param0 and param1 and receiver to method declaration). + val randomTypeAddAllTwoArgs = code.methods["random.Type.addAll"] + assertNotNull(randomTypeAddAllTwoArgs) + assertEquals(2, randomTypeAddAllTwoArgs.parameters.size) assertEquals( - setOf(methodAddAllTwoArgs.receiver!!), - methodAddAllTwoArgs.parameters[1].nextDFG + setOf( + randomTypeAddAllTwoArgs.parameters[1], + randomTypeAddAllTwoArgs.parameters[0], + randomTypeAddAllTwoArgs.receiver!! + ), + randomTypeAddAllTwoArgs.prevDFG ) }