Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
97 commits
Select commit Hold shift + click to select a range
5c41468
Change default mode
Saloed Jul 15, 2026
049cd63
tests
Saloed Jul 15, 2026
259601d
fact ap
Saloed Jul 15, 2026
4ff0b08
Base id edge subsumption
Saloed Jul 15, 2026
4383e3a
Add BaseOnly missed-finding regressions
Saloed Jul 16, 2026
8b60719
Add BaseOnly differential fuzz corpus
Saloed Jul 16, 2026
53f0da9
minor
Saloed Jul 16, 2026
2443d7b
Change fuzz test oracle
Saloed Jul 16, 2026
b26dc6c
Consolidate BaseOnly regression coverage
Saloed Jul 16, 2026
6523419
Fix primitive taint
Saloed Jul 16, 2026
0bcf6e1
Fix
Saloed Jul 16, 2026
99bda76
Retire resolved BaseOnly fuzz regressions
Saloed Jul 16, 2026
9ff8156
Add BaseOnly forward regression fuzz corpus
Saloed Jul 16, 2026
99e8639
Add BaseOnly nested reference regression test
Saloed Jul 16, 2026
cadc7ac
Fix
Saloed Jul 16, 2026
9551e58
test: cover BaseOnly nested factory trace regressions
Saloed Jul 16, 2026
236ea04
test: reduce BaseOnly trace regression to minimal case
Saloed Jul 16, 2026
cef99c3
minor
Saloed Jul 16, 2026
0821496
Optionally enable normalized edges
Saloed Jul 16, 2026
a31d676
fix: preserve BaseOnly field delta during trace resolution
Saloed Jul 16, 2026
3df9d7c
Fix BaseOnly trace resolution for any-field sources
Saloed Jul 17, 2026
6eda3d3
Fix BaseOnly split delta trace resolution
Saloed Jul 17, 2026
780ca1a
Fix concurrency issues
Saloed Jul 18, 2026
d22d6e0
test: cover BaseOnly Stirling and storage regressions
Saloed Jul 19, 2026
2919660
Fix
Saloed Jul 18, 2026
65dff9f
An option to avoid path sampling
Saloed Jul 18, 2026
7d30869
fix
Saloed Jul 18, 2026
3337c45
Remove irrelevant
Saloed Jul 18, 2026
327a8d5
debug
Saloed Jul 18, 2026
6075f76
test: reproduce Stirling summary trace regression
Saloed Jul 20, 2026
271a7bd
Optimize BaseOnly summary storage lookup
Saloed Jul 20, 2026
52e23ca
test: rewrite Stirling dataflow regression
Saloed Jul 21, 2026
de62318
test: reproduce Stirling trace resolution regression
Saloed Jul 21, 2026
64a83fe
test: avoid primitive flow in Stirling regression sample
Saloed Jul 21, 2026
824f082
fix(dataflow): restore layered BaseOnly summaries
Saloed Jul 22, 2026
a85c824
Staged analysis skeleton
Saloed Jul 23, 2026
e025fac
docs(dataflow): design trace action searcher
Saloed Jul 23, 2026
81f01e3
docs(dataflow): refine trace action collection
Saloed Jul 23, 2026
f77f5d1
Add shallow scan phase
Saloed Jul 25, 2026
b3e4047
m
Saloed Jul 25, 2026
a384149
m
Saloed Jul 25, 2026
0148ecd
m
Saloed Jul 25, 2026
375608f
m
Saloed Jul 25, 2026
bb58214
action searcher
Saloed Jul 25, 2026
1fdc785
m
Saloed Jul 25, 2026
b64cd2b
fix
Saloed Jul 25, 2026
40ceb63
m
Saloed Jul 25, 2026
e0a0576
Jir rule selector
Saloed Jul 25, 2026
d489437
Fo rule select
Saloed Jul 25, 2026
e13af77
Usee Tree for the full scan
Saloed Jul 25, 2026
2303eef
Fix cleaners
Saloed Jul 25, 2026
e6eb4c4
FIx rule selection
Saloed Jul 25, 2026
3f2909b
Fix
Saloed Jul 25, 2026
e3573cb
Fix
Saloed Jul 25, 2026
8d956d2
Fix
Saloed Jul 25, 2026
1433e37
Change default
Saloed Jul 25, 2026
7844226
Minor
Saloed Jul 27, 2026
dc880ed
Document BaseOnly summary edge subsumption
Saloed Jul 27, 2026
57f7f6c
Implement BaseOnly summary edge subsumption
Saloed Jul 27, 2026
e76aa7f
Fix BaseOnly summary exclusion merging
Saloed Jul 27, 2026
0648bb6
Design BaseOnly summary edge generalization
Saloed Jul 27, 2026
55027c0
Fix BaseOnly concat across implicit Any
Saloed Jul 27, 2026
259bc82
Subsumes same-premise BaseOnly summary edges
Saloed Jul 27, 2026
d4459a8
Keep BaseOnly fact sets exact during forward analysis
Saloed Jul 27, 2026
a56748a
Minor
Saloed Jul 27, 2026
d79ba72
Allow disabling BaseOnly field generalization
Saloed Jul 27, 2026
3610c6a
Set false by default
Saloed Jul 27, 2026
0922c59
docs
Saloed Jul 29, 2026
bf22153
minor
Saloed Jul 29, 2026
34f8431
variants
Saloed Jul 29, 2026
f1ced9d
variants
Saloed Jul 29, 2026
4c3148f
tmp
Saloed Jul 29, 2026
01e0ef4
Fix
Saloed Jul 30, 2026
0e188e3
Optimize BaseOnly initial fact abstraction
Saloed Jul 31, 2026
1d3254e
Prune irrelevant trace action summaries
Saloed Aug 3, 2026
5b0216c
Fix IFDS phase cancellation isolation
Saloed Aug 4, 2026
9ac1020
Optimize trace rule search by taint marks
Saloed Aug 4, 2026
785ab66
Optimize start trace resolution with over-approximation
Saloed Aug 4, 2026
3b449ac
Skip empty summary delta publications
Saloed Aug 5, 2026
185667c
Wire catch handlers from try boundaries
Saloed Aug 6, 2026
fc06de5
Prioritize zero-to-zero analysis edges
Saloed Aug 6, 2026
6089538
Add sink-only SARIF fingerprint
Saloed Aug 6, 2026
5454fec
Index BaseOnly Z2F summary subscriptions
Saloed Aug 7, 2026
7065937
Propagate side-effect exclusion deltas
Saloed Aug 7, 2026
8e387da
Compact BaseOnly exclusion sets
Saloed Aug 7, 2026
4d9b19e
Mitigate BaseOnly analysis explosion
Saloed Aug 12, 2026
e32645b
Publish concurrent object map writes safely
Saloed Aug 12, 2026
a4482ef
Cache repeated rule-search computations
Saloed Aug 13, 2026
80a5d1c
Select full-scan rules from shallow forward analysis
Saloed Aug 13, 2026
5f2e66c
Add hybrid actionable rule selection fallback
Saloed Aug 14, 2026
c86e17b
Limit shallow rule search time per vulnerability
Saloed Aug 14, 2026
fdbb94f
Filter fallback rules by taint mark reachability
Saloed Aug 14, 2026
00f1404
fix(analyzer): preserve overloaded Spring controller entry points
Saloed Aug 16, 2026
1313249
Restrict fallback reachability to relevant taint marks
Saloed Aug 16, 2026
9b959a7
fix(analyzer): preserve static facts across unresolved calls
Saloed Aug 16, 2026
8536c75
fix(ifds): reset method analyzer scheduling state
Saloed Aug 17, 2026
feb2094
perf(ifds): share context-independent shallow analysis
Saloed Aug 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ private class ConditionSimplifierImpl<A> : CommonConditionVisitor<A, CommonCondi
}

private val conditionSimplifier = ConditionSimplifierImpl<Nothing>()
private val falseCondition: CommonCondition<Nothing> = Not(CommonCondition.True)

@Suppress("UNCHECKED_CAST")
fun <A> conditionSimplifier(): CommonConditionVisitor<A, CommonCondition<A>> =
Expand All @@ -66,7 +67,9 @@ fun <A> conditionSimplifier(): CommonConditionVisitor<A, CommonCondition<A>> =
fun <A> mkTrue(): CommonCondition<A> =
CommonCondition.True as CommonCondition<A>

fun <A> mkFalse(): CommonCondition<A> = Not(mkTrue())
@Suppress("UNCHECKED_CAST")
fun <A> mkFalse(): CommonCondition<A> =
falseCondition as CommonCondition<A>

fun <A> mkOr(conditions: List<CommonCondition<A>>) = when (conditions.size) {
0 -> mkFalse()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.opentaint.dataflow.configuration

import kotlin.test.Test
import kotlin.test.assertSame

class ConditionFactoryTest {
@Test
fun `false condition is shared`() {
val first: Any = mkFalse<String>()
val second: Any = mkFalse<Int>()

assertSame(first, second)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.opentaint.dataflow.util;

import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import org.jetbrains.annotations.Nullable;

/**
* A primitive long map with point reads that tolerate a concurrent rehash.
*
* <p>The supported concurrency model is one writer and any number of readers. Removals are not
* supported. Iteration must use the captured-table helper in {@code MapUtils.kt}; the inherited
* fastutil iterators are not concurrent-read-safe.</p>
*/
public final class ConcurrentReadSafeLong2ObjectMap<V> extends Long2ObjectOpenHashMap<V> {
@Override
public @Nullable V get(long k) {
if (k == 0) {
if (!containsNullKey) return defRetValue;

do {
int n = this.n;
V[] value = this.value;
if (value.length == n + 1) return value[n];
} while (true);
}

while (true) {
long[] key = this.key;
V[] value = this.value;
int n = this.n;

// Capture a matching table generation to allow a read during rehash.
if (key.length != n + 1 || value.length != n + 1) continue;

int mask = n - 1;
int pos = (int) HashCommon.mix(k) & mask;
long curr = key[pos];
if (curr == 0) return defRetValue;

if (k == curr) return value[pos];

// There's always an unused entry.
while (true) {
pos = (pos + 1) & mask;
curr = key[pos];
if (curr == 0) return defRetValue;

if (k == curr) return value[pos];
}
}
}

@Override
public V remove(long k) {
throw new UnsupportedOperationException("Removals are not allowed");
}

public long[] getKeys() {
return this.key;
}

public V[] getValues() {
return this.value;
}

public int getN() {
return this.n;
}

public boolean getContainsNullKey() {
return this.containsNullKey;
}

private static final long serialVersionUID = 0L;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.opentaint.dataflow.util;

import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;

/**
* A primitive long set with point reads that tolerate a concurrent rehash.
*
* <p>The supported concurrency model is one writer and any number of readers. Removals are not
* supported. Iteration must use the captured-table helper in {@code MapUtils.kt}; the inherited
* fastutil iterators are not concurrent-read-safe.</p>
*/
public final class ConcurrentReadSafeLongSet extends LongOpenHashSet {
@Override
public boolean contains(long k) {
if (k == 0) return containsNull;

while (true) {
long[] key = this.key;
int n = this.n;

// Capture one complete table generation to allow a read during rehash.
if (key.length != n + 1) continue;

int mask = n - 1;
int pos = (int) HashCommon.mix(k) & mask;
long curr = key[pos];
if (curr == 0) return false;

if (k == curr) return true;

// There's always an unused entry.
while (true) {
pos = (pos + 1) & mask;
curr = key[pos];
if (curr == 0) return false;

if (k == curr) return true;
}
}
}

@Override
public boolean remove(long k) {
throw new UnsupportedOperationException("Removals are not allowed");
}

public long[] getKeys() {
return this.key;
}

public int getN() {
return this.n;
}

public boolean getContainsNull() {
return this.containsNull;
}

private static final long serialVersionUID = 0L;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,83 @@
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.jetbrains.annotations.Nullable;

/**
* A flat object-to-int map supporting one writer and multiple concurrent point readers.
*
* <p>Writes are published through a sequence counter. Readers retry if a write overlaps their
* lookup, which prevents observing a key before its primitive value or a partially published
* rehash. Removals are not supported.</p>
*/
public final class ConcurrentReadSafeObject2IntMap<K> extends Object2IntOpenHashMap<K> {
public static final int NO_VALUE = -1;

private volatile long writeSequence;

public ConcurrentReadSafeObject2IntMap() {
super();
defaultReturnValue(NO_VALUE);
}

@Override
public int getInt(@Nullable Object k) {
if (k == null) {
if (!containsNullKey) return defRetValue;

do {
int n = this.n;
int[] value = this.value;
if (value.length == n + 1) return value[n];
} while (true);
}

while (true) {
long sequenceBefore = writeSequence;
if ((sequenceBefore & 1) != 0) continue;

K[] key = this.key;
int[] value = this.value;
int n = this.n;
int result = findValue(k, key, value, n);

// capture arrays to allow concurrent reads
if (key.length != n + 1 || value.length != n + 1) continue;
if (sequenceBefore == writeSequence) return result;
}
}

int mask = n - 1;
private int findValue(@Nullable Object k, K[] key, int[] value, int n) {
if (k == null) return containsNullKey ? value[n] : defRetValue;

// The starting point.
int pos = HashCommon.mix(k.hashCode()) & mask;
int mask = n - 1;
int pos = HashCommon.mix(k.hashCode()) & mask;
K curr = key[pos];
if (curr == null) return defRetValue;
if (k.equals(curr)) return value[pos];

K curr = key[pos];
while (true) {
pos = (pos + 1) & mask;
curr = key[pos];
if (curr == null) return defRetValue;

if (k.equals(curr)) return value[pos];
}
}

// There's always an unused entry.
while (true) {
pos = (pos + 1) & mask;

curr = key[pos];
if (curr == null) return defRetValue;
@Override
public int put(K key, int value) {
beginWrite();
try {
return super.put(key, value);
} finally {
endWrite();
}
}

if (k.equals(curr)) return value[pos];
}
@Override
public int putIfAbsent(K key, int value) {
beginWrite();
try {
return super.putIfAbsent(key, value);
} finally {
endWrite();
}
}

private void beginWrite() {
writeSequence++;
}

private void endWrite() {
writeSequence++;
}

@Override
public int removeInt(Object k) {
throw new UnsupportedOperationException("Removals are not allowed");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface AnalysisRunner {
val methodCallResolver: MethodCallResolver

fun enqueueMethodAnalyzer(analyzer: MethodAnalyzer)
fun reprioritizeMethodAnalyzer(analyzer: MethodAnalyzer)
fun registerDelayedAnalyzer(analyzer: MethodAnalyzer)
fun addNewSummaryEdges(methodEntryPoint: MethodEntryPoint, edges: List<Edge>)
fun getPrecalculatedSummaries(methodEntryPoint: MethodEntryPoint): Pair<List<Edge>, List<InitialFactAp>>?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface AnalysisUnitRunnerManager {
fun getOrCreateUnitStorage(unit: UnitType): MethodSummariesUnitStorage?
fun getOrCreateUnitRunner(unit: UnitType): AnalysisRunner?
fun registerMethodCallFromUnit(method: CommonMethod, unit: UnitType)
fun registerResolvedMethodCall(caller: CommonMethod, callee: CommonMethod)

fun handleCrossUnitZeroCall(callerUnit: UnitType, methodEntryPoint: MethodEntryPoint) {
handleCrossUnitAction(callerUnit, methodEntryPoint) {
Expand Down Expand Up @@ -109,6 +110,15 @@ interface AnalysisUnitRunnerManager {
return storage.methodFactToFactSummaryEdges(methodEntryPoint, finalFactBase)
}

fun findFactToFactSummaryEdges(
methodEntryPoint: MethodEntryPoint,
finalFactPattern: FinalFactAp,
): List<Edge.FactToFact> {
val unit = unitResolver.resolve(methodEntryPoint.method)
val storage = getOrCreateUnitStorage(unit) ?: return emptyList()
return storage.methodFactToFactSummaryEdges(methodEntryPoint, finalFactPattern)
}

fun findFactNDSummaryEdges(
methodEntryPoint: MethodEntryPoint,
finalFactBase: AccessPathBase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@ import org.opentaint.dataflow.ap.ifds.access.InitialFactAp
import org.opentaint.ir.api.common.cfg.CommonInst

object EdgeCollection {
class UnprocessedEdgeList(
apManager: ApManager,
methodEntryPoint: MethodEntryPoint,
) {
private val zeroToZeroEdges = arrayListOf<Edge.ZeroToZero>()
private val otherEdges = EdgeList(apManager, methodEntryPoint)

val containsZeroToZeroEdges: Boolean
get() = zeroToZeroEdges.isNotEmpty()

val isEmpty: Boolean
get() = zeroToZeroEdges.isEmpty() && otherEdges.isEmpty

val size: Int
get() = zeroToZeroEdges.size + otherEdges.size

fun add(edge: Edge) {
if (edge is Edge.ZeroToZero) {
zeroToZeroEdges.add(edge)
} else {
otherEdges.add(edge)
}
}

fun removeLast(): Edge =
zeroToZeroEdges.removeLastOrNull() ?: otherEdges.removeLast()
}

class EdgeList(
private val apManager: ApManager,
private val methodEntryPoint: MethodEntryPoint
Expand Down
Loading
Loading