From 47fa1a8ec6ec7ca22330525b3225228669fda144 Mon Sep 17 00:00:00 2001 From: Valentyn Sobol <8640896+Saloed@users.noreply.github.com> Date: Wed, 13 May 2026 13:18:21 +0300 Subject: [PATCH] Keep unbound aliases --- .../jvm/ap/ifds/JIRLocalAliasAnalysis.kt | 27 ++++-- .../jvm/ap/ifds/alias/DSUAliasAnalysis.kt | 33 +++++-- .../ifds/alias/JIRIntraProcAliasAnalysis.kt | 97 ++++++++++++++----- .../ap/ifds/taint/JIRBasicAtomEvaluator.kt | 2 +- 4 files changed, 121 insertions(+), 38 deletions(-) diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRLocalAliasAnalysis.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRLocalAliasAnalysis.kt index 02c230320..6176d2386 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRLocalAliasAnalysis.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/JIRLocalAliasAnalysis.kt @@ -30,6 +30,7 @@ class JIRLocalAliasAnalysis( class MethodAliasInfo( val aliasBeforeStatement: Array>?>?, val aliasAfterStatement: Array>?>?, + val unboundBeforeStatement: Array>?>?, ) private fun getLocalVarAliases( @@ -46,10 +47,19 @@ class JIRLocalAliasAnalysis( return getLocalVarAliases(aliasBefore, idx, base) } - fun getAllAliasAtStatement(statement: CommonInst): Int2ObjectOpenHashMap> { - val aliasBefore = aliasInfo.aliasBeforeStatement ?: return Int2ObjectOpenHashMap() + fun getAllAliasAtStatement(statement: CommonInst): List> { + val result = mutableListOf>() val idx = languageManager.getInstIndex(statement) - return aliasBefore[idx]?.let { wrapAllInfo(it) } ?: Int2ObjectOpenHashMap() + + aliasInfo.aliasBeforeStatement?.let { aliasBefore -> + aliasBefore[idx]?.let { wrapAllInfo(it) }?.let { result.addAll(it.values) } + } + + aliasInfo.unboundBeforeStatement?.let { unboundBefore -> + unboundBefore[idx]?.let { aliases -> aliases.map { wrapAliasSet(it) } }?.let { result.addAll(it) } + } + + return result } fun findAliasAfterStatement(base: AccessPathBase.LocalVar, statement: CommonInst): List? { @@ -89,21 +99,26 @@ class JIRLocalAliasAnalysis( fun wrapAllInfo(info: Int2ObjectOpenHashMap>): Int2ObjectOpenHashMap> { val result = Int2ObjectOpenHashMap>() for ((key, aliases) in info) { - result.put(key, List(aliases.size) { aliases[it].wrapAliasInfo() }) + result.put(key, wrapAliasSet(aliases)) } return result } + fun wrapAliasSet(aliases: Array): List = + List(aliases.size) { aliases[it].wrapAliasInfo() } + fun unwrapAllInfo(info: Int2ObjectOpenHashMap>): Int2ObjectOpenHashMap> { val result = Int2ObjectOpenHashMap>(info.size, 0.99f) val iter = info.int2ObjectEntrySet().fastIterator() while (iter.hasNext()) { val entry = iter.next() - val value = entry.value - val unwrapped = Array(value.size) { value[it].unwrap() } + val unwrapped = unwrapAliasSet(entry.value) result.put(entry.intKey, unwrapped) } return result } + + fun unwrapAliasSet(aliases: List): Array = + Array(aliases.size) { aliases[it].unwrap() } } } 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 fbb5185a2..6c83262c3 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 @@ -506,9 +506,9 @@ class DSUAliasAnalysis( is Stmt.Copy -> evalCopy(stmt, state) - is Stmt.FieldStore -> evalFieldStore(stmt, state) + is Stmt.FieldStore -> evalFieldStore(stmt, callFrame, state) - is Stmt.ArrayStore -> evalArrayStore(stmt, state) + is Stmt.ArrayStore -> evalArrayStore(stmt, callFrame, state) // no effect on alias info is Stmt.Return, @@ -602,6 +602,23 @@ class DSUAliasAnalysis( instance: RefValue, value: ExprOrValue, state: State, + stmt: Stmt, + callFrame: CallTreeNode, + heapAppender: (Int) -> HeapAlias + ): State { + val valueInfo = when (value) { + is Expr -> evalExpr(value, stmt, callFrame, state) + is RefValue -> aliasSetFromInfo(value.aliasInfo()) + } + + return evalHeapStore(isFieldStore, instance, valueInfo, state, heapAppender) + } + + private fun evalHeapStore( + isFieldStore: Boolean, + instance: RefValue, + value: AliasSet, + state: State, heapAppender: (Int) -> HeapAlias ): State { val instanceInfo = instance.aliasInfo() @@ -614,18 +631,16 @@ class DSUAliasAnalysis( resultState = resultState.remove(heapAlias) } - if (value is RefValue) { - resultState = resultState.mergeWith(value.aliasInfo().index(), heapAlias) - } + resultState = resultState.mergeWith(value.repr, heapAlias) return resultState } - private fun evalArrayStore(stmt: Stmt.ArrayStore, state: State): State = - evalHeapStore(isFieldStore = false, stmt.instance, stmt.value, state, ::createArrayAlias) + private fun evalArrayStore(stmt: Stmt.ArrayStore, callFrame: CallTreeNode, state: State): State = + evalHeapStore(isFieldStore = false, stmt.instance, stmt.value, state, stmt, callFrame, ::createArrayAlias) - private fun evalFieldStore(stmt: Stmt.FieldStore, state: State): State = - evalHeapStore(isFieldStore = true, stmt.instance, stmt.value, state) { createFieldAlias(it, stmt.field) } + private fun evalFieldStore(stmt: Stmt.FieldStore, callFrame: CallTreeNode, state: State): State = + evalHeapStore(isFieldStore = true, stmt.instance, stmt.value, state, stmt, callFrame) { createFieldAlias(it, stmt.field) } private fun State.containsMultipleConcreteOrOuterLocations(instance: AAInfo): Boolean = containsMultipleConcreteOrOuterLocations(instance.index(), IntOpenHashSet()) diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/JIRIntraProcAliasAnalysis.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/JIRIntraProcAliasAnalysis.kt index 36738a0ee..b5570e611 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/JIRIntraProcAliasAnalysis.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/alias/JIRIntraProcAliasAnalysis.kt @@ -75,7 +75,8 @@ class JIRIntraProcAliasAnalysis( JIRLocalAliasAnalysis.MethodAliasInfo( aliasBeforeStatement = null, - aliasAfterStatement = null + aliasAfterStatement = null, + unboundBeforeStatement = null, ) } ) @@ -87,27 +88,65 @@ class JIRIntraProcAliasAnalysis( val jig = getJIG(entryPoint) val daa = DSUAliasAnalysis(CallResolver(), localVariableReachability, cancellation).analyze(jig) - val aliasBeforeStatement = Array(jig.statements.size) { i -> - resolveLocalVar(daa.statesBeforeStmt[i], localVariableReachability, i, cancellation) - } + val aliasBeforeStatement = Array(jig.statements.size) { Int2ObjectOpenHashMap>() } + val aliasAfterStatement = Array(jig.statements.size) { Int2ObjectOpenHashMap>() } + + val unboundAliasBeforeStatement = Array(jig.statements.size) { mutableListOf>() } + val unboundAliasAfterStatement = Array(jig.statements.size) { mutableListOf>() } - val aliasAfterStatement = Array(jig.statements.size) { i -> - resolveLocalVar(daa.statesAfterStmt[i], localVariableReachability, i, cancellation) + for (i in jig.statements.indices) { + resolveLocalVar( + daa.statesBeforeStmt[i], localVariableReachability, + aliasBeforeStatement[i], unboundAliasBeforeStatement[i], + i, cancellation + ) + + resolveLocalVar( + daa.statesAfterStmt[i], localVariableReachability, + aliasAfterStatement[i], unboundAliasAfterStatement[i], + i, cancellation + ) } - return compressAliasInfo(aliasBeforeStatement, aliasAfterStatement) + return compressAliasInfo(aliasBeforeStatement, aliasAfterStatement, unboundAliasBeforeStatement) } private fun compressAliasInfo( aliasBeforeStatement: Array>>, - aliasAfterStatement: Array>> + aliasAfterStatement: Array>>, + unboundBeforeStatement: Array>>, ): JIRLocalAliasAnalysis.MethodAliasInfo { val compressedBefore = arrayOfNulls>>(aliasBeforeStatement.size) val compressedAfter = arrayOfNulls>>(aliasAfterStatement.size) compress(aliasBeforeStatement, compressedBefore, reference = null, referenceCompressed = null) compress(aliasAfterStatement, compressedAfter, aliasBeforeStatement, compressedBefore) - return JIRLocalAliasAnalysis.MethodAliasInfo(compressedBefore, compressedAfter) + + val compressedUnbound = compressUnboundAliases(unboundBeforeStatement) + return JIRLocalAliasAnalysis.MethodAliasInfo(compressedBefore, compressedAfter, compressedUnbound) + } + + private fun compressUnboundAliases( + statementInfo: Array>> + ): Array>?>? { + if (statementInfo.all { it.isEmpty() }) return null + + val compressed = arrayOfNulls>>(statementInfo.size) + for (i in statementInfo.indices) { + val current = statementInfo[i] + if (current.isEmpty()) continue + + if (i > 0 && statementInfo[i - 1] == current) { + compressed[i] = compressed[i - 1] + continue + } + + val unwrapped = Array(current.size) { i -> + JIRLocalAliasAnalysis.unwrapAliasSet(current[i]) + } + compressed[i] = unwrapped + } + return compressed } private fun compress( @@ -144,16 +183,12 @@ class JIRIntraProcAliasAnalysis( private fun resolveLocalVar( daa: ConnectedAliases, reachableLocals: JIRLocalVariableReachability, + result: Int2ObjectOpenHashMap>, + unboundAliases: MutableList>, instIdx: Int, cancellation: AnalysisCancellation, - ): Int2ObjectOpenHashMap> { - val result = Int2ObjectOpenHashMap>() + ) { daa.aliasGroups.forEach { (_, group) -> - val locals = group.filter { - it is LocalAlias.SimpleLoc && it.loc is Local && reachableLocals.isReachable(it.loc.idx, instIdx) - } - if (locals.isEmpty()) return@forEach - val converted = group .flatMap { it.convertToAliasInfo(daa.aliasGroups, depth = 0, cancellation) } .filter { it !is AliasApInfo || reachableLocals.isReachable(it.base, instIdx) } @@ -161,12 +196,20 @@ class JIRIntraProcAliasAnalysis( // size == 1 means only local was converted to AliasInfo; not really meaningful if (converted.size <= 1) return@forEach + + val locals = converted.filterIsInstance() + .filter { it.accessors.isEmpty() } + .mapNotNull { it.base as? AccessPathBase.LocalVar } + + if (locals.isEmpty()) { + unboundAliases += converted + return@forEach + } + locals.forEach { local -> - val id = ((local as LocalAlias.SimpleLoc).loc as Local).idx - result[id] = converted + result[local.idx] = converted } } - return result } private fun AAInfo.convertToAliasInfo( @@ -215,11 +258,13 @@ class JIRIntraProcAliasAnalysis( } is LocalAlias.Alloc -> { - val assign = cur.stmt as? Stmt.Assign ?: return null + val assignedExpr = cur.stmt.assignedExpr() + ?: return null + + val const = assignedExpr as? SimpleValue.RefConst - val const = assign.expr as? SimpleValue.RefConst val stringConst = const?.expr as? JIRStringConstant - ?: return AliasAllocInfo(assign.originalIdx) + ?: return AliasAllocInfo(cur.stmt.originalIdx) AccessPathBase.Constant("java.lang.String", stringConst.value) } @@ -232,4 +277,12 @@ class JIRIntraProcAliasAnalysis( return AliasApInfo(base, emptyList()) } + + private fun Stmt.assignedExpr(): Expr? = when (this) { + is Stmt.Assign -> expr + is Stmt.FieldStore -> value as? Expr + is Stmt.ArrayStore -> value as? Expr + is Stmt.WriteStatic -> value as? Expr + else -> null + } } 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 4937fe8b1..b6cd59160 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 @@ -307,7 +307,7 @@ class JIRBasicAtomEvaluator( } } else { val allAliases = aa.getAllAliasAtStatement(statement) - for ((_, aliasSet) in allAliases) { + for (aliasSet in allAliases) { for (info in aliasSet) { if (info !is AliasApInfo || info.base != base) continue val singleAccessor = info.accessors.singleOrNull() ?: continue