undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
217
defects/solc/unit/SolcAssemblyRjumpTest.java
Normal file
217
defects/solc/unit/SolcAssemblyRjumpTest.java
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Unit test for solc-0002: Assembly calculateMaxStackHeight RJUMP target CWE-407.
|
||||
*
|
||||
* Defect: calculateMaxStackHeight() (EOF CFG builder) resolves each RJUMP/CRJUMP
|
||||
* target via std::find(items.begin(), items.end(), item.tag()) — an O(N) scan
|
||||
* over all assembly items per jump. With J jumps over N items, total cost is
|
||||
* O(J × N).
|
||||
*
|
||||
* File: libevmasm/Assembly.cpp:1077
|
||||
* Symbol: calculateMaxStackHeight — `std::find(items.begin(), items.end(), item.tag())`
|
||||
*
|
||||
* Fix: Build an unordered_map<u256, size_t> (tagIndex) once before the
|
||||
* worklist loop, mapping each Tag item's data value to its index.
|
||||
* Each RJUMP resolution becomes O(1). Total cost O(J + N).
|
||||
*
|
||||
* Modeled here in Java:
|
||||
* - AssemblyItem ≡ Item record (type + label)
|
||||
* - tag lookup ≡ linear scan of items[] vs HashMap<Integer,Integer>
|
||||
* - comparisons counted at each scan step
|
||||
*
|
||||
* Expected at J=N=100:
|
||||
* defective ≈ J × N = 10000
|
||||
* fixed ≈ J + N = 200 (index build + lookups)
|
||||
* ratio > 10×
|
||||
*/
|
||||
public class SolcAssemblyRjumpTest {
|
||||
|
||||
static final int TYPE_TAG = 0;
|
||||
static final int TYPE_RJUMP = 1;
|
||||
static final int TYPE_COND_RJUMP = 2;
|
||||
static final int TYPE_OTHER = 3;
|
||||
|
||||
// ── Defective: std::find linear scan per jump ─────────────────────────────
|
||||
|
||||
static class DefectiveStackCalc {
|
||||
final int[] itemTypes; // TYPE_* per item
|
||||
final int[] itemLabels; // label for Tag and Jump items (−1 if not applicable)
|
||||
long comparisons = 0;
|
||||
|
||||
DefectiveStackCalc(int[] itemTypes, int[] itemLabels) {
|
||||
this.itemTypes = itemTypes;
|
||||
this.itemLabels = itemLabels;
|
||||
}
|
||||
|
||||
/** Returns index of the Tag item whose label equals targetLabel, −1 if not found. */
|
||||
int findTag(int targetLabel) {
|
||||
for (int i = 0; i < itemTypes.length; i++) {
|
||||
comparisons++;
|
||||
if (itemTypes[i] == TYPE_TAG && itemLabels[i] == targetLabel)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** Process all jumps, resolving targets via linear scan. */
|
||||
void resolveAllJumps() {
|
||||
for (int idx = 0; idx < itemTypes.length; idx++) {
|
||||
int type = itemTypes[idx];
|
||||
if (type == TYPE_RJUMP || type == TYPE_COND_RJUMP) {
|
||||
int target = itemLabels[idx];
|
||||
int pos = findTag(target);
|
||||
assert pos >= 0 : "Tag not found for label " + target;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Fixed: pre-built HashMap index, O(1) lookup ───────────────────────────
|
||||
|
||||
static class FixedStackCalc {
|
||||
final int[] itemTypes;
|
||||
final int[] itemLabels;
|
||||
final Map<Integer, Integer> tagIndex;
|
||||
long comparisons = 0;
|
||||
|
||||
FixedStackCalc(int[] itemTypes, int[] itemLabels) {
|
||||
this.itemTypes = itemTypes;
|
||||
this.itemLabels = itemLabels;
|
||||
// Build index once: O(N)
|
||||
tagIndex = new HashMap<>();
|
||||
for (int i = 0; i < itemTypes.length; i++) {
|
||||
if (itemTypes[i] == TYPE_TAG) {
|
||||
comparisons++; // count the index-build work
|
||||
tagIndex.put(itemLabels[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void resolveAllJumps() {
|
||||
for (int idx = 0; idx < itemTypes.length; idx++) {
|
||||
int type = itemTypes[idx];
|
||||
if (type == TYPE_RJUMP || type == TYPE_COND_RJUMP) {
|
||||
int target = itemLabels[idx];
|
||||
comparisons++; // O(1) hash lookup
|
||||
Integer pos = tagIndex.get(target);
|
||||
assert pos != null : "Tag not found for label " + target;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Graph builders ────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Build an items array with N tags interleaved with J jumps.
|
||||
* Layout: [TAG_0, OTHER, OTHER, RJUMP→TAG_0, TAG_1, OTHER, RJUMP→TAG_1, …]
|
||||
* Ensures every jump target exists as a Tag item.
|
||||
*/
|
||||
static int[][] buildItems(int N, int J) {
|
||||
// Each tag gets an index slot; jumps are interspersed.
|
||||
// Simple layout: slots 0..N-1 are Tags, slots N..N+J-1 are RJUMPs
|
||||
// targeting tag (slot % N).
|
||||
int total = N + J;
|
||||
int[] types = new int[total];
|
||||
int[] labels = new int[total];
|
||||
|
||||
for (int i = 0; i < N; i++) {
|
||||
types[i] = TYPE_TAG;
|
||||
labels[i] = i; // label == index for simplicity
|
||||
}
|
||||
for (int j = 0; j < J; j++) {
|
||||
types[N + j] = TYPE_RJUMP;
|
||||
labels[N + j] = j % N; // jump to tag j%N
|
||||
}
|
||||
return new int[][]{ types, labels };
|
||||
}
|
||||
|
||||
// ── Simulation helpers ────────────────────────────────────────────────────
|
||||
|
||||
public static long simulateDefective(int N, int J) {
|
||||
int[][] items = buildItems(N, J);
|
||||
DefectiveStackCalc calc = new DefectiveStackCalc(items[0], items[1]);
|
||||
calc.resolveAllJumps();
|
||||
return calc.comparisons;
|
||||
}
|
||||
|
||||
public static long simulateFixed(int N, int J) {
|
||||
int[][] items = buildItems(N, J);
|
||||
FixedStackCalc calc = new FixedStackCalc(items[0], items[1]);
|
||||
calc.resolveAllJumps();
|
||||
return calc.comparisons;
|
||||
}
|
||||
|
||||
// ── Tests ─────────────────────────────────────────────────────────────────
|
||||
|
||||
static void testCorrectnessMatch() {
|
||||
// Both must resolve same targets without assertion failure
|
||||
int N = 10, J = 10;
|
||||
int[][] items = buildItems(N, J);
|
||||
DefectiveStackCalc def = new DefectiveStackCalc(items[0], items[1]);
|
||||
FixedStackCalc fix = new FixedStackCalc(items[0], items[1]);
|
||||
|
||||
// just verify they don't throw
|
||||
def.resolveAllJumps();
|
||||
fix.resolveAllJumps();
|
||||
System.out.println("PASS testCorrectnessMatch");
|
||||
}
|
||||
|
||||
static void testDefectiveGrowsQuadratically() {
|
||||
long prev = -1;
|
||||
for (int S : new int[]{10, 20, 40}) {
|
||||
long c = simulateDefective(S, S);
|
||||
if (prev > 0) {
|
||||
double ratio = (double) c / prev;
|
||||
assert ratio > 3.0
|
||||
: "defective should grow >3x when N=J doubled; got " + ratio + " at N=J=" + S;
|
||||
}
|
||||
prev = c;
|
||||
}
|
||||
System.out.println("PASS testDefectiveGrowsQuadratically");
|
||||
}
|
||||
|
||||
static void testFixedGrowsLinearly() {
|
||||
long prev = -1;
|
||||
for (int S : new int[]{10, 20, 40}) {
|
||||
long c = simulateFixed(S, S);
|
||||
if (prev > 0) {
|
||||
double ratio = (double) c / prev;
|
||||
assert ratio < 2.5
|
||||
: "fixed should grow ~2x when N=J doubled; got " + ratio + " at N=J=" + S;
|
||||
}
|
||||
prev = c;
|
||||
}
|
||||
System.out.println("PASS testFixedGrowsLinearly");
|
||||
}
|
||||
|
||||
static void testRatioAtScale() {
|
||||
int N = 100, J = 100;
|
||||
long defComp = simulateDefective(N, J);
|
||||
long fixComp = simulateFixed(N, J);
|
||||
double ratio = (double) defComp / fixComp;
|
||||
|
||||
// defective: each of J=100 jumps scans up to N+J=200 items before finding tag.
|
||||
// Tags are at positions 0..99, jumps at 100..199; worst-case each jump scans
|
||||
// past all 200 items. In our layout tags are first so average scan is ~N/2.
|
||||
// Minimum expected: J * 1 = 100 comparisons. We assert > 10x fixed.
|
||||
assert ratio > 10.0
|
||||
: "ratio should be >10x at N=J=100; got " + ratio +
|
||||
" (defective=" + defComp + ", fixed=" + fixComp + ")";
|
||||
|
||||
System.out.printf(
|
||||
"PASS testRatioAtScale (defective=%d, fixed=%d, ratio=%.1fx)%n",
|
||||
defComp, fixComp, ratio);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
testCorrectnessMatch();
|
||||
testDefectiveGrowsQuadratically();
|
||||
testFixedGrowsLinearly();
|
||||
testRatioAtScale();
|
||||
System.out.println("All solc-0002 tests passed.");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue