whitepaper: 398/185 — wave6d (rails-0012..16, jsc-0001/2, vtk, sm-0002, redis/valkey-0003, helm-0002/3, k8s-0003)
This commit is contained in:
parent
eb9612e4bf
commit
3735145aa5
47 changed files with 3488 additions and 33 deletions
382
defects/linux/unit/Linux0005Test.java
Normal file
382
defects/linux/unit/Linux0005Test.java
Normal file
|
|
@ -0,0 +1,382 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Linux0005Test — CWE-407 benchmark for linux-0005 and linux-0006
|
||||
*
|
||||
* linux-0005 (COMPONENT_FIND_QUADRATIC):
|
||||
* Models drivers/base/component.c find_components():
|
||||
* SLOW: for each adev [O(A)]:
|
||||
* for each match entry [O(M)]:
|
||||
* find_component() scans component_list [O(C)]
|
||||
* Total: O(A × M × C) per component_add()
|
||||
* With N component_add() calls at boot: O(N × A × M × C) = O(N²) boot cost
|
||||
* FAST: hash table keyed on dev pointer — O(1) lookup
|
||||
* Total per component_add(): O(A × M)
|
||||
*
|
||||
* linux-0006 (BTF_MODULE_SCAN_LINEAR):
|
||||
* Models kernel/bpf/btf.c bpf_find_btf_id():
|
||||
* SLOW: idr_for_each_entry() scans all loaded module BTFs [O(M)] per kptr field
|
||||
* A BPF map struct with F kptr fields → O(F × M) per BPF_MAP_CREATE syscall
|
||||
* Kernel comment: "linear search could be slow"
|
||||
* FAST: hash table keyed on (name_hash ^ kind) — O(1) amortised after first miss
|
||||
* O(F) per BPF_MAP_CREATE after cache warm-up
|
||||
*/
|
||||
public class Linux0005Test {
|
||||
|
||||
// =========================================================================
|
||||
// linux-0005: component find_components O(A×M×C) vs O(A×M)
|
||||
// =========================================================================
|
||||
|
||||
/** Simulates struct component — one registered device component. */
|
||||
static class Component {
|
||||
final Object dev; // device pointer (any Object — we use identity)
|
||||
Object boundAdev; // null if unbound
|
||||
Component(Object dev) { this.dev = dev; }
|
||||
}
|
||||
|
||||
/** One entry in a match array — holds the dev pointer to find. */
|
||||
static class MatchEntry {
|
||||
final Object devWanted; // the device this entry is looking for
|
||||
Component component; // filled in when found
|
||||
MatchEntry(Object devWanted) { this.devWanted = devWanted; }
|
||||
}
|
||||
|
||||
/** Simulates struct aggregate_device. */
|
||||
static class AggDev {
|
||||
final MatchEntry[] match;
|
||||
AggDev(MatchEntry[] match) { this.match = match; }
|
||||
}
|
||||
|
||||
/**
|
||||
* SLOW: find_component() — O(C) linear scan of component_list.
|
||||
* Returns number of comparisons performed.
|
||||
*/
|
||||
static long findComponent_slow(List<Component> componentList,
|
||||
AggDev adev,
|
||||
MatchEntry mc) {
|
||||
long ops = 0;
|
||||
for (Component c : componentList) {
|
||||
ops++;
|
||||
if (c.boundAdev != null && c.boundAdev != adev)
|
||||
continue;
|
||||
// mc->compare(c->dev, mc->data) — identity comparison
|
||||
if (c.dev == mc.devWanted) {
|
||||
return ops;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* SLOW: find_components() — calls find_component() M times per adev.
|
||||
* Outer loop over adevs: O(A × M × C).
|
||||
* Returns total comparison count.
|
||||
*/
|
||||
static long findComponents_slow(List<Component> componentList,
|
||||
List<AggDev> adevList) {
|
||||
long ops = 0;
|
||||
for (AggDev adev : adevList) {
|
||||
for (MatchEntry mc : adev.match) {
|
||||
if (mc.component != null) continue;
|
||||
ops += findComponent_slow(componentList, adev, mc);
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* FAST: hash table (IdentityHashMap as O(1) lookup) keyed on dev pointer.
|
||||
* find_component() becomes a single map.get() call.
|
||||
* Returns total comparison count (always 1 per match entry for a hit).
|
||||
*/
|
||||
static long findComponents_fast(Map<Object, Component> componentMap,
|
||||
List<AggDev> adevList) {
|
||||
long ops = 0;
|
||||
for (AggDev adev : adevList) {
|
||||
for (MatchEntry mc : adev.match) {
|
||||
if (mc.component != null) continue;
|
||||
ops++; // one hash probe
|
||||
Component c = componentMap.get(mc.devWanted);
|
||||
if (c != null && (c.boundAdev == null || c.boundAdev == adev)) {
|
||||
// found
|
||||
}
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// linux-0006: bpf_find_btf_id O(F×M) vs O(F) with cache
|
||||
// =========================================================================
|
||||
|
||||
/** Simulates one module BTF — holds a flat array of type names. */
|
||||
static class ModuleBtf {
|
||||
final String moduleName;
|
||||
final String[] typeNames;
|
||||
ModuleBtf(String moduleName, String[] typeNames) {
|
||||
this.moduleName = moduleName;
|
||||
this.typeNames = typeNames;
|
||||
}
|
||||
/** O(T) linear scan — btf_find_by_name_kind for module BTF. */
|
||||
int findByNameKind(String name, int kind) {
|
||||
for (int i = 0; i < typeNames.length; i++) {
|
||||
if (typeNames[i].equals(name)) return i + 1; // positive id
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SLOW: bpf_find_btf_id() — idr_for_each_entry over all module BTFs.
|
||||
* For each kptr field: scan M module BTFs → O(F × M × T).
|
||||
* Returns number of (module-BTF, field) scan iterations.
|
||||
*/
|
||||
static long findBtfId_slow(List<ModuleBtf> moduleBtfs,
|
||||
String[] kptrFieldNames,
|
||||
int kind) {
|
||||
long ops = 0;
|
||||
for (String fieldName : kptrFieldNames) {
|
||||
// idr_for_each_entry walks all M module BTFs
|
||||
for (ModuleBtf mbtf : moduleBtfs) {
|
||||
ops++;
|
||||
int id = mbtf.findByNameKind(fieldName, kind);
|
||||
if (id > 0) break; // found — stop scanning
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
* FAST: name→id hash cache (HashMap as O(1) lookup).
|
||||
* First lookup for a name misses and populates the cache; subsequent
|
||||
* lookups are O(1). Returns total module-BTF iterations across all fields.
|
||||
*
|
||||
* Simulates: check vmlinux (O(log T) bsearch, modelled as O(1)),
|
||||
* then check cache (O(1)), then fall through to O(M) scan on miss.
|
||||
*/
|
||||
static long findBtfId_fast(List<ModuleBtf> moduleBtfs,
|
||||
String[] kptrFieldNames,
|
||||
int kind,
|
||||
Map<String, Integer> cache) {
|
||||
long ops = 0;
|
||||
for (String fieldName : kptrFieldNames) {
|
||||
String cacheKey = fieldName + ":" + kind;
|
||||
if (cache.containsKey(cacheKey)) {
|
||||
ops++; // O(1) cache hit
|
||||
continue;
|
||||
}
|
||||
// Cache miss — scan modules (first time only)
|
||||
for (ModuleBtf mbtf : moduleBtfs) {
|
||||
ops++;
|
||||
int id = mbtf.findByNameKind(fieldName, kind);
|
||||
if (id > 0) {
|
||||
cache.put(cacheKey, id); // populate cache
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Harness
|
||||
// =========================================================================
|
||||
|
||||
static void bench(String label, long sOps, long fOps, long minRatio) {
|
||||
double ratio = fOps == 0 ? Double.MAX_VALUE : (double) sOps / fOps;
|
||||
boolean pass = ratio >= minRatio;
|
||||
System.out.printf(" %-60s slow=%,d fast=%,d ratio=%.1fx [%s]%n",
|
||||
label, sOps, fOps, ratio, pass ? "PASS" : "FAIL");
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// main
|
||||
// =========================================================================
|
||||
|
||||
public static void main(String[] args) {
|
||||
int passed = 0, total = 0;
|
||||
|
||||
System.out.println("Linux0005Test — CWE-407 (linux-0005 component, linux-0006 btf)");
|
||||
System.out.println("=".repeat(76));
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// linux-0005: component find_components quadratic
|
||||
// ------------------------------------------------------------------
|
||||
System.out.println("\nlinux-0005: component find_components O(A×M×C) vs O(A×M)");
|
||||
{
|
||||
// Realistic SoC: 80 components, 6 aggregate devices, 8 match entries each
|
||||
int C = 80, A = 6, M = 8;
|
||||
List<Object> devPtrs = new ArrayList<>(C);
|
||||
for (int i = 0; i < C; i++) devPtrs.add(new Object());
|
||||
|
||||
List<Component> componentList = new ArrayList<>(C);
|
||||
Map<Object, Component> componentMap = new IdentityHashMap<>(C * 2);
|
||||
for (Object dev : devPtrs) {
|
||||
Component comp = new Component(dev);
|
||||
componentList.add(comp);
|
||||
componentMap.put(dev, comp);
|
||||
}
|
||||
|
||||
// Each adev matches the last M devices (worst-case: found at end of list)
|
||||
List<AggDev> adevList = new ArrayList<>(A);
|
||||
for (int a = 0; a < A; a++) {
|
||||
MatchEntry[] matches = new MatchEntry[M];
|
||||
for (int m = 0; m < M; m++) {
|
||||
// Point at tail of the component list → worst case for linear scan
|
||||
matches[m] = new MatchEntry(devPtrs.get(C - 1 - m));
|
||||
}
|
||||
adevList.add(new AggDev(matches));
|
||||
}
|
||||
|
||||
// Simulate N component_add() events — each triggers find_components on all adevs
|
||||
int N = 80;
|
||||
long slowTotal = 0, fastTotal = 0;
|
||||
for (int n = 0; n < N; n++) {
|
||||
// Clear bound state so all matches are re-evaluated
|
||||
for (AggDev ad : adevList)
|
||||
for (MatchEntry me : ad.match) me.component = null;
|
||||
slowTotal += findComponents_slow(componentList, adevList);
|
||||
}
|
||||
for (int n = 0; n < N; n++) {
|
||||
for (AggDev ad : adevList)
|
||||
for (MatchEntry me : ad.match) me.component = null;
|
||||
fastTotal += findComponents_fast(componentMap, adevList);
|
||||
}
|
||||
|
||||
// Expected: slow = N × A × M × avg_scan = 80 × 6 × 8 × ~(C/2) ≈ 153600
|
||||
// fast = N × A × M × 1 = 80 × 6 × 8 = 3840
|
||||
// Ratio ≈ C/2 = 40x
|
||||
long expectedMinRatio = Math.max(5L, (long)(C / 4));
|
||||
bench(String.format("SoC boot C=%d A=%d M=%d N=%d component_add events", C, A, M, N),
|
||||
slowTotal, fastTotal, expectedMinRatio);
|
||||
total++;
|
||||
if (slowTotal > fastTotal * expectedMinRatio) passed++;
|
||||
}
|
||||
{
|
||||
// Large display controller: 200 components, 12 adevs, 15 match entries
|
||||
int C = 200, A = 12, M = 15;
|
||||
List<Object> devPtrs = new ArrayList<>(C);
|
||||
for (int i = 0; i < C; i++) devPtrs.add(new Object());
|
||||
|
||||
List<Component> componentList = new ArrayList<>(C);
|
||||
Map<Object, Component> componentMap = new IdentityHashMap<>(C * 2);
|
||||
for (Object dev : devPtrs) {
|
||||
Component comp = new Component(dev);
|
||||
componentList.add(comp);
|
||||
componentMap.put(dev, comp);
|
||||
}
|
||||
|
||||
List<AggDev> adevList = new ArrayList<>(A);
|
||||
for (int a = 0; a < A; a++) {
|
||||
MatchEntry[] matches = new MatchEntry[M];
|
||||
for (int m = 0; m < M; m++)
|
||||
matches[m] = new MatchEntry(devPtrs.get(C - 1 - m));
|
||||
adevList.add(new AggDev(matches));
|
||||
}
|
||||
|
||||
int N = 200;
|
||||
long slowTotal = 0, fastTotal = 0;
|
||||
for (int n = 0; n < N; n++) {
|
||||
for (AggDev ad : adevList)
|
||||
for (MatchEntry me : ad.match) me.component = null;
|
||||
slowTotal += findComponents_slow(componentList, adevList);
|
||||
}
|
||||
for (int n = 0; n < N; n++) {
|
||||
for (AggDev ad : adevList)
|
||||
for (MatchEntry me : ad.match) me.component = null;
|
||||
fastTotal += findComponents_fast(componentMap, adevList);
|
||||
}
|
||||
|
||||
long expectedMinRatio = Math.max(5L, (long)(C / 4));
|
||||
bench(String.format("Display ctrl C=%d A=%d M=%d N=%d component_add events", C, A, M, N),
|
||||
slowTotal, fastTotal, expectedMinRatio);
|
||||
total++;
|
||||
if (slowTotal > fastTotal * expectedMinRatio) passed++;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// linux-0006: bpf_find_btf_id O(F×M) vs O(F) with cache
|
||||
// ------------------------------------------------------------------
|
||||
System.out.println("\nlinux-0006: bpf_find_btf_id O(F×M) vs O(F) with hash cache");
|
||||
{
|
||||
// 64 loaded kernel modules, BPF map struct with 10 kptr fields
|
||||
int M = 64, F = 10;
|
||||
int KIND = 22; // BTF_KIND_STRUCT
|
||||
|
||||
// Build module BTFs — the target type lives in the last module (worst case)
|
||||
List<ModuleBtf> moduleBtfs = new ArrayList<>(M);
|
||||
String[] kptrNames = new String[F];
|
||||
for (int f = 0; f < F; f++) kptrNames[f] = "kptr_type_" + f;
|
||||
|
||||
for (int m = 0; m < M; m++) {
|
||||
String[] types;
|
||||
if (m == M - 1) {
|
||||
// Last module holds all target types
|
||||
types = Arrays.copyOf(kptrNames, F);
|
||||
} else {
|
||||
types = new String[]{"unrelated_type_" + m};
|
||||
}
|
||||
moduleBtfs.add(new ModuleBtf("module_" + m, types));
|
||||
}
|
||||
|
||||
// Simulate 500 BPF_MAP_CREATE syscalls — each re-scans all kptr fields
|
||||
int SYSCALLS = 500;
|
||||
long slowTotal = 0, fastTotal = 0;
|
||||
|
||||
for (int s = 0; s < SYSCALLS; s++)
|
||||
slowTotal += findBtfId_slow(moduleBtfs, kptrNames, KIND);
|
||||
|
||||
Map<String, Integer> cache = new HashMap<>();
|
||||
for (int s = 0; s < SYSCALLS; s++)
|
||||
fastTotal += findBtfId_fast(moduleBtfs, kptrNames, KIND, cache);
|
||||
|
||||
// slow: SYSCALLS × F × avg_M_scanned = 500 × 10 × 64 = 320000
|
||||
// fast: first call = 500 × 10 × 64 (cold), subsequent = SYSCALLS-1 × F × 1
|
||||
// ≈ 10 × 64 + 499 × 10 = 640 + 4990 = 5630 total for F=10 fields
|
||||
// (cache warms on first SYSCALL, rest are O(F) hits)
|
||||
// Actual fast ≈ F*M + (SYSCALLS-1)*F = 640+4990 = 5630
|
||||
// Ratio ≈ 320000/5630 ≈ 56x
|
||||
bench(String.format("BPF kptr M=%d modules F=%d fields SYSCALLS=%d", M, F, SYSCALLS),
|
||||
slowTotal, fastTotal, 10L);
|
||||
total++;
|
||||
if (slowTotal > fastTotal * 10L) passed++;
|
||||
}
|
||||
{
|
||||
// 200 modules, struct with 25 kptr fields, 1000 map-create events
|
||||
int M = 200, F = 25, SYSCALLS = 1000;
|
||||
int KIND = 22;
|
||||
|
||||
List<ModuleBtf> moduleBtfs = new ArrayList<>(M);
|
||||
String[] kptrNames = new String[F];
|
||||
for (int f = 0; f < F; f++) kptrNames[f] = "heavy_kptr_" + f;
|
||||
|
||||
for (int m = 0; m < M; m++) {
|
||||
String[] types = (m == M - 1)
|
||||
? Arrays.copyOf(kptrNames, F)
|
||||
: new String[]{"stub_" + m};
|
||||
moduleBtfs.add(new ModuleBtf("mod_" + m, types));
|
||||
}
|
||||
|
||||
long slowTotal = 0, fastTotal = 0;
|
||||
for (int s = 0; s < SYSCALLS; s++)
|
||||
slowTotal += findBtfId_slow(moduleBtfs, kptrNames, KIND);
|
||||
|
||||
Map<String, Integer> cache = new HashMap<>();
|
||||
for (int s = 0; s < SYSCALLS; s++)
|
||||
fastTotal += findBtfId_fast(moduleBtfs, kptrNames, KIND, cache);
|
||||
|
||||
// slow: 1000 × 25 × 200 = 5,000,000
|
||||
// fast: first miss = 25×200=5000, then 999×25=24975 → ≈ 30000
|
||||
// ratio ≈ 166x
|
||||
bench(String.format("BPF kptr M=%d modules F=%d fields SYSCALLS=%d", M, F, SYSCALLS),
|
||||
slowTotal, fastTotal, 20L);
|
||||
total++;
|
||||
if (slowTotal > fastTotal * 20L) passed++;
|
||||
}
|
||||
|
||||
System.out.println("\n" + passed + "/" + total + " PASS");
|
||||
if (passed < total) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue