Skip to content

Commit 5c4eb4b

Browse files
committed
feat: Enhance summary rewriter and enable alias analysis by default
1 parent 6da5b5e commit 5c4eb4b

File tree

8 files changed

+112
-64
lines changed

8 files changed

+112
-64
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ seqraOrg=seqra
33
seqraBuildVersion=2025.10.01.6b66511
44

55
seqraIrVersion=2025.10.09.8af579a
6-
seqraConfigVersion=2025.10.28.c6855e5
6+
seqraConfigVersion=2025.11.08.5fe2c2b
77
seqraUtilVersion=2025.10.28.b93c206

seqra-dataflow/src/main/kotlin/org/seqra/dataflow/ap/ifds/analysis/MethodCallSummaryHandler.kt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ interface MethodCallSummaryHandler {
3232
): Set<Sequent> = handleSummary(
3333
currentFactAp,
3434
summaryEffect,
35-
summaryFact
35+
summaryFact,
36+
createSideEffectRequirement = {
37+
check(it is ExclusionSet.Universe) { "Incorrect refinement" }
38+
null
39+
}
3640
) { initialFactRefinement: ExclusionSet?, summaryFactAp ->
3741
check(initialFactRefinement == null || initialFactRefinement is ExclusionSet.Universe) {
3842
"Incorrect refinement"
@@ -49,7 +53,10 @@ interface MethodCallSummaryHandler {
4953
): Set<Sequent> = handleSummary(
5054
currentFactAp,
5155
summaryEffect,
52-
summaryFact
56+
summaryFact,
57+
createSideEffectRequirement = { refinement ->
58+
Sequent.SideEffectRequirement(initialFactAp.refine(refinement))
59+
}
5360
) { initialFactRefinement: ExclusionSet?, summaryFactAp: FinalFactAp ->
5461
Sequent.FactToFact(initialFactAp.refine(initialFactRefinement), summaryFactAp, TraceInfo.ApplySummary)
5562
}
@@ -64,7 +71,11 @@ interface MethodCallSummaryHandler {
6471
): Set<Sequent> = handleSummary(
6572
currentFactAp,
6673
summaryEffect,
67-
summaryFact
74+
summaryFact,
75+
createSideEffectRequirement = {
76+
check(it is ExclusionSet.Universe) { "Incorrect refinement" }
77+
null
78+
}
6879
) { initialFactRefinement: ExclusionSet?, summaryFactAp: FinalFactAp ->
6980
check(initialFactRefinement == null || initialFactRefinement is ExclusionSet.Universe) {
7081
"Incorrect refinement"
@@ -86,6 +97,7 @@ interface MethodCallSummaryHandler {
8697
currentFactAp: FinalFactAp,
8798
summaryEffect: SummaryEdgeApplication,
8899
summaryFact: FinalFactAp,
100+
createSideEffectRequirement: (refinement: ExclusionSet) -> Sequent?,
89101
handleSummaryEdge: (initialFactRefinement: ExclusionSet?, summaryFactAp: FinalFactAp) -> Sequent
90102
): Set<Sequent> {
91103
val mappedSummaryFacts = mapMethodExitToReturnFlowFact(summaryFact)

seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRAnalysisManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import org.seqra.util.analysis.ApplicationGraph
4444

4545
class JIRAnalysisManager(
4646
cp: JIRClasspath,
47-
private val applyAliasInfo: Boolean = false,
47+
private val applyAliasInfo: Boolean = true,
4848
) : JIRLanguageManager(cp), TaintAnalysisManager {
4949
private val lambdaTracker = JIRLambdaTracker()
5050
private val factTypeChecker = JIRFactTypeChecker(cp)

seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,6 @@ class JIRMethodCallFlowFunction(
310310
addCallToReturn(factReaderAfterCleaner, aliased, trace)
311311
}
312312
}
313-
314-
// Skip method invocation
315-
return
316313
}
317314

318315
val cleanedFact = factReaderAfterCleaner.factAp
Lines changed: 45 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
package org.seqra.dataflow.jvm.ap.ifds.analysis
22

3-
import mu.KotlinLogging
43
import org.seqra.dataflow.ap.ifds.access.ApManager
54
import org.seqra.dataflow.ap.ifds.access.FinalFactAp
6-
import org.seqra.dataflow.configuration.jvm.AssignMark
7-
import org.seqra.dataflow.configuration.jvm.ContainsMark
5+
import org.seqra.dataflow.configuration.jvm.Position
6+
import org.seqra.dataflow.configuration.jvm.RemoveMark
7+
import org.seqra.dataflow.configuration.jvm.TaintConfigurationItem
8+
import org.seqra.dataflow.configuration.jvm.TaintMark
89
import org.seqra.dataflow.jvm.ap.ifds.CallPositionToJIRValueResolver
9-
import org.seqra.dataflow.jvm.ap.ifds.JIRMarkAwareConditionExpr
1010
import org.seqra.dataflow.jvm.ap.ifds.JIRMarkAwareConditionRewriter
11-
import org.seqra.dataflow.jvm.ap.ifds.removeTrueLiterals
11+
import org.seqra.dataflow.jvm.ap.ifds.taint.EvaluatedCleanAction
1212
import org.seqra.dataflow.jvm.ap.ifds.taint.FinalFactReader
13+
import org.seqra.dataflow.jvm.ap.ifds.taint.TaintCleanActionEvaluator
1314
import org.seqra.dataflow.jvm.ap.ifds.taint.TaintRulesProvider
14-
import org.seqra.dataflow.jvm.ap.ifds.taint.resolveAp
15+
import org.seqra.dataflow.jvm.ap.ifds.taint.UserDefinedRuleInfo
1516
import org.seqra.ir.api.jvm.cfg.JIRAssignInst
1617
import org.seqra.ir.api.jvm.cfg.JIRImmediate
1718
import org.seqra.ir.api.jvm.cfg.JIRInst
@@ -37,61 +38,56 @@ class JIRMethodCallRuleBasedSummaryRewriter(
3738
)
3839
}
3940

40-
private val conditionedActions: List<Pair<List<AssignMark>, JIRMarkAwareConditionExpr?>> by lazy {
41+
private data class UserRuleDefinedAction(
42+
val rule: TaintConfigurationItem,
43+
val positions: List<Position>,
44+
val controlledMarks: Set<String>
45+
)
46+
47+
private val userRuleDefinedActions: List<UserRuleDefinedAction> by lazy {
4148
val method = callExpr.method.method
42-
val sourceRules = config.sourceRulesForMethod(method, statement).toList()
43-
if (sourceRules.isEmpty()) return@lazy emptyList()
44-
45-
val conditionedActions = mutableListOf<Pair<List<AssignMark>, JIRMarkAwareConditionExpr?>>()
46-
47-
for (rule in sourceRules) {
48-
val ruleCondition = rule.condition
49-
val simplifiedCondition = conditionRewriter.rewrite(ruleCondition)
50-
val conditionExpr = when {
51-
simplifiedCondition.isFalse -> continue
52-
simplifiedCondition.isTrue -> null
53-
else -> simplifiedCondition.expr
54-
}
5549

56-
conditionedActions.add(rule.actionsAfter to conditionExpr)
50+
val result = mutableListOf<UserRuleDefinedAction>()
51+
for (sourceRule in config.sourceRulesForMethod(method, statement)) {
52+
val ruleInfo = sourceRule.info as? UserDefinedRuleInfo ?: continue
53+
54+
val simplifiedCondition = conditionRewriter.rewrite(sourceRule.condition)
55+
if (simplifiedCondition.isFalse) continue
56+
57+
val positions = sourceRule.actionsAfter.map { it.position }
58+
result += UserRuleDefinedAction(sourceRule, positions, ruleInfo.relevantTaintMarks)
5759
}
5860

59-
conditionedActions
60-
}
61+
for (cleanRule in config.cleanerRulesForMethod(method, statement)) {
62+
val ruleInfo = cleanRule.info as? UserDefinedRuleInfo ?: continue
6163

62-
fun rewriteSummaryFact(fact: FinalFactAp): Pair<FinalFactAp, FinalFactReader>? {
63-
val factReader = FinalFactReader(fact, apManager)
64-
for ((actions, cond) in conditionedActions) {
65-
val relevantPositiveConditions = hashSetOf<ContainsMark>()
66-
cond?.removeTrueLiterals {
67-
if (!it.negated) {
68-
relevantPositiveConditions.add(it.condition)
69-
}
70-
false
71-
}
64+
val simplifiedCondition = conditionRewriter.rewrite(cleanRule.condition)
65+
if (simplifiedCondition.isFalse) continue
7266

73-
val allRelevantMarks = relevantPositiveConditions.mapTo(hashSetOf()) { it.mark }
67+
val positions = cleanRule.actionsAfter.filterIsInstance<RemoveMark>().map { it.position }
68+
result += UserRuleDefinedAction(cleanRule, positions, ruleInfo.relevantTaintMarks)
69+
}
7470

75-
for (action in actions) {
76-
val markToExclude = allRelevantMarks.toHashSet()
77-
markToExclude.remove(action.mark)
71+
result
72+
}
7873

79-
val pos = action.position.resolveAp()
80-
for (mark in markToExclude) {
81-
if (!factReader.containsPositionWithTaintMark(pos, mark)) {
82-
continue
83-
}
74+
fun rewriteSummaryFact(fact: FinalFactAp): Pair<FinalFactAp, FinalFactReader>? {
75+
val startFactReader = FinalFactReader(fact, apManager)
8476

85-
logger.error("Summary fact handled unproperly due to conflict with rule")
86-
return null
77+
val cleanEvaluator = TaintCleanActionEvaluator()
78+
var cleanedFact = EvaluatedCleanAction.initial(startFactReader)
79+
80+
for (ruleDefinedAction in userRuleDefinedActions) {
81+
val markToExclude = ruleDefinedAction.controlledMarks.map { TaintMark(it) }
82+
for (mark in markToExclude) {
83+
for (pos in ruleDefinedAction.positions) {
84+
val removeAction = RemoveMark(mark, pos)
85+
cleanedFact = cleanEvaluator.evaluate(cleanedFact, ruleDefinedAction.rule, removeAction)
8786
}
8887
}
8988
}
9089

91-
return fact to factReader
92-
}
93-
94-
companion object {
95-
private val logger = KotlinLogging.logger {}
90+
val resultFact = cleanedFact.fact ?: return null
91+
return resultFact.factAp to resultFact
9692
}
9793
}

seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/analysis/JIRMethodCallSummaryHandler.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,21 @@ class JIRMethodCallSummaryHandler(
2929
currentFactAp: FinalFactAp,
3030
summaryEffect: MethodSummaryEdgeApplicationUtils.SummaryEdgeApplication,
3131
summaryFact: FinalFactAp,
32+
createSideEffectRequirement: (refinement: ExclusionSet) -> Sequent?,
3233
handleSummaryEdge: (initialFactRefinement: ExclusionSet?, summaryFactAp: FinalFactAp) -> Sequent
3334
): Set<Sequent> {
3435
val result = hashSetOf<Sequent>()
3536

3637
result += super.handleSummary(
3738
currentFactAp,
3839
summaryEffect,
39-
summaryFact
40+
summaryFact,
41+
createSideEffectRequirement,
4042
) { initialFactRefinement: ExclusionSet?, summaryFactAp: FinalFactAp ->
43+
if (initialFactRefinement != null) {
44+
createSideEffectRequirement(initialFactRefinement)?.also { result.add(it) }
45+
}
46+
4147
analysisContext.aliasAnalysis?.forEachAliasAfterStatement(statement, summaryFactAp) { aliased ->
4248
handleSummaryEdge(initialFactRefinement, aliased)
4349
}

seqra-jvm-dataflow/src/main/kotlin/org/seqra/dataflow/jvm/ap/ifds/taint/TaintEvaluator.kt

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.seqra.dataflow.jvm.ap.ifds.taint
22

3+
import mu.KotlinLogging
34
import org.seqra.dataflow.ap.ifds.AccessPathBase
45
import org.seqra.dataflow.ap.ifds.Accessor
56
import org.seqra.dataflow.ap.ifds.AnyAccessor
@@ -172,7 +173,8 @@ class TaintCleanActionEvaluator {
172173
if (!fact.containsPosition(from)) return evc
173174

174175
if (from !is PositionAccess.Simple) {
175-
TODO("Remove from complex: $from")
176+
logger.error("Unsupported Remove from complex: $from")
177+
return evc
176178
}
177179

178180
val actionInfo = EvaluatedCleanAction.ActionInfo(rule, action)
@@ -190,16 +192,44 @@ class TaintCleanActionEvaluator {
190192

191193
if (!fact.containsPositionWithTaintMark(from, markRestriction)) return evc
192194

193-
if (from !is PositionAccess.Simple) {
194-
TODO("Remove from complex: $from")
195-
}
195+
val cleanAccessors = from.accessorList() + TaintMarkAccessor(markRestriction.name)
196+
val cleanedFacts = clearPosition(cleanAccessors, fact.factAp)
196197

197-
val factWithoutFinal = fact.factAp.clearAccessor(TaintMarkAccessor(markRestriction.name))
198-
val resultFact = factWithoutFinal?.let { fact.replaceFact(it) }
198+
if (cleanedFacts.size > 1) {
199+
logger.error("Unsupported Remove from complex: $from")
200+
return evc
201+
}
199202

203+
val cleanedFact = cleanedFacts.firstOrNull()
204+
val resultFact = cleanedFact?.let { fact.replaceFact(it) }
200205
val actionInfo = EvaluatedCleanAction.ActionInfo(rule, action)
201206
return EvaluatedCleanAction(resultFact, actionInfo, evc)
202207
}
208+
209+
private fun clearPosition(accessors: List<Accessor>, fact: FinalFactAp): List<FinalFactAp> {
210+
val head = accessors.first()
211+
val tail = accessors.drop(1)
212+
if (tail.isEmpty()) {
213+
return listOfNotNull(fact.clearAccessor(head))
214+
}
215+
216+
val remaining = listOfNotNull(fact.clearAccessor(head))
217+
218+
val child = fact.readAccessor(head)
219+
val cleanChild = child?.let { clearPosition(tail, it) }
220+
val cleanChildWithAccessor = cleanChild?.map { it.prependAccessor(head) }
221+
222+
return remaining + cleanChildWithAccessor.orEmpty()
223+
}
224+
225+
private fun PositionAccess.accessorList(): List<Accessor> = when (this) {
226+
is PositionAccess.Simple -> emptyList()
227+
is PositionAccess.Complex -> base.accessorList() + accessor
228+
}
229+
230+
companion object {
231+
private val logger = KotlinLogging.logger {}
232+
}
203233
}
204234

205235
class TaintPassActionPreconditionEvaluator(
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.seqra.dataflow.jvm.ap.ifds.taint
2+
3+
import org.seqra.dataflow.configuration.jvm.serialized.ItemInfo
4+
5+
interface UserDefinedRuleInfo: ItemInfo {
6+
val relevantTaintMarks: Set<String>
7+
}

0 commit comments

Comments
 (0)