diff --git a/.github/workflows/ci-dataflow.yaml b/.github/workflows/ci-dataflow.yaml index 82ba9fbf8..806267202 100644 --- a/.github/workflows/ci-dataflow.yaml +++ b/.github/workflows/ci-dataflow.yaml @@ -28,17 +28,22 @@ concurrency: jobs: check: + name: Run tests on JDK ${{ matrix.jdk }} runs-on: ubuntu-latest container: gitlab/gitlab-runner-helper:ubuntu-x86_64-latest + strategy: + fail-fast: false + matrix: + jdk: [ 11, 17 ] permissions: contents: read steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 + - name: Set up JDK ${{ matrix.jdk }} uses: actions/setup-java@v4 with: - java-version: '11' + java-version: ${{ matrix.jdk }} distribution: 'temurin' - name: Install Go-ir dependencies @@ -63,6 +68,6 @@ jobs: if: (!cancelled()) uses: actions/upload-artifact@v4 with: - name: gradle-reports-ci-dataflow + name: gradle-reports-ci-dataflow-jdk${{ matrix.jdk }} path: '**/build/reports/' retention-days: 1 diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/samples/build.gradle.kts b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/samples/build.gradle.kts index c128a5c94..0ac5d9da2 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/samples/build.gradle.kts +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/samples/build.gradle.kts @@ -2,6 +2,13 @@ plugins { java } +val recordSamplePath = "sample/alias/RecordAliasSample.java" +val supportsJava17 = JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17) + +sourceSets.main { + java.exclude(recordSamplePath) +} + tasks { withType { sourceCompatibility = JavaVersion.VERSION_1_8.toString() @@ -10,7 +17,21 @@ tasks { } } +val java17 = sourceSets.create("java17") { + java.setSrcDirs(listOf("src/main/java")) + java.include(recordSamplePath) +} + +tasks.named(java17.compileJavaTaskName) { + enabled = supportsJava17 + sourceCompatibility = JavaVersion.VERSION_17.toString() + targetCompatibility = JavaVersion.VERSION_17.toString() +} + tasks.jar { + if (supportsJava17) { + from(java17.output) + } from(sourceSets.main.get().allSource) { include("**/*.java") } diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/samples/src/main/java/sample/alias/RecordAliasSample.java b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/samples/src/main/java/sample/alias/RecordAliasSample.java new file mode 100644 index 000000000..e0aa09418 --- /dev/null +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/samples/src/main/java/sample/alias/RecordAliasSample.java @@ -0,0 +1,14 @@ +package sample.alias; + +public class RecordAliasSample { + + public record Payload(Object value) {} + + static void recordHashCodeInlined(Object src) { + Payload payload = new Payload(src); + payload.hashCode(); + sinkOneValue(payload.value()); + } + + static void sinkOneValue(Object value) {} +} diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRCallPositionResolver.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRCallPositionResolver.kt index 95a72a46a..091f04038 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRCallPositionResolver.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRCallPositionResolver.kt @@ -20,9 +20,9 @@ import org.opentaint.ir.api.jvm.JIRMethod import org.opentaint.ir.api.jvm.JIRParameter import org.opentaint.ir.api.jvm.JIRType import org.opentaint.ir.api.jvm.cfg.JIRArgument -import org.opentaint.ir.api.jvm.cfg.JIRCallExpr import org.opentaint.ir.api.jvm.cfg.JIRImmediate import org.opentaint.ir.api.jvm.cfg.JIRInstanceCallExpr +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRValue import org.opentaint.ir.api.jvm.ext.toType @@ -33,7 +33,7 @@ sealed interface CallPositionValue { } class CallPositionToJIRValueResolver( - private val callExpr: JIRCallExpr, + private val callExpr: JIRMethodCallExpr, private val returnValue: JIRImmediate? ) : PositionResolver { override fun resolve(position: Position): CallPositionValue = when (position) { @@ -48,6 +48,24 @@ class CallPositionToJIRValueResolver( } } +class JIRMethodCallPositionBaseTypeResolver( + private val callExpr: JIRMethodCallExpr +) : PositionTypeResolver { + override fun resolve(position: PositionAccess): CommonType? { + if (position !is PositionAccess.Simple) return null + + return when (val base = position.base) { + is AccessPathBase.Argument -> callExpr.args.getOrNull(base.idx)?.type + is AccessPathBase.Return -> callExpr.type + is AccessPathBase.This -> (callExpr as? JIRInstanceCallExpr)?.instance?.type + is AccessPathBase.ClassStatic, + is AccessPathBase.Constant, + is AccessPathBase.Exception, + is AccessPathBase.LocalVar -> null + } + } +} + class CalleePositionToJIRValueResolver( private val method: JIRMethod ) : PositionResolver { diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRCallResolver.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRCallResolver.kt index ab6174563..777ef8da4 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRCallResolver.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRCallResolver.kt @@ -27,6 +27,7 @@ import org.opentaint.ir.api.jvm.cfg.JIRInst import org.opentaint.ir.api.jvm.cfg.JIRInstanceCallExpr import org.opentaint.ir.api.jvm.cfg.JIRLambdaExpr import org.opentaint.ir.api.jvm.cfg.JIRLocalVar +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRNewExpr import org.opentaint.ir.api.jvm.cfg.JIRValue import org.opentaint.ir.api.jvm.cfg.JIRVirtualCallExpr @@ -88,18 +89,22 @@ class JIRCallResolver( } fun resolve(call: JIRCallExpr, location: JIRInst, context: JIRMethodAnalysisContext): List { - val method = call.method.method + if (call is JIRLambdaExpr) { + // lambda expr is an allocation site. lambda calls resolved as virtual calls + return emptyList() + } + + // A bootstrap method links an invokedynamic call site; it is not the + // method executed when the surrounding program reaches that instruction. + val methodCall = call as? JIRMethodCallExpr + ?: return listOf(MethodResolutionResult.MethodResolutionFailed) + val method = methodCall.method.method val methodIgnored = unitResolver.resolve(method) == UnknownUnit if (methodIgnored && alwaysIgnoreMethod(method)) { return listOf(MethodResolutionResult.MethodResolutionFailed) } - if (call is JIRLambdaExpr) { - // lambda expr is an allocation site. lambda calls resolved as virtual calls - return emptyList() - } - if (call !is JIRVirtualCallExpr) { if (methodIgnored) { return listOf(MethodResolutionResult.MethodResolutionFailed) diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRLanguageManager.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRLanguageManager.kt index 50a04b315..6f6cd81e7 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRLanguageManager.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRLanguageManager.kt @@ -8,6 +8,7 @@ import org.opentaint.ir.api.jvm.JIRClasspath import org.opentaint.ir.api.jvm.JIRMethod import org.opentaint.ir.api.jvm.cfg.JIRCallExpr import org.opentaint.ir.api.jvm.cfg.JIRInst +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRThrowInst import org.opentaint.ir.api.jvm.cfg.JIRValue import org.opentaint.ir.api.jvm.ext.cfg.callExpr @@ -48,7 +49,8 @@ open class JIRLanguageManager(val cp: JIRClasspath) : LanguageManager { override fun getCalleeMethod(callExpr: CommonCallExpr): JIRMethod { jIRDowncast(callExpr) - return callExpr.method.method + return (callExpr as? JIRMethodCallExpr)?.method?.method + ?: error("Dynamic call sites do not have a callee method") } override val methodContextSerializer = JIRMethodContextSerializer(cp) @@ -60,4 +62,4 @@ internal inline fun jIRDowncast(value: Any?) { returns() implies(value is T) } check(value is T) { "Downcast error: expected ${T::class}, got $value" } -} \ No newline at end of file +} diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/DSUAliasAnalysis.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/DSUAliasAnalysis.kt index 341990e74..87047b366 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/DSUAliasAnalysis.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/DSUAliasAnalysis.kt @@ -150,16 +150,18 @@ class DSUAliasAnalysis( private fun evalCall(stmt: Stmt.Call, state: State, callFrame: CallTreeNode): State { // todo: use instance alloc info - val resolvedCall = callFrame.resolveCall(stmt, methodCallResolver) + val resolvedCall = (stmt as? Stmt.MethodCall)?.let { + callFrame.resolveCall(it, methodCallResolver) + } if (resolvedCall != null) { val result = evalCall(stmt, state, callFrame, resolvedCall) if (result != null) return result } var resultState = state - if (stmt.lValue != null) { + stmt.lValue?.let { lValue -> val info = aliasSetFromInfo(CallReturn(stmt, callFrame.ctx)) - resultState = resultState.removeOldAndMergeWith(stmt.lValue.aliasInfo().index(), info) + resultState = resultState.removeOldAndMergeWith(lValue.aliasInfo().index(), info) } if (!stmt.cantMutateAliasedHeap()) { @@ -172,7 +174,8 @@ class DSUAliasAnalysis( resultState = resultState.invalidateOuterHeapAliases(argAliases) } - val externalModel = methodCallResolver.externalCallModel(stmt.method) + val method = (stmt as? Stmt.MethodCall)?.method + val externalModel = method?.let(methodCallResolver::externalCallModel).orEmpty() resultState = externalModel.fold(resultState) { s, model -> model.evalExternalCallModel(stmt, s) } @@ -566,6 +569,7 @@ class DSUAliasAnalysis( private fun Stmt.Call.cantMutateAliasedHeap(): Boolean { if (args.any { it !is SimpleValue.Primitive }) return false + val method = (this as? Stmt.MethodCall)?.method ?: return false return method.isStatic || method.isConstructor } } diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/InterProcCallNode.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/InterProcCallNode.kt index 35a625319..0674246f0 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/InterProcCallNode.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/InterProcCallNode.kt @@ -15,7 +15,7 @@ import org.opentaint.jvm.graph.JApplicationGraph import java.util.BitSet interface CallResolver { - fun resolveMethodCall(callStmt: Stmt.Call, level: Int): List? + fun resolveMethodCall(callStmt: Stmt.MethodCall, level: Int): List? fun buildMethodGraph(method: JIRMethod): JIRInstGraph? fun externalCallModel(method: JIRMethod): List } @@ -28,7 +28,7 @@ abstract class JirCallResolver( ): CallResolver { abstract fun buildMethodJig(entryPoint: JIRInst): JIRInstGraph - override fun resolveMethodCall(callStmt: Stmt.Call, level: Int): List? { + override fun resolveMethodCall(callStmt: Stmt.MethodCall, level: Int): List? { if (level >= params.aliasAnalysisInterProcCallDepth) return null val methods = callResolver.allKnownOverridesOrNull(callStmt.method) @@ -56,7 +56,7 @@ class CallTreeNode(val ctx: ContextInfo, val instEvalCtx: InstEvalContext) { private val emptyCalls = BitSet() private val calls = Int2ObjectOpenHashMap() - fun resolveCall(stmt: Stmt.Call, callResolver: CallResolver): Map? { + fun resolveCall(stmt: Stmt.MethodCall, callResolver: CallResolver): Map? { if (emptyCalls.get(stmt.originalIdx)) return ResolvedCall.empty.methods return calls.getOrPut(stmt.originalIdx) { @@ -87,7 +87,7 @@ private class NestedCallInstEvalCtx(val call: Stmt.Call, val ctx: ContextInfo) : override fun createLocal(idx: Int): Local = Local(idx, ctx) } -private fun resolveCallNoCache(stmt: Stmt.Call, ctx: ContextInfo, callResolver: CallResolver): ResolvedCall { +private fun resolveCallNoCache(stmt: Stmt.MethodCall, ctx: ContextInfo, callResolver: CallResolver): ResolvedCall { val methods = callResolver.resolveMethodCall(stmt, ctx.level) ?: return ResolvedCall.empty @@ -106,5 +106,5 @@ private fun resolveCallNoCache(stmt: Stmt.Call, ctx: ContextInfo, callResolver: return ResolvedCall(resolvedCall) } -private fun mkContextId(stmt: Stmt.Call, methodIdx: Int): Int = +private fun mkContextId(stmt: Stmt.MethodCall, methodIdx: Int): Int = (stmt.originalIdx * 1000) + methodIdx diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/JIRDSUAABuilder.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/JIRDSUAABuilder.kt index 5fc25ffb9..51826ac86 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/JIRDSUAABuilder.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/JIRDSUAABuilder.kt @@ -18,6 +18,7 @@ import org.opentaint.ir.api.jvm.cfg.JIRImmediate import org.opentaint.ir.api.jvm.cfg.JIRInst import org.opentaint.ir.api.jvm.cfg.JIRInstanceCallExpr import org.opentaint.ir.api.jvm.cfg.JIRLocalVar +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRNewArrayExpr import org.opentaint.ir.api.jvm.cfg.JIRNewExpr import org.opentaint.ir.api.jvm.cfg.JIRRef @@ -91,7 +92,26 @@ sealed interface Stmt : Comparable { sealed interface NoCall: Stmt - data class Call(val method: JIRMethod, val lValue: RefValue.Local?, val instance: Value?, val args: List, override val originalIdx: Int) : Stmt + sealed interface Call : Stmt { + val lValue: RefValue.Local? + val instance: Value? + val args: List + } + + data class MethodCall( + val method: JIRMethod, + override val lValue: RefValue.Local?, + override val instance: Value?, + override val args: List, + override val originalIdx: Int + ) : Call + + data class OpaqueCall( + override val lValue: RefValue.Local?, + override val instance: Value?, + override val args: List, + override val originalIdx: Int + ) : Call data class Copy(val lValue: RefValue.Local, val rValue: RefValue, override val originalIdx: Int): NoCall data class Assign(val lValue: RefValue.Local, val expr: Expr, override val originalIdx: Int) : NoCall @@ -192,15 +212,20 @@ private fun InstEvalContext.evalCall( ): Stmt? { val lhs = (lValue as? JIRLocalVar)?.let { createLocal(it.index) } - if (expr.method.method.isPrimitiveBoxAllocMethod()) { + val method = (expr as? JIRMethodCallExpr)?.method?.method + if (method?.isPrimitiveBoxAllocMethod() == true) { if (lhs == null) return null return Stmt.Assign(lhs, Expr.Alloc(loc), loc.location.index) } val args = expr.args.map { evalSimpleValue(it as JIRImmediate, loc) } val instance = (expr as? JIRInstanceCallExpr)?.instance?.let { evalSimpleValue(it as JIRImmediate, loc) } - val stmt = Stmt.Call(expr.method.method, lhs, instance, args, loc.location.index) - return stmt + return when (expr) { + is JIRMethodCallExpr -> Stmt.MethodCall( + expr.method.method, lhs, instance, args, loc.location.index + ) + else -> Stmt.OpaqueCall(lhs, instance, args, loc.location.index) + } } private fun InstEvalContext.evalExpr(expr: JIRExpr, inst: JIRInst): ExprOrValue = when (expr) { diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRAnalysisManager.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRAnalysisManager.kt index adc8d8297..f5c542184 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRAnalysisManager.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRAnalysisManager.kt @@ -36,6 +36,7 @@ import org.opentaint.dataflow.jvm.ap.ifds.jIRDowncast import org.opentaint.dataflow.jvm.ap.ifds.taint.JIRTaintAnalysisContext import org.opentaint.dataflow.jvm.ap.ifds.taint.TaintRulesProvider import org.opentaint.dataflow.jvm.ap.ifds.trace.JIRMethodCallPrecondition +import org.opentaint.dataflow.jvm.ap.ifds.trace.JIRNonMethodCallPrecondition import org.opentaint.dataflow.jvm.ap.ifds.trace.JIRMethodSequentPrecondition import org.opentaint.dataflow.jvm.ap.ifds.trace.JIRMethodStartPrecondition import org.opentaint.dataflow.jvm.ifds.JIRUnitResolver @@ -48,6 +49,7 @@ import org.opentaint.ir.api.jvm.JIRClasspath import org.opentaint.ir.api.jvm.cfg.JIRCallExpr import org.opentaint.ir.api.jvm.cfg.JIRImmediate import org.opentaint.ir.api.jvm.cfg.JIRInst +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.jvm.graph.JApplicationGraph import org.opentaint.util.analysis.ApplicationGraph import java.util.concurrent.ConcurrentHashMap @@ -211,13 +213,11 @@ class JIRAnalysisManager( jIRDowncast(analysisContext) return analysisContext.cachedCallFF(statement.location.index) { + val methodCall = callExpr as? JIRMethodCallExpr + ?: return@cachedCallFF JIRNonMethodCallFlowFunction(returnValue, callExpr) + JIRMethodCallFlowFunction( - apManager, - analysisContext, - returnValue, - callExpr, - statement, - generateTrace + apManager, analysisContext, returnValue, methodCall, statement, generateTrace ) } } @@ -259,13 +259,8 @@ class JIRAnalysisManager( jIRDowncast(statement) jIRDowncast(analysisContext) - return JIRMethodCallPrecondition( - apManager, - analysisContext, - returnValue, - callExpr, - statement - ) + val methodCall = callExpr as? JIRMethodCallExpr ?: return JIRNonMethodCallPrecondition + return JIRMethodCallPrecondition(apManager, analysisContext, returnValue, methodCall, statement) } override fun getEdgePostProcessor( @@ -327,4 +322,4 @@ class JIRAnalysisManager( val percentValue = current.toDouble() / total return String.format("%.2f", percentValue * 100) + "%" } -} \ No newline at end of file +} diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodAnalysisContext.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodAnalysisContext.kt index d9837eac0..6fb9cdb8e 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodAnalysisContext.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodAnalysisContext.kt @@ -6,6 +6,7 @@ import org.opentaint.dataflow.ap.ifds.TaintAnalysisManager.Phase import org.opentaint.dataflow.ap.ifds.TaintMarkAccessor import org.opentaint.dataflow.ap.ifds.analysis.MethodAnalysisContext import org.opentaint.dataflow.ap.ifds.analysis.MethodCallFactMapper +import org.opentaint.dataflow.ap.ifds.analysis.MethodCallFlowFunction import org.opentaint.dataflow.jvm.ap.ifds.JIRFactTypeChecker import org.opentaint.dataflow.jvm.ap.ifds.JIRLambdaTracker import org.opentaint.dataflow.jvm.ap.ifds.JIRLocalAliasAnalysis @@ -38,16 +39,16 @@ class JIRMethodAnalysisContext( val lambdaCallResolution = Int2ObjectOpenHashMap() - fun cachedCallFF(stmtIdx: Int, body: () -> JIRMethodCallFlowFunction): JIRMethodCallFlowFunction = + fun cachedCallFF(stmtIdx: Int, body: () -> MethodCallFlowFunction): MethodCallFlowFunction = getCallFFCache().computeIfAbsent(stmtIdx) { body() } fun cachedCallSH(stmtIdx: Int, body: () -> JIRMethodCallSummaryHandler): JIRMethodCallSummaryHandler = getCallSHCache().computeIfAbsent(stmtIdx) { body() } - private var callFFCache: Reference>? = null - private fun getCallFFCache(): Int2ObjectOpenHashMap { + private var callFFCache: Reference>? = null + private fun getCallFFCache(): Int2ObjectOpenHashMap { callFFCache?.get()?.let { return it } - return int2ObjectMap().also { + return int2ObjectMap().also { callFFCache = refManager.createRef(it) } } diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt index 81c6b6ee0..34c6b4883 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt @@ -17,7 +17,7 @@ import org.opentaint.dataflow.configuration.jvm.serialized.UserDefinedRuleInfo import org.opentaint.dataflow.jvm.ap.ifds.JIRCallResolver import org.opentaint.dataflow.jvm.ap.ifds.JIRMethodCallFactMapper import org.opentaint.dataflow.jvm.ap.ifds.JIRMethodCallFactMapper.factIsRelevantToMethodCall -import org.opentaint.dataflow.jvm.ap.ifds.JIRMethodPositionBaseTypeResolver +import org.opentaint.dataflow.jvm.ap.ifds.JIRMethodCallPositionBaseTypeResolver import org.opentaint.dataflow.jvm.ap.ifds.TaintConfigUtils.applyCleaner import org.opentaint.dataflow.jvm.ap.ifds.TaintConfigUtils.applyPassThrough import org.opentaint.dataflow.jvm.ap.ifds.taint.JIRMethodCallTaintUtil @@ -29,16 +29,16 @@ import org.opentaint.dataflow.taint.FinalFactReader import org.opentaint.dataflow.taint.TaintFactAwareConditionEvaluator import org.opentaint.dataflow.taint.TaintPassActionEvaluator import org.opentaint.ir.api.jvm.JIRMethod -import org.opentaint.ir.api.jvm.cfg.JIRCallExpr import org.opentaint.ir.api.jvm.cfg.JIRImmediate import org.opentaint.ir.api.jvm.cfg.JIRInst +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.util.onSome class JIRMethodCallFlowFunction( private val apManager: ApManager, private val analysisContext: JIRMethodAnalysisContext, private val returnValue: JIRImmediate?, - private val callExpr: JIRCallExpr, + private val callExpr: JIRMethodCallExpr, private val statement: JIRInst, private val generateTrace: Boolean, ): MethodCallFlowFunction.Default { @@ -49,7 +49,7 @@ class JIRMethodCallFlowFunction( } val typeResolver by lazy { - JIRMethodPositionBaseTypeResolver(callExpr.method.method) + JIRMethodCallPositionBaseTypeResolver(callExpr) } override fun propagateZeroToZero() = buildSet { @@ -223,7 +223,6 @@ class JIRMethodCallFlowFunction( ): List> { val sinkRules = taintCtx.sinkRulesForCallStatement(statement, callExpr, returnValue, factReader?.factAp) if (sinkRules.isEmpty()) return emptyList() - val taintUtil = JIRMethodCallTaintUtil(apManager, statement, callExpr, analysisContext, generateTrace) taintUtil.applySinkRules( sinkRules, factReader, markAfterAnyFieldResolver @@ -241,7 +240,6 @@ class JIRMethodCallFlowFunction( ) { val sourceRules = taintCtx.sourceRulesForCallStatement(statement, callExpr, returnValue, factReader?.factAp) if (sourceRules.isEmpty()) return - val taintUtil = JIRMethodCallTaintUtil(apManager, statement, callExpr, analysisContext, generateTrace) taintUtil.applySourceRules( sourceRules, initialFacts, factReader, exclusion, diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallRuleBasedSummaryRewriter.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallRuleBasedSummaryRewriter.kt index 3aec7b58e..b7c9dee67 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallRuleBasedSummaryRewriter.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallRuleBasedSummaryRewriter.kt @@ -10,7 +10,7 @@ import org.opentaint.dataflow.configuration.jvm.TaintMark import org.opentaint.dataflow.configuration.jvm.serialized.UserDefinedRuleInfo import org.opentaint.dataflow.jvm.ap.ifds.CallPositionToJIRValueResolver import org.opentaint.dataflow.jvm.ap.ifds.JIRMarkAwareConditionRewriter -import org.opentaint.dataflow.jvm.ap.ifds.JIRMethodPositionBaseTypeResolver +import org.opentaint.dataflow.jvm.ap.ifds.JIRMethodCallPositionBaseTypeResolver import org.opentaint.dataflow.jvm.ap.ifds.TaintConfigUtils.applyCleanerActions import org.opentaint.dataflow.jvm.ap.ifds.taint.JIRTaintCleanActionEvaluator import org.opentaint.dataflow.jvm.ap.ifds.taint.resolveBaseAp @@ -19,6 +19,7 @@ import org.opentaint.dataflow.taint.FinalFactReader import org.opentaint.ir.api.jvm.cfg.JIRAssignInst import org.opentaint.ir.api.jvm.cfg.JIRImmediate import org.opentaint.ir.api.jvm.cfg.JIRInst +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.ext.cfg.callExpr class JIRMethodCallRuleBasedSummaryRewriter( @@ -29,7 +30,8 @@ class JIRMethodCallRuleBasedSummaryRewriter( private val taintCtx get() = analysisContext.taint private val callExpr by lazy { - statement.callExpr ?: error("Call summary handler at statement without method call") + statement.callExpr as? JIRMethodCallExpr + ?: error("Method-call summary handler at a non-method call site") } private val conditionRewriter by lazy { @@ -42,7 +44,7 @@ class JIRMethodCallRuleBasedSummaryRewriter( } private val typeResolver by lazy { - JIRMethodPositionBaseTypeResolver(callExpr.method.method) + JIRMethodCallPositionBaseTypeResolver(callExpr) } private data class UserRuleDefinedAction( diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRNonMethodCallFlowFunction.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRNonMethodCallFlowFunction.kt new file mode 100644 index 000000000..9a4cf625c --- /dev/null +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRNonMethodCallFlowFunction.kt @@ -0,0 +1,84 @@ +package org.opentaint.dataflow.jvm.ap.ifds.analysis + +import org.opentaint.dataflow.ap.ifds.AccessPathBase +import org.opentaint.dataflow.ap.ifds.access.FactAp +import org.opentaint.dataflow.ap.ifds.access.FinalFactAp +import org.opentaint.dataflow.ap.ifds.access.InitialFactAp +import org.opentaint.dataflow.ap.ifds.analysis.MethodCallFlowFunction +import org.opentaint.dataflow.ap.ifds.analysis.MethodCallFlowFunction.CallToReturnFFact +import org.opentaint.dataflow.ap.ifds.analysis.MethodCallFlowFunction.CallToReturnNonDistributiveFact +import org.opentaint.dataflow.ap.ifds.analysis.MethodCallFlowFunction.CallToReturnZFact +import org.opentaint.dataflow.ap.ifds.analysis.MethodCallFlowFunction.CallToReturnZeroFact +import org.opentaint.dataflow.ap.ifds.analysis.MethodCallFlowFunction.TraceInfo +import org.opentaint.dataflow.ap.ifds.analysis.MethodCallFlowFunction.Unchanged +import org.opentaint.dataflow.jvm.ap.ifds.JIRMethodCallFactMapper +import org.opentaint.ir.api.jvm.cfg.JIRCallExpr +import org.opentaint.ir.api.jvm.cfg.JIRImmediate + +/** + * Call-to-return flow for call-like expressions that do not invoke a method. + * + * Lambda expressions construct an object. An invokedynamic bootstrap links its call site instead + * of being invoked by the program. Neither expression has a callee to analyze or match against + * method rules, summaries, and approximations. + */ +class JIRNonMethodCallFlowFunction( + private val returnValue: JIRImmediate?, + private val callExpr: JIRCallExpr, +) : MethodCallFlowFunction { + override fun propagateZeroToZero(): Set = + setOf(CallToReturnZeroFact) + + override fun propagateZeroToFact(currentFactAp: FinalFactAp): Set = + if (isRelevant(currentFactAp)) { + setOf(CallToReturnZFact(currentFactAp, TraceInfo.Flow)) + } else { + setOf(Unchanged) + } + + override fun propagateFactToFact( + initialFactAp: InitialFactAp, + currentFactAp: FinalFactAp, + ): Set = + if (isRelevant(currentFactAp)) { + setOf(CallToReturnFFact(initialFactAp, currentFactAp, TraceInfo.Flow)) + } else { + setOf(Unchanged) + } + + override fun propagateNDFactToFact( + initialFacts: Set, + currentFactAp: FinalFactAp, + ): Set = + if (isRelevant(currentFactAp)) { + setOf(CallToReturnNonDistributiveFact(initialFacts, currentFactAp, TraceInfo.Flow)) + } else { + setOf(Unchanged) + } + + override fun propagateZeroToZeroResolutionFailure(): Set = + setOf(CallToReturnZeroFact) + + override fun propagateZeroToFactResolutionFailure( + currentFactAp: FinalFactAp, + startFactBase: AccessPathBase, + ): Set = + setOf(CallToReturnZFact(currentFactAp, TraceInfo.Flow)) + + override fun propagateFactToFactResolutionFailure( + initialFactAp: InitialFactAp, + currentFactAp: FinalFactAp, + startFactBase: AccessPathBase, + ): Set = + setOf(CallToReturnFFact(initialFactAp, currentFactAp, TraceInfo.Flow)) + + override fun propagateNDFactToFactResolutionFailure( + initialFacts: Set, + currentFactAp: FinalFactAp, + startFactBase: AccessPathBase, + ): Set = + setOf(CallToReturnNonDistributiveFact(initialFacts, currentFactAp, TraceInfo.Flow)) + + private fun isRelevant(fact: FactAp): Boolean = + JIRMethodCallFactMapper.factIsRelevantToMethodCall(returnValue, callExpr, fact.base) +} diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRBasicAtomEvaluator.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRBasicAtomEvaluator.kt index 77f1b240a..70952dd2e 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRBasicAtomEvaluator.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRBasicAtomEvaluator.kt @@ -35,12 +35,12 @@ import org.opentaint.ir.api.jvm.JIRClassType import org.opentaint.ir.api.jvm.JIRRefType import org.opentaint.ir.api.jvm.JIRType import org.opentaint.ir.api.jvm.cfg.JIRBool -import org.opentaint.ir.api.jvm.cfg.JIRCallExpr import org.opentaint.ir.api.jvm.cfg.JIRConstant import org.opentaint.ir.api.jvm.cfg.JIRFieldRef import org.opentaint.ir.api.jvm.cfg.JIRInst import org.opentaint.ir.api.jvm.cfg.JIRInt import org.opentaint.ir.api.jvm.cfg.JIRLocalVar +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRNullConstant import org.opentaint.ir.api.jvm.cfg.JIRStringConstant import org.opentaint.ir.api.jvm.cfg.JIRValue @@ -226,14 +226,14 @@ class JIRBasicAtomEvaluator( } .mapNotNull { it.args.getOrNull(0) as? JIRConstant } - private fun List.findAllocCalls(): List { + private fun List.findAllocCalls(): List { val allocs = filterIsInstance() if (allocs.isEmpty()) return emptyList() val instList = (statement as JIRInst).location.method.instList return allocs .mapNotNull { instList.getOrNull(it.allocInst) } - .mapNotNull { it.callExpr } + .mapNotNull { it.callExpr as? JIRMethodCallExpr } } private fun matches(value: JIRValue, pattern: Regex, matchArrayValue: Boolean): Boolean { diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRMethodCallTaintUtil.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRMethodCallTaintUtil.kt index 393db42f4..f2fda92be 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRMethodCallTaintUtil.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRMethodCallTaintUtil.kt @@ -24,12 +24,13 @@ import org.opentaint.dataflow.taint.TaintSourceActionEvaluator import org.opentaint.dataflow.taint.TaintUtil import org.opentaint.ir.api.jvm.cfg.JIRCallExpr import org.opentaint.ir.api.jvm.cfg.JIRInst +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.util.onSome class JIRMethodCallTaintUtil( apManager: ApManager, val statement: JIRInst, - val callExpr: JIRCallExpr, + val callExpr: JIRMethodCallExpr, val analysisContext: JIRMethodAnalysisContext, val generateTrace: Boolean, ) : TaintUtil(apManager) { diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRTaintAnalysisContext.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRTaintAnalysisContext.kt index d5311836c..553d6b011 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRTaintAnalysisContext.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/taint/JIRTaintAnalysisContext.kt @@ -24,10 +24,9 @@ import org.opentaint.dataflow.jvm.ap.ifds.JIRMarkAwareConditionRewriter import org.opentaint.dataflow.jvm.ap.ifds.analysis.JIRMethodAnalysisContext import org.opentaint.dataflow.taint.RuleConditionRewriter import org.opentaint.ir.api.jvm.JIRField -import org.opentaint.ir.api.jvm.JIRMethod -import org.opentaint.ir.api.jvm.cfg.JIRCallExpr import org.opentaint.ir.api.jvm.cfg.JIRImmediate import org.opentaint.ir.api.jvm.cfg.JIRInst +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.ext.cfg.callExpr class JIRTaintAnalysisContext( @@ -46,67 +45,80 @@ class JIRTaintAnalysisContext( taintSinkTracker.reset() } - private fun JIRInst.callExpr(): JIRCallExpr = callExpr ?: error("Non-call statement") - private fun JIRCallExpr.calleeMethod(): JIRMethod = method.method - private fun JIRInst.calleeMethod(): JIRMethod = callExpr().calleeMethod() + private fun JIRInst.methodCallExprOrNull(): JIRMethodCallExpr? = + callExpr as? JIRMethodCallExpr fun allRelevantSourceRulesForCallStatement(statement: JIRInst): Iterable { if (analysisContext.phase is Phase.Prescan) return emptyList() - return taintConfig.sourceRulesForMethod(statement.calleeMethod(), statement, fact = null, allRelevant = true) + val method = statement.methodCallExprOrNull()?.method?.method ?: return emptyList() + return taintConfig.sourceRulesForMethod(method, statement, fact = null, allRelevant = true) } fun allRelevantCleanRulesForCallStatement(statement: JIRInst): Iterable { if (analysisContext.phase is Phase.Prescan) return emptyList() - return taintConfig.cleanerRulesForMethod(statement.calleeMethod(), statement, fact = null, allRelevant = true) + val method = statement.methodCallExprOrNull()?.method?.method ?: return emptyList() + return taintConfig.cleanerRulesForMethod(method, statement, fact = null, allRelevant = true) } fun sourceRulesForCallStatement( statement: JIRInst, - callExpr: JIRCallExpr, + callExpr: JIRMethodCallExpr, returnValue: JIRImmediate?, fact: FinalFactAp? - ) = prepareCallStatementRules( - taintConfig.sourceRulesForMethod(statement.calleeMethod(), statement, fact, allRelevant = false), - TaintMethodSource::condition, - statement, callExpr, returnValue - ) + ): List> { + val method = callExpr.method.method + return prepareCallStatementRules( + taintConfig.sourceRulesForMethod(method, statement, fact, allRelevant = false), + TaintMethodSource::condition, + statement, callExpr, returnValue + ) + } fun sinkRulesForCallStatement( statement: JIRInst, - callExpr: JIRCallExpr, + callExpr: JIRMethodCallExpr, returnValue: JIRImmediate?, fact: FinalFactAp? - ) = prepareCallStatementRules( - taintConfig.sinkRulesForMethod(statement.calleeMethod(), statement, fact, allRelevant = false), - TaintMethodSink::condition, - statement, callExpr, returnValue - ) + ): List> { + val method = callExpr.method.method + return prepareCallStatementRules( + taintConfig.sinkRulesForMethod(method, statement, fact, allRelevant = false), + TaintMethodSink::condition, + statement, callExpr, returnValue + ) + } fun cleanRulesForCallStatement( statement: JIRInst, - callExpr: JIRCallExpr, + callExpr: JIRMethodCallExpr, returnValue: JIRImmediate?, fact: FinalFactAp? - ) = prepareCallStatementRules( - taintConfig.cleanerRulesForMethod(statement.calleeMethod(), statement, fact, allRelevant = false), - TaintCleaner::condition, - statement, callExpr, returnValue - ) + ): List> { + val method = callExpr.method.method + return prepareCallStatementRules( + taintConfig.cleanerRulesForMethod(method, statement, fact, allRelevant = false), + TaintCleaner::condition, + statement, callExpr, returnValue + ) + } fun passRulesForCallStatement( statement: JIRInst, - callExpr: JIRCallExpr, + callExpr: JIRMethodCallExpr, returnValue: JIRImmediate?, fact: FinalFactAp? - ) = prepareCallStatementRules( - taintConfig.passTroughRulesForMethod(statement.calleeMethod(), statement, fact, allRelevant = false), - TaintPassThrough::condition, - statement, callExpr, returnValue - ) + ): List> { + val method = callExpr.method.method + return prepareCallStatementRules( + taintConfig.passTroughRulesForMethod(method, statement, fact, allRelevant = false), + TaintPassThrough::condition, + statement, callExpr, returnValue + ) + } private inline fun prepareCallStatementRules( rules: Iterable, cond: T.() -> Condition, - statement: JIRInst, callExpr: JIRCallExpr, returnValue: JIRImmediate?, + statement: JIRInst, callExpr: JIRMethodCallExpr, returnValue: JIRImmediate?, ): List> { val conditionRewriter = JIRMarkAwareConditionRewriter( CallPositionToJIRValueResolver(callExpr, returnValue), diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt index 365e722c1..b1eda3d6c 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt @@ -28,15 +28,15 @@ import org.opentaint.dataflow.taint.TaintSourceActionPreconditionEvaluator import org.opentaint.dataflow.taint.evaluatePassRulePrecondition import org.opentaint.dataflow.taint.evaluateSourceRulePrecondition import org.opentaint.ir.api.common.cfg.CommonInst -import org.opentaint.ir.api.jvm.cfg.JIRCallExpr import org.opentaint.ir.api.jvm.cfg.JIRImmediate import org.opentaint.ir.api.jvm.cfg.JIRInst +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr class JIRMethodCallPrecondition( override val apManager: ApManager, private val analysisContext: JIRMethodAnalysisContext, private val returnValue: JIRImmediate?, - private val callExpr: JIRCallExpr, + private val callExpr: JIRMethodCallExpr, private val statement: JIRInst, ) : MethodCallPrecondition.Default { private val methodCallFactMapper: MethodCallFactMapper get() = analysisContext.methodCallFactMapper diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRNonMethodCallPrecondition.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRNonMethodCallPrecondition.kt new file mode 100644 index 000000000..7bdb4aa67 --- /dev/null +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRNonMethodCallPrecondition.kt @@ -0,0 +1,29 @@ +package org.opentaint.dataflow.jvm.ap.ifds.trace + +import org.opentaint.dataflow.ap.ifds.AccessPathBase +import org.opentaint.dataflow.ap.ifds.MethodAnalyzerEdges +import org.opentaint.dataflow.ap.ifds.access.InitialFactAp +import org.opentaint.dataflow.ap.ifds.trace.MethodCallPrecondition +import org.opentaint.dataflow.ap.ifds.trace.MethodCallPrecondition.CallPrecondition +import org.opentaint.dataflow.ap.ifds.trace.MethodCallPrecondition.CallPreconditionFact +import org.opentaint.dataflow.ap.ifds.trace.MethodCallPrecondition.PassRuleConditionFacts +import org.opentaint.dataflow.ap.ifds.trace.TaintRulePrecondition.PassRuleCondition + +/** + * Non-method call sites have no callee summaries or method rules to reconstruct. + */ +object JIRNonMethodCallPrecondition : MethodCallPrecondition { + override fun factPrecondition(fact: InitialFactAp): List = + listOf(CallPrecondition.Unchanged) + + override fun factPreconditionResolutionFailure( + fact: InitialFactAp, + startFactBase: AccessPathBase, + ): List = + listOf(CallPreconditionFact.UnresolvedCallSkip) + + override fun resolvePassRuleCondition( + precondition: PassRuleCondition, + edges: MethodAnalyzerEdges, + ): List = emptyList() +} diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/util/JirMethodExt.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/util/JirMethodExt.kt index 2e5b82f5d..a2cc36575 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/util/JirMethodExt.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/util/JirMethodExt.kt @@ -1,7 +1,7 @@ package org.opentaint.dataflow.jvm.util import org.opentaint.ir.api.jvm.JIRMethod -import org.opentaint.ir.api.jvm.cfg.JIRCallExpr +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRThis import org.opentaint.ir.api.jvm.ext.toType @@ -9,5 +9,5 @@ import org.opentaint.ir.api.jvm.ext.toType val JIRMethod.thisInstance: JIRThis get() = JIRThis(enclosingClass.toType()) -val JIRCallExpr.callee: JIRMethod +val JIRMethodCallExpr.callee: JIRMethod get() = method.method diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/util/JirVararg.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/util/JirVararg.kt index 3106028d7..7b87159f3 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/util/JirVararg.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/util/JirVararg.kt @@ -2,12 +2,12 @@ package org.opentaint.dataflow.jvm.util import org.objectweb.asm.Opcodes import org.opentaint.ir.api.jvm.JIRMethod -import org.opentaint.ir.api.jvm.cfg.JIRCallExpr +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr fun JIRMethod.isVararg(): Boolean = access and Opcodes.ACC_VARARGS != 0 -fun JIRCallExpr.isVararg(): Boolean = +fun JIRMethodCallExpr.isVararg(): Boolean = method.method.isVararg() fun JIRMethod.varargParamIdx(): Int = diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/test/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/AliasSampleTest.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/test/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/AliasSampleTest.kt index e713d55a4..4047accf9 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/test/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/AliasSampleTest.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/test/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/AliasSampleTest.kt @@ -1,6 +1,8 @@ package org.opentaint.dataflow.jvm.ap.ifds.alias import kotlinx.coroutines.runBlocking +import org.junit.jupiter.api.Assertions.assertDoesNotThrow +import org.junit.jupiter.api.Assumptions.assumeTrue import org.junit.jupiter.api.TestInstance import org.opentaint.dataflow.ap.ifds.AccessPathBase import org.opentaint.dataflow.ap.ifds.AccessPathBase.Companion.Argument @@ -36,12 +38,16 @@ import org.opentaint.ir.api.jvm.JIRField import org.opentaint.ir.api.jvm.JIRMethod import org.opentaint.ir.api.jvm.RegisteredLocation import org.opentaint.ir.api.jvm.cfg.JIRCallInst +import org.opentaint.ir.api.jvm.cfg.JIRDynamicCallExpr +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRInst import org.opentaint.ir.api.jvm.cfg.JIRLocalVar import org.opentaint.ir.api.jvm.cfg.JIRValue import org.opentaint.ir.impl.features.usagesExt +import org.opentaint.ir.api.jvm.ext.cfg.callExpr import org.opentaint.jvm.graph.JApplicationGraphImpl import kotlin.test.Test +import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue @@ -577,6 +583,34 @@ class AliasSampleTest : BasicTestUtils() { assertTrue { aa.sinkArgApAliases(sink).isNotEmpty() } } + @Test + fun `record invokedynamic is not analyzed as a call to its bootstrap method`() { + assumeTrue(javaFeatureVersion() >= 17, "Record fixture requires Java 17") + + val method = findMethod(RECORD_SAMPLE, "recordHashCodeInlined") + val recordHashCode = cp.findClassOrNull("$RECORD_SAMPLE\$Payload") + ?.declaredMethods + ?.single { it.name == "hashCode" } + ?: error("Record hashCode method not found") + val dynamicInst = recordHashCode.instList.single { it.callExpr is JIRDynamicCallExpr } + val dynamicCall = dynamicInst.callExpr as JIRDynamicCallExpr + + assertEquals("bootstrap", dynamicCall.bootstrapMethod.name) + assertEquals(dynamicCall.callSiteReturnType, dynamicCall.type) + assertFalse { dynamicCall.type == dynamicCall.bootstrapMethod.returnType } + + val usages = runBlocking { cp.usagesExt() } + val graph = JApplicationGraphImpl(cp, usages) + assertTrue { graph.callees(dynamicInst).none() } + + assertDoesNotThrow { + val aa = aaForMethodKnowingObjectMethods(method, depth = 2) + val sink = method.findSinkCall("sinkOneValue") + val aliases = aa.sinkArgApAliases(sink) + assertTrue { aliases.any { it.base == Argument(0) } } + } + } + private fun aaForMethod( method: JIRMethod, params: JIRLocalAliasAnalysis.Params = JIRLocalAliasAnalysis.Params() @@ -592,11 +626,37 @@ class AliasSampleTest : BasicTestUtils() { return JIRLocalAliasAnalysis(ep, graph, callResolver, noRules, localReachability, cancellation, manager, params) } + private fun aaForMethodKnowingObjectMethods(method: JIRMethod, depth: Int): JIRLocalAliasAnalysis { + val ep = method.instList.first() + val usages = runBlocking { cp.usagesExt() } + val graph = JApplicationGraphImpl(cp, usages) + + // Make the bootstrap implementation available. The test must still not + // enter it: invokedynamic executes its linked call site, not the bootstrap. + val sampleLoc = method.enclosingClass.declaration.location + val objectMethodsLoc = + cp.findClassOrNull("java.lang.runtime.ObjectMethods")?.declaration?.location + val knownLocs = setOfNotNull(sampleLoc, objectMethodsLoc) + + val callResolver = JIRCallResolver(cp, MultiLocationUnit(knownLocs)) + val localReachability = JIRLocalVariableReachability(method, graph, manager) + val cancellation = Cancellation().also { it.activate() } + + return JIRLocalAliasAnalysis( + ep, graph, callResolver, noRules, localReachability, cancellation, manager, interProcParams(depth) + ) + } + private fun interProcParams(depth: Int) = JIRLocalAliasAnalysis.Params(useAliasAnalysis = true, aliasAnalysisInterProcCallDepth = depth) + private fun javaFeatureVersion(): Int = + System.getProperty("java.specification.version").removePrefix("1.").substringBefore('.').toInt() + private fun JIRMethod.findSinkCall(sinkName: String): JIRCallInst = - instList.filterIsInstance().first { it.callExpr.method.name == sinkName } + instList.filterIsInstance().first { + (it.callExpr as? JIRMethodCallExpr)?.method?.name == sinkName + } private fun JIRLocalAliasAnalysis.valueApAliases(value: JIRValue, stmt: JIRInst): List = valueAliases(value, stmt).filterIsInstance() @@ -631,6 +691,13 @@ class AliasSampleTest : BasicTestUtils() { override fun locationIsUnknown(loc: RegisteredLocation): Boolean = loc != this.loc } + private class MultiLocationUnit(val locs: Set) : JIRUnitResolver { + override fun resolve(method: JIRMethod): UnitType = + if (method.enclosingClass.declaration.location in locs) SingletonUnit else UnknownUnit + + override fun locationIsUnknown(loc: RegisteredLocation): Boolean = loc !in locs + } + companion object { const val ALIAS_SAMPLE_PKG = "sample.alias" const val SIMPLE_SAMPLE = "$ALIAS_SAMPLE_PKG.SimpleAliasSample" @@ -639,6 +706,7 @@ class AliasSampleTest : BasicTestUtils() { const val INTERPROC_SAMPLE = "$ALIAS_SAMPLE_PKG.InterProcAliasSample" const val FLAKY_SAMPLE = "$ALIAS_SAMPLE_PKG.FlakyAliasSample" const val HEADER_VALUES_SAMPLE = "sample.alias.HeaderValuesHangSample" + const val RECORD_SAMPLE = "$ALIAS_SAMPLE_PKG.RecordAliasSample" private const val FIELD_VALUE = "value" private const val FIELD_BOX = "box" diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/test/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/DSUAliasAnalysisInvalidateOuterHeapAliasesTest.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/test/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/DSUAliasAnalysisInvalidateOuterHeapAliasesTest.kt index aefa6cc53..1fc46edfc 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/test/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/DSUAliasAnalysisInvalidateOuterHeapAliasesTest.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/test/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/DSUAliasAnalysisInvalidateOuterHeapAliasesTest.kt @@ -17,7 +17,7 @@ class DSUAliasAnalysisInvalidateOuterHeapAliasesTest { private val analysis = DSUAliasAnalysis( methodCallResolver = object : CallResolver { - override fun resolveMethodCall(callStmt: Stmt.Call, level: Int): List? = null + override fun resolveMethodCall(callStmt: Stmt.MethodCall, level: Int): List? = null override fun buildMethodGraph(method: JIRMethod): JIRInstGraph? = null override fun externalCallModel(method: JIRMethod): List = emptyList() }, diff --git a/core/opentaint-ir/opentaint-ir-api-jvm/src/main/kotlin/org/opentaint/ir/api/jvm/cfg/JIRInst.kt b/core/opentaint-ir/opentaint-ir-api-jvm/src/main/kotlin/org/opentaint/ir/api/jvm/cfg/JIRInst.kt index fae604986..a3e2bcbe1 100644 --- a/core/opentaint-ir/opentaint-ir-api-jvm/src/main/kotlin/org/opentaint/ir/api/jvm/cfg/JIRInst.kt +++ b/core/opentaint-ir/opentaint-ir-api-jvm/src/main/kotlin/org/opentaint/ir/api/jvm/cfg/JIRInst.kt @@ -585,18 +585,20 @@ data class JIRInstanceOfExpr( } interface JIRCallExpr : JIRExpr, CommonCallExpr { - val method: JIRTypedMethod - override val args: List - override val type: JIRType - get() = method.returnType - override val operands: List get() = args } -interface JIRInstanceCallExpr : JIRCallExpr, CommonInstanceCallExpr { +interface JIRMethodCallExpr : JIRCallExpr { + val method: JIRTypedMethod + + override val type: JIRType + get() = method.returnType +} + +interface JIRInstanceCallExpr : JIRMethodCallExpr, CommonInstanceCallExpr { override val instance: JIRValue val declaredMethod: JIRTypedMethod @@ -639,8 +641,9 @@ data class JIRLambdaExpr( val isNewInvokeSpecial: Boolean get() = lambdaInvokeKind == BsmHandleTag.MethodHandle.NEW_INVOKE_SPECIAL - override val method get() = bsmRef.method + val bootstrapMethod get() = bsmRef.method override val args get() = callSiteArgs + override val type get() = callSiteReturnType override fun accept(visitor: JIRExprVisitor): T { return visitor.visitJIRLambdaExpr(this) @@ -656,8 +659,9 @@ data class JIRDynamicCallExpr( val callSiteArgs: List, ) : JIRCallExpr { - override val method get() = bsmRef.method + val bootstrapMethod get() = bsmRef.method override val args get() = callSiteArgs + override val type get() = callSiteReturnType override fun accept(visitor: JIRExprVisitor): T { return visitor.visitJIRDynamicCallExpr(this) @@ -695,7 +699,7 @@ data class JIRVirtualCallExpr( data class JIRStaticCallExpr( private val methodRef: TypedMethodRef, override val args: List, -) : JIRCallExpr { +) : JIRMethodCallExpr { override val method: JIRTypedMethod get() = methodRef.method diff --git a/core/opentaint-ir/opentaint-ir-approximations/src/test/kotlin/org/opentaint/ir/approximations/ApproximationsTest.kt b/core/opentaint-ir/opentaint-ir-approximations/src/test/kotlin/org/opentaint/ir/approximations/ApproximationsTest.kt index 82252400e..872b9b5ae 100644 --- a/core/opentaint-ir/opentaint-ir-approximations/src/test/kotlin/org/opentaint/ir/approximations/ApproximationsTest.kt +++ b/core/opentaint-ir/opentaint-ir-approximations/src/test/kotlin/org/opentaint/ir/approximations/ApproximationsTest.kt @@ -4,6 +4,7 @@ import org.opentaint.ir.api.jvm.JavaVersion import org.opentaint.ir.api.jvm.cfg.JIRAssignInst import org.opentaint.ir.api.jvm.cfg.JIRCallInst import org.opentaint.ir.api.jvm.cfg.JIRFieldRef +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRRawAssignInst import org.opentaint.ir.api.jvm.cfg.JIRRawCallInst import org.opentaint.ir.api.jvm.cfg.JIRRawFieldRef @@ -151,7 +152,7 @@ open class ApproximationsTest : BaseTest() { val location = inst.location assertTrue(location.method === method) - val callExpr = inst.callExpr + val callExpr = inst.callExpr as? JIRMethodCallExpr ?: return@forEach types += callExpr.type.typeName types += callExpr.method.returnType.typeName types += callExpr.method.enclosingType.typeName diff --git a/core/opentaint-ir/opentaint-ir-core/src/main/kotlin/org/opentaint/ir/impl/cfg/GraphExt.kt b/core/opentaint-ir/opentaint-ir-core/src/main/kotlin/org/opentaint/ir/impl/cfg/GraphExt.kt index e2fe1496a..e2fd908c8 100644 --- a/core/opentaint-ir/opentaint-ir-core/src/main/kotlin/org/opentaint/ir/impl/cfg/GraphExt.kt +++ b/core/opentaint-ir/opentaint-ir-core/src/main/kotlin/org/opentaint/ir/impl/cfg/GraphExt.kt @@ -292,11 +292,7 @@ open class JIRExceptionResolver( } override fun visitJIRLambdaExpr(expr: JIRLambdaExpr): List { - return buildList { - add(runtimeExceptionType) - add(errorType) - addAll(expr.method.exceptions.thisOrThrowable()) - } + return listOf(runtimeExceptionType, errorType) } override fun visitJIRDynamicCallExpr(expr: JIRDynamicCallExpr): List { diff --git a/core/opentaint-ir/opentaint-ir-core/src/test/kotlin/org/opentaint/ir/testing/UnknownClassesTest.kt b/core/opentaint-ir/opentaint-ir-core/src/test/kotlin/org/opentaint/ir/testing/UnknownClassesTest.kt index f5baa291f..c33f6af44 100644 --- a/core/opentaint-ir/opentaint-ir-core/src/test/kotlin/org/opentaint/ir/testing/UnknownClassesTest.kt +++ b/core/opentaint-ir/opentaint-ir-core/src/test/kotlin/org/opentaint/ir/testing/UnknownClassesTest.kt @@ -1,6 +1,7 @@ package org.opentaint.ir.testing import org.opentaint.ir.api.jvm.JIRMethod +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.ext.cfg.callExpr import org.opentaint.ir.api.jvm.ext.cfg.fieldRef import org.opentaint.ir.api.jvm.ext.findClass @@ -80,7 +81,7 @@ class UnknownClassesTest : BaseTest() { val cfg = flowGraph() cfg.instructions.forEach { it.callExpr?.let { - assertNotNull(it.method) + assertNotNull((it as? JIRMethodCallExpr)?.method) } it.fieldRef?.let { assertNotNull(it.field) diff --git a/core/opentaint-ir/opentaint-ir-core/src/test/kotlin/org/opentaint/ir/testing/cfg/InstructionsTest.kt b/core/opentaint-ir/opentaint-ir-core/src/test/kotlin/org/opentaint/ir/testing/cfg/InstructionsTest.kt index dc27a7f06..df3212569 100644 --- a/core/opentaint-ir/opentaint-ir-core/src/test/kotlin/org/opentaint/ir/testing/cfg/InstructionsTest.kt +++ b/core/opentaint-ir/opentaint-ir-core/src/test/kotlin/org/opentaint/ir/testing/cfg/InstructionsTest.kt @@ -43,7 +43,9 @@ class InstructionsTest : BaseInstructionsTest() { val bench = cp.findClass() val use = bench.declaredMethods.first { it.name == "use" } val instructions = method.instList.instructions - val firstUse = instructions.indexOfFirst { it.callExpr?.method?.method == use } + val firstUse = instructions.indexOfFirst { + (it.callExpr as? JIRMethodCallExpr)?.method?.method == use + } val assign = instructions[firstUse + 1] as JIRAssignInst assertEquals("b", (assign.lhv as JIRLocalVar).name) assertEquals("a", (assign.rhv as JIRLocalVar).name) @@ -54,7 +56,9 @@ class InstructionsTest : BaseInstructionsTest() { val clazz = cp.findClass() val method = clazz.declaredMethods.first { it.name == "invoke" } val instructions = method.instList.instructions - val usedArgumentExprs = instructions.filter { it.callExpr?.method?.method?.name == "println" } + val usedArgumentExprs = instructions.filter { + (it.callExpr as? JIRMethodCallExpr)?.method?.method?.name == "println" + } .flatMap { it.callExpr?.args.orEmpty() } val usedArgumentNames = usedArgumentExprs.map { (it as JIRArgument).name } assertEquals(listOf("i", "j", "b", "d"), usedArgumentNames) @@ -248,7 +252,7 @@ class InstructionsTest : BaseInstructionsTest() { val callDoSmth = instList.mapNotNull { it.callExpr }.first { it.toString().contains("doSmth") } - assertEquals("doSmth", callDoSmth.method.method.name) + assertEquals("doSmth", (callDoSmth as JIRMethodCallExpr).method.method.name) } @Test @@ -258,7 +262,7 @@ class InstructionsTest : BaseInstructionsTest() { val callDefaultMethod = instList.mapNotNull { it.callExpr }.first { it.toString().contains("defaultMethod") } - assertEquals("defaultMethod", callDefaultMethod.method.method.name) + assertEquals("defaultMethod", (callDefaultMethod as JIRMethodCallExpr).method.method.name) } @Test diff --git a/core/opentaint-java-querylang/src/main/kotlin/org/opentaint/semgrep/pattern/SemgrepJavaPatternMatcher.kt b/core/opentaint-java-querylang/src/main/kotlin/org/opentaint/semgrep/pattern/SemgrepJavaPatternMatcher.kt index 0dc9d267d..f370e51ec 100644 --- a/core/opentaint-java-querylang/src/main/kotlin/org/opentaint/semgrep/pattern/SemgrepJavaPatternMatcher.kt +++ b/core/opentaint-java-querylang/src/main/kotlin/org/opentaint/semgrep/pattern/SemgrepJavaPatternMatcher.kt @@ -15,6 +15,7 @@ import org.opentaint.ir.api.jvm.cfg.JIRInst import org.opentaint.ir.api.jvm.cfg.JIRInstanceCallExpr import org.opentaint.ir.api.jvm.cfg.JIRInt import org.opentaint.ir.api.jvm.cfg.JIRLocalVar +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRNewExpr import org.opentaint.ir.api.jvm.cfg.JIRReturnInst import org.opentaint.ir.api.jvm.cfg.JIRStringConstant @@ -703,7 +704,7 @@ class SemgrepJavaPatternMatcher( pattern: MethodInvocation, expr: JIRExpr, ): SemgrepMatchingResult { - if (expr !is JIRCallExpr) { + if (expr !is JIRMethodCallExpr) { val exprs = expandLocalVar(position, expr) val variants = exprs.map { match(it.second, pattern, it.first) } return mergeLocalVarVariantMatches(strategy, variants) diff --git a/core/opentaint-utils/opentaint-jvm-util/src/main/kotlin/org/opentaint/jvm/graph/JApplicationGraphImpl.kt b/core/opentaint-utils/opentaint-jvm-util/src/main/kotlin/org/opentaint/jvm/graph/JApplicationGraphImpl.kt index 857cb8a04..e2eb3ec71 100644 --- a/core/opentaint-utils/opentaint-jvm-util/src/main/kotlin/org/opentaint/jvm/graph/JApplicationGraphImpl.kt +++ b/core/opentaint-utils/opentaint-jvm-util/src/main/kotlin/org/opentaint/jvm/graph/JApplicationGraphImpl.kt @@ -3,6 +3,7 @@ package org.opentaint.jvm.graph import org.opentaint.ir.api.jvm.JIRClasspath import org.opentaint.ir.api.jvm.JIRMethod import org.opentaint.ir.api.jvm.cfg.JIRInst +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.ext.cfg.callExpr import org.opentaint.ir.impl.features.SyncUsagesExtension @@ -36,14 +37,14 @@ open class JApplicationGraphImpl( } override fun callees(node: JIRInst): Sequence { - val callExpr = node.callExpr ?: return emptySequence() + val callExpr = node.callExpr as? JIRMethodCallExpr ?: return emptySequence() return sequenceOf(callExpr.method.method) } override fun callers(method: JIRMethod): Sequence { return usages.findUsages(method).flatMap { it.flowGraph().instructions.asSequence().filter { inst -> - val callExpr = inst.callExpr ?: return@filter false + val callExpr = inst.callExpr as? JIRMethodCallExpr ?: return@filter false callExpr.method.method == method } } diff --git a/core/opentaint-utils/opentaint-jvm-util/src/main/kotlin/org/opentaint/jvm/transformer/JStringConcatTransformer.kt b/core/opentaint-utils/opentaint-jvm-util/src/main/kotlin/org/opentaint/jvm/transformer/JStringConcatTransformer.kt index b59a59ff5..094f6ccf8 100644 --- a/core/opentaint-utils/opentaint-jvm-util/src/main/kotlin/org/opentaint/jvm/transformer/JStringConcatTransformer.kt +++ b/core/opentaint-utils/opentaint-jvm-util/src/main/kotlin/org/opentaint/jvm/transformer/JStringConcatTransformer.kt @@ -41,7 +41,7 @@ object JStringConcatTransformer : JIRInstExtFeature { val stringConcatCalls = list.mapNotNull { inst -> val assignInst = inst as? JIRAssignInst ?: return@mapNotNull null val invokeDynamicExpr = assignInst.rhv as? JIRDynamicCallExpr ?: return@mapNotNull null - if (!methodIsStringConcat(invokeDynamicExpr.method.method)) return@mapNotNull null + if (!methodIsStringConcat(invokeDynamicExpr.bootstrapMethod.method)) return@mapNotNull null assignInst to invokeDynamicExpr } diff --git a/core/src/main/kotlin/org/opentaint/jvm/sast/ast/AbstractAstSpanResolver.kt b/core/src/main/kotlin/org/opentaint/jvm/sast/ast/AbstractAstSpanResolver.kt index e710edba3..55b4971c2 100644 --- a/core/src/main/kotlin/org/opentaint/jvm/sast/ast/AbstractAstSpanResolver.kt +++ b/core/src/main/kotlin/org/opentaint/jvm/sast/ast/AbstractAstSpanResolver.kt @@ -15,6 +15,7 @@ import org.opentaint.ir.api.jvm.cfg.JIRCallInst import org.opentaint.ir.api.jvm.cfg.JIRFieldRef import org.opentaint.ir.api.jvm.cfg.JIRInst import org.opentaint.ir.api.jvm.cfg.JIRInstanceCallExpr +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRNewExpr import org.opentaint.ir.api.jvm.cfg.JIRReturnInst import org.opentaint.ir.api.jvm.cfg.JIRValue @@ -51,7 +52,7 @@ abstract class AbstractAstSpanResolver(protected val traits: JIRSarifTraits) : A } protected fun isConstructorCall(call: JIRCallExpr): Boolean = - call.method.method.isConstructor + (call as? JIRMethodCallExpr)?.method?.method?.isConstructor == true protected fun inferAssignKind(assign: JIRAssignInst): InstructionKind { val l = assign.lhv @@ -170,7 +171,7 @@ abstract class AbstractAstSpanResolver(protected val traits: JIRSarifTraits) : A protected fun JIRInst?.getAssignee(): String? { // fix for initializer calls that are assignments in source - if (this is JIRCallInst && callExpr.method.method.isConstructor && callExpr is JIRInstanceCallExpr) { + if (this is JIRCallInst && isConstructorCall(callExpr) && callExpr is JIRInstanceCallExpr) { return getRawValue((callExpr as JIRInstanceCallExpr).instance) } if (this !is JIRAssignInst) return null diff --git a/core/src/main/kotlin/org/opentaint/jvm/sast/ast/JavaAstSpanResolver.kt b/core/src/main/kotlin/org/opentaint/jvm/sast/ast/JavaAstSpanResolver.kt index ca6c3a7f1..cb0b3ff1d 100644 --- a/core/src/main/kotlin/org/opentaint/jvm/sast/ast/JavaAstSpanResolver.kt +++ b/core/src/main/kotlin/org/opentaint/jvm/sast/ast/JavaAstSpanResolver.kt @@ -9,6 +9,7 @@ import org.antlr.v4.runtime.tree.TerminalNode import org.opentaint.dataflow.jvm.util.callee import org.opentaint.ir.api.jvm.JIRMethod import org.opentaint.ir.api.jvm.cfg.JIRInst +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.ext.cfg.callExpr import org.opentaint.jvm.sast.sarif.IntermediateLocation import org.opentaint.jvm.sast.sarif.JIRSarifTraits @@ -188,7 +189,7 @@ class JavaAstSpanResolver(traits: JIRSarifTraits) : AbstractAstSpanResolver(trai } private fun findMethodCallNode(root: ParseTree, line: Int, inst: JIRInst): ParserRuleContext? { - val call = inst.callExpr ?: return oldFindMethodCallNode(root, line) + val call = inst.callExpr as? JIRMethodCallExpr ?: return oldFindMethodCallNode(root, line) val callee = call.callee.name @@ -235,7 +236,7 @@ class JavaAstSpanResolver(traits: JIRSarifTraits) : AbstractAstSpanResolver(trai } private fun findObjectCreationNode(root: ParseTree, line: Int, inst: JIRInst): ParserRuleContext? { - val callExpr = inst.callExpr ?: return null + val callExpr = inst.callExpr as? JIRMethodCallExpr ?: return null val typeName = callExpr.method.method.enclosingClass.simpleName val creations = collectContexts(root, line) { checkCreatedType(it, typeName) } return adjustForAssignment(creations.maxByOrNull { spanLen(it) }, inst) diff --git a/core/src/main/kotlin/org/opentaint/jvm/sast/ast/KotlinAstSpanResolver.kt b/core/src/main/kotlin/org/opentaint/jvm/sast/ast/KotlinAstSpanResolver.kt index 351e81cee..d796e8b02 100644 --- a/core/src/main/kotlin/org/opentaint/jvm/sast/ast/KotlinAstSpanResolver.kt +++ b/core/src/main/kotlin/org/opentaint/jvm/sast/ast/KotlinAstSpanResolver.kt @@ -11,6 +11,7 @@ import org.opentaint.dataflow.jvm.util.callee import org.opentaint.jvm.sast.sarif.JIRSarifTraits import org.opentaint.ir.api.jvm.JIRMethod import org.opentaint.ir.api.jvm.cfg.JIRInst +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.ext.cfg.callExpr import org.opentaint.jvm.sast.sarif.IntermediateLocation import org.opentaint.jvm.sast.sarif.LocationSpan @@ -325,7 +326,7 @@ class KotlinAstSpanResolver(traits: JIRSarifTraits) : AbstractAstSpanResolver(tr } private fun findMethodCallNode(root: ParseTree, line: Int, inst: JIRInst): ParserRuleContext? { - val call = inst.callExpr ?: return oldFindMethodCallNode(root, line) + val call = inst.callExpr as? JIRMethodCallExpr ?: return oldFindMethodCallNode(root, line) val callee = call.callee.name @@ -382,7 +383,7 @@ class KotlinAstSpanResolver(traits: JIRSarifTraits) : AbstractAstSpanResolver(tr } private fun findObjectCreationNode(root: ParseTree, line: Int, inst: JIRInst): ParserRuleContext? { - val callExpr = inst.callExpr ?: return null + val callExpr = inst.callExpr as? JIRMethodCallExpr ?: return null val typeName = callExpr.method.method.enclosingClass.simpleName val creations = collectContexts(root, line) { checkCreatedType(it, typeName) } return adjustForAssignment(creations.maxByOrNull { spanLen(it) }, inst) @@ -561,4 +562,3 @@ class KotlinAstSpanResolver(traits: JIRSarifTraits) : AbstractAstSpanResolver(tr override fun visitChildren(node: RuleNode) = visitChildrenWithLine(node, line) } } - diff --git a/core/src/main/kotlin/org/opentaint/jvm/sast/project/JavaPropertiesResolveTransformer.kt b/core/src/main/kotlin/org/opentaint/jvm/sast/project/JavaPropertiesResolveTransformer.kt index ded47a54c..c84c94b76 100644 --- a/core/src/main/kotlin/org/opentaint/jvm/sast/project/JavaPropertiesResolveTransformer.kt +++ b/core/src/main/kotlin/org/opentaint/jvm/sast/project/JavaPropertiesResolveTransformer.kt @@ -11,6 +11,7 @@ import org.opentaint.ir.api.jvm.cfg.JIRImmediate import org.opentaint.ir.api.jvm.cfg.JIRInst import org.opentaint.ir.api.jvm.cfg.JIRInstList import org.opentaint.ir.api.jvm.cfg.JIRInstanceCallExpr +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRStringConstant import org.opentaint.ir.api.jvm.cfg.JIRValue import org.opentaint.ir.impl.fs.BuildFolderLocation @@ -181,7 +182,7 @@ class JavaPropertiesResolveTransformer( private inline fun traverseCalls( start: JIRInst, instructions: JIRInstList, - body: (JIRInst, JIRCallExpr) -> Unit + body: (JIRInst, JIRMethodCallExpr) -> Unit ): Nothing? { var instIdx = start.location.index while (instIdx >= 0) { @@ -210,7 +211,7 @@ class JavaPropertiesResolveTransformer( ) } - private fun JIRInst.findGetPropertyCall(): JIRCallExpr? { + private fun JIRInst.findGetPropertyCall(): JIRMethodCallExpr? { val call = findCallExpr() ?: return null if (!call.method.method.isGetProperty()) return null return call @@ -225,9 +226,9 @@ class JavaPropertiesResolveTransformer( private fun JIRMethod.isGetResource(): Boolean = name == GET_RESOURCE && enclosingClass.name == CLASS_LOADER - private fun JIRInst.findCallExpr(): JIRCallExpr? = when (this) { - is JIRAssignInst -> rhv as? JIRCallExpr - is JIRCallInst -> callExpr + private fun JIRInst.findCallExpr(): JIRMethodCallExpr? = when (this) { + is JIRAssignInst -> rhv as? JIRMethodCallExpr + is JIRCallInst -> callExpr as? JIRMethodCallExpr else -> null } diff --git a/core/src/main/kotlin/org/opentaint/jvm/sast/project/spring/SpringReactorOperatorsTransformer.kt b/core/src/main/kotlin/org/opentaint/jvm/sast/project/spring/SpringReactorOperatorsTransformer.kt index 77ea667c9..f3c91c39e 100644 --- a/core/src/main/kotlin/org/opentaint/jvm/sast/project/spring/SpringReactorOperatorsTransformer.kt +++ b/core/src/main/kotlin/org/opentaint/jvm/sast/project/spring/SpringReactorOperatorsTransformer.kt @@ -8,12 +8,12 @@ import org.opentaint.ir.api.jvm.JIRType import org.opentaint.ir.api.jvm.PredefinedPrimitives import org.opentaint.ir.api.jvm.cfg.JIRAssignInst import org.opentaint.ir.api.jvm.cfg.JIRBool -import org.opentaint.ir.api.jvm.cfg.JIRCallExpr import org.opentaint.ir.api.jvm.cfg.JIRClassConstant import org.opentaint.ir.api.jvm.cfg.JIRFieldRef import org.opentaint.ir.api.jvm.cfg.JIRGraph import org.opentaint.ir.api.jvm.cfg.JIRInst import org.opentaint.ir.api.jvm.cfg.JIRInstList +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRReturnInst import org.opentaint.ir.api.jvm.cfg.JIRStringConstant import org.opentaint.ir.api.jvm.cfg.JIRValue @@ -74,7 +74,7 @@ object SpringReactorOperatorsTransformer : JIRInstExtFeature { private fun findOperatorsSetField( graph: JIRGraph, setInst: JIRInst, - setCall: JIRCallExpr, + setCall: JIRMethodCallExpr, classInitializer: JIRMethod ): Pair? { val (fieldRef, instance, fieldValue) = setCall.args @@ -90,7 +90,7 @@ object SpringReactorOperatorsTransformer : JIRInstExtFeature { ?: return null val fieldUpdaterRef = JIRFieldRef(instance = null, fieldUpdater) - val fieldUpdaterCall = findSingleAssignedValue( + val fieldUpdaterCall = findSingleAssignedValue( classInitializerGraph, clinitReturn, fieldUpdaterRef )?.takeIf { it.method.method.isJavaAtomicRefFieldUpdater() } ?: return null @@ -148,9 +148,9 @@ object SpringReactorOperatorsTransformer : JIRInstExtFeature { return results.singleOrNull() } - private fun findOperatorsSet(inst: JIRInst): JIRCallExpr? { - val call = inst.callExpr - val method = call?.method ?: return null + private fun findOperatorsSet(inst: JIRInst): JIRMethodCallExpr? { + val call = inst.callExpr as? JIRMethodCallExpr ?: return null + val method = call.method if (!method.isStatic) return null if (method.enclosingType.typeName != OPERATORS_CLASS) return null diff --git a/core/src/main/kotlin/org/opentaint/jvm/sast/sarif/JIRSarifTraits.kt b/core/src/main/kotlin/org/opentaint/jvm/sast/sarif/JIRSarifTraits.kt index 946f585f1..9df1f9aae 100644 --- a/core/src/main/kotlin/org/opentaint/jvm/sast/sarif/JIRSarifTraits.kt +++ b/core/src/main/kotlin/org/opentaint/jvm/sast/sarif/JIRSarifTraits.kt @@ -16,6 +16,7 @@ import org.opentaint.ir.api.jvm.cfg.JIRFieldRef import org.opentaint.ir.api.jvm.cfg.JIRInst import org.opentaint.ir.api.jvm.cfg.JIRInstanceCallExpr import org.opentaint.ir.api.jvm.cfg.JIRLocalVar +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRThis import org.opentaint.ir.api.jvm.cfg.JIRValue import org.opentaint.ir.api.jvm.cfg.values @@ -85,7 +86,7 @@ class JIRSarifTraits( printThis(statement) { getReadableInstance(it) } private inline fun printThis(statement: JIRInst, parseStatement: (JIRInst) -> String?): String = - if (statement.callExpr?.callee?.isConstructor == true) { + if ((statement.callExpr as? JIRMethodCallExpr)?.callee?.isConstructor == true) { "the created object" } else { parseStatement(statement) ?: "the calling object" @@ -108,12 +109,12 @@ class JIRSarifTraits( } override fun getCallee(callExpr: CommonCallExpr): JIRMethod { - check(callExpr is JIRCallExpr) + check(callExpr is JIRMethodCallExpr) { "Dynamic call sites do not have a callee method" } return callExpr.callee } override fun getCalleeClassName(callExpr: CommonCallExpr): String { - check(callExpr is JIRCallExpr) + check(callExpr is JIRMethodCallExpr) { "Dynamic call sites do not have a callee method" } return callExpr.callee.enclosingClass.simpleName } diff --git a/core/src/main/kotlin/org/opentaint/jvm/sast/sarif/TraceMessageBuilder.kt b/core/src/main/kotlin/org/opentaint/jvm/sast/sarif/TraceMessageBuilder.kt index 18300af11..46b69e9e1 100644 --- a/core/src/main/kotlin/org/opentaint/jvm/sast/sarif/TraceMessageBuilder.kt +++ b/core/src/main/kotlin/org/opentaint/jvm/sast/sarif/TraceMessageBuilder.kt @@ -30,6 +30,7 @@ import org.opentaint.ir.api.jvm.cfg.JIRCallInst import org.opentaint.ir.api.jvm.cfg.JIRGraph import org.opentaint.ir.api.jvm.cfg.JIRInst import org.opentaint.ir.api.jvm.cfg.JIRLocalVar +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRReturnInst import org.opentaint.ir.api.jvm.cfg.JIRThis import org.opentaint.ir.api.jvm.cfg.JIRThrowInst @@ -116,7 +117,7 @@ class TraceMessageBuilder( private val markedVararg = hashMapOf>() private fun JIRInst.getCallVararg(): CommonValue? { - val call = (traits.getCallExpr(this) as JIRCallExpr?) ?: return null + val call = traits.getCallExpr(this) as? JIRMethodCallExpr ?: return null if (!call.isVararg() || call.args.isEmpty()) return null return call.args.last() } @@ -133,7 +134,8 @@ class TraceMessageBuilder( return } - val expr = call.callExpr + val expr = call.callExpr as? JIRMethodCallExpr + ?: return // skip `this` as the first argument as it is not used in lambda's call val captureStart = if (expr.args.isNotEmpty() && expr.args[0] is JIRThis) 1 else 0 val lambdaCapture = expr.args.drop(captureStart).map { param -> @@ -192,7 +194,8 @@ class TraceMessageBuilder( } private fun CommonInst.isLambdaCreation() = - this is JIRCallInst && this.callExpr.method.method is JIRLambdaMethod + this is JIRCallInst && + (this.callExpr as? JIRMethodCallExpr)?.method?.method is JIRLambdaMethod private fun TracePathNode.isLambdaCreation() = this.statement.isLambdaCreation() diff --git a/core/src/test/kotlin/org/opentaint/jvm/sast/ast/JavaAstSpanResolverTest.kt b/core/src/test/kotlin/org/opentaint/jvm/sast/ast/JavaAstSpanResolverTest.kt index 862b4a176..057eac7fe 100644 --- a/core/src/test/kotlin/org/opentaint/jvm/sast/ast/JavaAstSpanResolverTest.kt +++ b/core/src/test/kotlin/org/opentaint/jvm/sast/ast/JavaAstSpanResolverTest.kt @@ -6,6 +6,7 @@ import org.junit.jupiter.api.Test import org.opentaint.ir.api.jvm.cfg.JIRArrayAccess import org.opentaint.ir.api.jvm.cfg.JIRAssignInst import org.opentaint.ir.api.jvm.cfg.JIRCallExpr +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRCallInst import org.opentaint.ir.api.jvm.cfg.JIRFieldRef import org.opentaint.ir.api.jvm.cfg.JIRNullConstant @@ -40,7 +41,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val getValueAssign = assignInsts.find { inst -> val rhv = inst.rhv - rhv is JIRCallExpr && rhv.method.method.name == "getValue" + rhv is JIRMethodCallExpr && rhv.method.method.name == "getValue" } checkNotNull(getValueAssign) { "Assignment with getValue() call not found" } @@ -130,7 +131,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(SAMPLE_FQN, "createObject") val constructorCall = callInsts.find { - it.callExpr.method.method.isConstructor + it.callExpr.methodOrNull!!.method.isConstructor } checkNotNull(constructorCall) { "Constructor call not found" } @@ -163,7 +164,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(SAMPLE_FQN, "chainedCall") val toUpperCaseCall = callInsts.find { - it.callExpr?.method?.method?.name == "toUpperCase" + it.callExpr?.methodOrNull?.method?.name == "toUpperCase" } checkNotNull(toUpperCaseCall) { "toUpperCase() call not found in chain" } @@ -202,7 +203,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(STATIC_SAMPLE_FQN, "staticMethod") val toLowerCaseCall = callInsts.find { - it.callExpr?.method?.method?.name == "toLowerCase" + it.callExpr?.methodOrNull?.method?.name == "toLowerCase" } checkNotNull(toLowerCaseCall) { "toLowerCase() call not found" } @@ -246,7 +247,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(SAMPLE_FQN, "simpleMethodCall") val toUpperCaseCall = callInsts.find { - it.callExpr?.method?.method?.name == "toUpperCase" + it.callExpr?.methodOrNull?.method?.name == "toUpperCase" } checkNotNull(toUpperCaseCall) { "toUpperCase() call not found" } @@ -382,11 +383,11 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val instructions = method.instList val expectedSpans = mapOf( - "entry" to findInstruction(instructions) { it.rhv is JIRCallExpr && (it.rhv as JIRCallExpr).method.method.name == "trim" }, - "local" to findInstruction(instructions) { it.rhv is JIRCallExpr && (it.rhv as JIRCallExpr).method.method.name == "trim" }, + "entry" to findInstruction(instructions) { it.rhv is JIRMethodCallExpr && (it.rhv as JIRMethodCallExpr).method.method.name == "trim" }, + "local" to findInstruction(instructions) { it.rhv is JIRMethodCallExpr && (it.rhv as JIRMethodCallExpr).method.method.name == "trim" }, "fieldWrite" to findInstruction(instructions) { it.lhv is JIRFieldRef }, "fieldRead" to findInstruction(instructions) { it.rhv is JIRFieldRef }, - "call" to findInstruction(instructions) { it.rhv is JIRCallExpr && (it.rhv as JIRCallExpr).method.method.name == "toUpperCase" }, + "call" to findInstruction(instructions) { it.rhv is JIRMethodCallExpr && (it.rhv as JIRMethodCallExpr).method.method.name == "toUpperCase" }, "return" to findInstruction(instructions) { true }, "exit" to findInstruction(instructions) { true } ) @@ -540,7 +541,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(SAMPLE_FQN, "methodCallNoArgs") val noArgsCall = callInsts.find { - it.callExpr.method.method.name == "noArgsHelper" + it.callExpr.methodOrNull!!.method.name == "noArgsHelper" } checkNotNull(noArgsCall) { "noArgsHelper() call not found" } @@ -558,7 +559,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(SAMPLE_FQN, "methodCallOneArg") val oneArgCall = callInsts.find { - it.callExpr.method.method.name == "oneArgHelper" + it.callExpr.methodOrNull!!.method.name == "oneArgHelper" } checkNotNull(oneArgCall) { "oneArgHelper() call not found" } @@ -576,7 +577,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(SAMPLE_FQN, "methodCallTwoArgs") val twoArgsCall = callInsts.find { - it.callExpr.method.method.name == "twoArgsHelper" + it.callExpr.methodOrNull!!.method.name == "twoArgsHelper" } checkNotNull(twoArgsCall) { "twoArgsHelper() call not found" } @@ -594,7 +595,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(SAMPLE_FQN, "methodCallThreeArgs") val threeArgsCall = callInsts.find { - it.callExpr.method.method.name == "threeArgsHelper" + it.callExpr.methodOrNull!!.method.name == "threeArgsHelper" } checkNotNull(threeArgsCall) { "threeArgsHelper() call not found" } @@ -612,7 +613,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(SAMPLE_FQN, "methodCallVarargs") val varargsCall = callInsts.find { - it.callExpr.method.method.name == "varargsHelper" + it.callExpr.methodOrNull!!.method.name == "varargsHelper" } checkNotNull(varargsCall) { "varargsHelper() call not found" } @@ -631,7 +632,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val sourcePath = getSourcePath(SAMPLE_FQN) val callInsts = getInstructionsOfType(SAMPLE_FQN, "localVarCallNoArgs") - val call = callInsts.find { it.callExpr?.method?.method?.name == "length" } + val call = callInsts.find { it.callExpr?.methodOrNull?.method?.name == "length" } checkNotNull(call) { "length() call not found" } val location = createIntermediateLocation(call) @@ -646,7 +647,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val sourcePath = getSourcePath(SAMPLE_FQN) val callInsts = getInstructionsOfType(SAMPLE_FQN, "localVarCallOneArg") - val call = callInsts.find { it.callExpr?.method?.method?.name == "charAt" } + val call = callInsts.find { it.callExpr?.methodOrNull?.method?.name == "charAt" } checkNotNull(call) { "charAt() call not found" } val location = createIntermediateLocation(call) @@ -661,7 +662,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val sourcePath = getSourcePath(SAMPLE_FQN) val callInsts = getInstructionsOfType(SAMPLE_FQN, "localVarCallTwoArgs") - val call = callInsts.find { it.callExpr?.method?.method?.name == "substring" } + val call = callInsts.find { it.callExpr?.methodOrNull?.method?.name == "substring" } checkNotNull(call) { "substring() call not found" } val location = createIntermediateLocation(call) @@ -676,7 +677,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val sourcePath = getSourcePath(SAMPLE_FQN) val callInsts = getInstructionsOfType(SAMPLE_FQN, "localVarCallThreeArgs") - val call = callInsts.find { it.callExpr?.method?.method?.name == "replace" } + val call = callInsts.find { it.callExpr?.methodOrNull?.method?.name == "replace" } checkNotNull(call) { "replace() call not found" } val location = createIntermediateLocation(call) @@ -693,7 +694,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val sourcePath = getSourcePath(SAMPLE_FQN) val callInsts = getInstructionsOfType(SAMPLE_FQN, "chainedCallNoArgs") - val call = callInsts.find { it.callExpr?.method?.method?.name == "length" } + val call = callInsts.find { it.callExpr?.methodOrNull?.method?.name == "length" } checkNotNull(call) { "length() call not found" } val location = createIntermediateLocation(call) @@ -708,7 +709,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val sourcePath = getSourcePath(SAMPLE_FQN) val callInsts = getInstructionsOfType(SAMPLE_FQN, "chainedCallOneArg") - val call = callInsts.find { it.callExpr?.method?.method?.name == "charAt" } + val call = callInsts.find { it.callExpr?.methodOrNull?.method?.name == "charAt" } checkNotNull(call) { "charAt() call not found" } val location = createIntermediateLocation(call) @@ -723,7 +724,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val sourcePath = getSourcePath(SAMPLE_FQN) val callInsts = getInstructionsOfType(SAMPLE_FQN, "chainedCallTwoArgs") - val call = callInsts.find { it.callExpr?.method?.method?.name == "substring" } + val call = callInsts.find { it.callExpr?.methodOrNull?.method?.name == "substring" } checkNotNull(call) { "substring() call not found" } val location = createIntermediateLocation(call) @@ -738,7 +739,7 @@ class JavaAstSpanResolverTest : BasicTestUtils() { val sourcePath = getSourcePath(SAMPLE_FQN) val callInsts = getInstructionsOfType(SAMPLE_FQN, "chainedCallThreeArgs") - val call = callInsts.find { it.callExpr?.method?.method?.name == "replace" } + val call = callInsts.find { it.callExpr?.methodOrNull?.method?.name == "replace" } checkNotNull(call) { "replace() call not found" } val location = createIntermediateLocation(call) @@ -749,3 +750,5 @@ class JavaAstSpanResolverTest : BasicTestUtils() { } } +private val JIRCallExpr.methodOrNull + get() = (this as? JIRMethodCallExpr)?.method diff --git a/core/src/test/kotlin/org/opentaint/jvm/sast/ast/KotlinAstSpanResolverTest.kt b/core/src/test/kotlin/org/opentaint/jvm/sast/ast/KotlinAstSpanResolverTest.kt index 7014bddb7..ac86d93fd 100644 --- a/core/src/test/kotlin/org/opentaint/jvm/sast/ast/KotlinAstSpanResolverTest.kt +++ b/core/src/test/kotlin/org/opentaint/jvm/sast/ast/KotlinAstSpanResolverTest.kt @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Test import org.opentaint.ir.api.jvm.cfg.JIRArrayAccess import org.opentaint.ir.api.jvm.cfg.JIRAssignInst import org.opentaint.ir.api.jvm.cfg.JIRCallExpr +import org.opentaint.ir.api.jvm.cfg.JIRMethodCallExpr import org.opentaint.ir.api.jvm.cfg.JIRCallInst import org.opentaint.ir.api.jvm.cfg.JIRFieldRef import org.opentaint.ir.api.jvm.cfg.JIRInst @@ -61,7 +62,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val getValueAssign = assignInsts.find { inst -> val rhv = inst.rhv - rhv is JIRCallExpr && rhv.method.method.name == "getFieldValue" + rhv is JIRMethodCallExpr && rhv.method.method.name == "getFieldValue" } checkNotNull(getValueAssign) { "Assignment with getFieldValue() call not found" } @@ -151,7 +152,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(SAMPLE_FQN, "createObject") val constructorCall = callInsts.find { - it.callExpr.method.method.isConstructor + it.callExpr.methodOrNull!!.method.isConstructor } checkNotNull(constructorCall) { "Constructor call not found" } @@ -323,8 +324,8 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val instructions = method.instList val expectedSpans = mapOf( - "entry" to findInstruction(instructions) { it.rhv is JIRCallExpr && (it.rhv as JIRCallExpr).method.method.name == "trim" }, - "local" to findInstruction(instructions) { it.rhv is JIRCallExpr && (it.rhv as JIRCallExpr).method.method.name == "trim" }, + "entry" to findInstruction(instructions) { it.rhv is JIRMethodCallExpr && (it.rhv as JIRMethodCallExpr).method.method.name == "trim" }, + "local" to findInstruction(instructions) { it.rhv is JIRMethodCallExpr && (it.rhv as JIRMethodCallExpr).method.method.name == "trim" }, "fieldWrite" to findInstruction(instructions) { it.lhv is JIRFieldRef }, "fieldRead" to findInstruction(instructions) { it.rhv is JIRFieldRef }, "return" to findInstruction(instructions) { true }, @@ -550,7 +551,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val assignInsts = method.instList.filterIsInstance() val extensionCall = assignInsts.find { - it.callExpr?.method?.method?.name == "myExtension" + it.callExpr?.methodOrNull?.method?.name == "myExtension" } checkNotNull(extensionCall) { "Extension function call not found" } @@ -584,7 +585,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val assignInsts = method.instList.filterIsInstance() val plusCall = assignInsts.find { - it.callExpr?.method?.method?.name == "plus" + it.callExpr?.methodOrNull?.method?.name == "plus" } checkNotNull(plusCall) { "Plus operator call not found" } @@ -722,7 +723,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val callInsts = getInstructionsOfType(DATA_CLASS_WITH_INIT_FQN, "") val printlnCall = callInsts.find { inst -> - inst.callExpr.method.method.name == "println" + inst.callExpr.methodOrNull!!.method.name == "println" } checkNotNull(printlnCall) { "println call in init block not found" } @@ -772,7 +773,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val assignInsts = method.instList.filterIsInstance() val suspendCall = assignInsts.find { - it.callExpr?.method?.method?.name == "suspendFunction" + it.callExpr?.methodOrNull?.method?.name == "suspendFunction" } checkNotNull(suspendCall) { "Suspend function call not found" } @@ -894,7 +895,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val assignInsts = method.instList.filterIsInstance() val uppercaseCall = assignInsts.find { inst -> - inst.callExpr?.method?.method?.name == "toUpperCase" + inst.callExpr?.methodOrNull?.method?.name == "toUpperCase" } checkNotNull(uppercaseCall) { "toUpperCase() call not found in suspend function body" } @@ -910,7 +911,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val assignInsts = method.instList.filterIsInstance() val valueOfCall = assignInsts.find { inst -> - inst.callExpr?.method?.method?.name == "valueOf" + inst.callExpr?.methodOrNull?.method?.name == "valueOf" } checkNotNull(valueOfCall) { "valueOf() call from inlined body not found" } @@ -962,7 +963,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val assignInsts = method.instList.filterIsInstance() val uppercaseCall = assignInsts.find { inst -> - inst.callExpr?.method?.method?.name == "toUpperCase" + inst.callExpr?.methodOrNull?.method?.name == "toUpperCase" } checkNotNull(uppercaseCall) { "uppercase() call inside for loop not found" } @@ -978,7 +979,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val assignInsts = method.instList.filterIsInstance() val getCall = assignInsts.find { inst -> - inst.callExpr?.method?.method?.name == "get" + inst.callExpr?.methodOrNull?.method?.name == "get" } checkNotNull(getCall) { "get() call inside while loop not found" } @@ -1009,10 +1010,10 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val expectedSpans = mutableMapOf() expectedSpans["complexLoopCall"] = findInstruction(instructions) { - it.callExpr?.method?.method?.name == "toUpperCase" + it.callExpr?.methodOrNull?.method?.name == "toUpperCase" } expectedSpans["complexWhileGet"] = findInstruction(instructions) { - it.callExpr?.method?.method?.name == "get" + it.callExpr?.methodOrNull?.method?.name == "get" } expectedSpans["complexReturn"] = findInstruction(instructions) { true } expectedSpans["complexMethodExit"] = findInstruction(instructions) { true } @@ -1054,7 +1055,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { val assignInsts = method.instList.filterIsInstance() val processCall = assignInsts.find { inst -> - inst.callExpr?.method?.method?.name == "process" + inst.callExpr?.methodOrNull?.method?.name == "process" } checkNotNull(processCall) { "process() call not found in expression body" } @@ -1197,7 +1198,7 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { checkNotNull(defaultCtor) { "Default constructor not found" } val primaryCtorCall = findInstruction(defaultCtor.instList) { - it.callExpr.method.method.isConstructor + it.callExpr.methodOrNull!!.method.isConstructor } checkNotNull(primaryCtorCall) { "Primary ctor call not found" } @@ -1248,3 +1249,6 @@ class KotlinAstSpanResolverTest : BasicTestUtils() { private fun getAnnotatedSourcePath() = sourcesDir.resolve("test/samples/KotlinAnnotatedSample.kt") } + +private val JIRCallExpr.methodOrNull + get() = (this as? JIRMethodCallExpr)?.method