wave17 complete: systemd/emacs/vim/qemu/tcl/kafka-0007/spark-0004 + 559/240
This commit is contained in:
parent
4221966e66
commit
cce7ec653a
32 changed files with 3007 additions and 5 deletions
196
defects/systemd/unit/SystemdUnitFileGetListTest.java
Normal file
196
defects/systemd/unit/SystemdUnitFileGetListTest.java
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* CWE-407 unit test: systemd-0002
|
||||
*
|
||||
* unit_file_get_list() calls strv_contains(states, unit_file_state_to_string(state))
|
||||
* for every unit file found on disk. strv_contains is O(S) linear scan over the
|
||||
* states filter array. Total cost: O(U × S) where U = unit files, S = state filters.
|
||||
*
|
||||
* Fix: build a HashSet from the states array before the loop — O(1) lookup per unit.
|
||||
*/
|
||||
public class SystemdUnitFileGetListTest {
|
||||
|
||||
// Enum representing systemd UnitFileState values
|
||||
enum UnitFileState {
|
||||
ENABLED, DISABLED, STATIC, MASKED, LINKED, INDIRECT,
|
||||
ENABLED_RUNTIME, LINKED_RUNTIME, ALIAS, GENERATED,
|
||||
TRANSIENT, BAD
|
||||
}
|
||||
|
||||
static UnitFileState[] ALL_STATES = UnitFileState.values();
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// SLOW path: simulates the defective strv_contains check per unit file
|
||||
// -----------------------------------------------------------------------
|
||||
static long slowFilter(String[] unitFiles, String[] states) {
|
||||
long ops = 0;
|
||||
List<String> matched = new ArrayList<>();
|
||||
for (String unit : unitFiles) {
|
||||
// Assign a state (deterministic from unit name hash)
|
||||
UnitFileState state = ALL_STATES[Math.abs(unit.hashCode()) % ALL_STATES.length];
|
||||
String stateStr = state.name().toLowerCase();
|
||||
|
||||
// O(S) linear scan — strv_contains equivalent
|
||||
boolean found = false;
|
||||
for (String s : states) {
|
||||
ops++;
|
||||
if (stateStr.equals(s)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) matched.add(unit);
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// FAST path: HashSet built once, O(1) lookup per unit
|
||||
// -----------------------------------------------------------------------
|
||||
static long fastFilter(String[] unitFiles, String[] states) {
|
||||
long ops = 0;
|
||||
Set<String> stateSet = new HashSet<>(Arrays.asList(states));
|
||||
List<String> matched = new ArrayList<>();
|
||||
for (String unit : unitFiles) {
|
||||
UnitFileState state = ALL_STATES[Math.abs(unit.hashCode()) % ALL_STATES.length];
|
||||
String stateStr = state.name().toLowerCase();
|
||||
ops++; // single hash lookup
|
||||
if (stateSet.contains(stateStr)) matched.add(unit);
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -----------------------------------------------------------------------
|
||||
static String[] makeUnits(int n) {
|
||||
String[] a = new String[n];
|
||||
for (int i = 0; i < n; i++) a[i] = "unit-" + i + ".service";
|
||||
return a;
|
||||
}
|
||||
|
||||
static String[] makeStates(int s) {
|
||||
String[] a = new String[s];
|
||||
String[] names = {"enabled", "disabled", "static", "masked", "linked"};
|
||||
for (int i = 0; i < s; i++) a[i] = names[i % names.length];
|
||||
return a;
|
||||
}
|
||||
|
||||
// Count matching results identically for both paths for correctness check
|
||||
static Set<String> matchedSet(String[] unitFiles, String[] states) {
|
||||
Set<String> stateSet = new HashSet<>(Arrays.asList(states));
|
||||
Set<String> matched = new LinkedHashSet<>();
|
||||
for (String unit : unitFiles) {
|
||||
UnitFileState state = ALL_STATES[Math.abs(unit.hashCode()) % ALL_STATES.length];
|
||||
if (stateSet.contains(state.name().toLowerCase())) matched.add(unit);
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Tests
|
||||
// -----------------------------------------------------------------------
|
||||
static int pass = 0;
|
||||
static int fail = 0;
|
||||
|
||||
static void check(String name, boolean condition) {
|
||||
if (condition) {
|
||||
System.out.println("PASS: " + name);
|
||||
pass++;
|
||||
} else {
|
||||
System.out.println("FAIL: " + name);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
// Slow path variant that always scans full list (no early exit) to measure worst case
|
||||
static long slowFilterWorstCase(String[] unitFiles, String[] states) {
|
||||
long ops = 0;
|
||||
for (String unit : unitFiles) {
|
||||
// Assign a state that never matches any filter (simulates "bad"/"transient" units)
|
||||
String stateStr = "bad"; // last state, never in typical filter lists
|
||||
for (String s : states) {
|
||||
ops++;
|
||||
if (stateStr.equals(s)) break; // never breaks — scans all S
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
static long fastFilterWorstCase(String[] unitFiles, String[] states) {
|
||||
long ops = 0;
|
||||
Set<String> stateSet = new HashSet<>(Arrays.asList(states));
|
||||
for (String unit : unitFiles) {
|
||||
ops++; // one hash lookup per unit, regardless of match
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
static void testCase(int numUnits, int numStates) {
|
||||
String[] units = makeUnits(numUnits);
|
||||
String[] states = makeStates(numStates);
|
||||
|
||||
// Worst case: every unit has state NOT in filter, slow path scans all S per unit
|
||||
long slowOps = slowFilterWorstCase(units, states);
|
||||
long fastOps = fastFilterWorstCase(units, states);
|
||||
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
System.out.printf(" U=%d S=%d: slowOps=%d fastOps=%d ratio=%.1fx%n",
|
||||
numUnits, numStates, slowOps, fastOps, ratio);
|
||||
check("U=" + numUnits + " S=" + numStates + ": slowOps == U*S",
|
||||
slowOps == (long) numUnits * numStates);
|
||||
check("U=" + numUnits + " S=" + numStates + ": fastOps == numUnits", fastOps == numUnits);
|
||||
check("U=" + numUnits + " S=" + numStates + ": slowOps/fastOps >= 5x", ratio >= 5.0);
|
||||
}
|
||||
|
||||
static void testCorrectness() {
|
||||
// Small case: 6 units, filter for "enabled" and "static"
|
||||
String[] units = new String[12];
|
||||
for (int i = 0; i < 12; i++) units[i] = "svc-" + i + ".service";
|
||||
String[] states = {"enabled", "static"};
|
||||
|
||||
// Run both paths and compare matched counts
|
||||
Set<String> expected = matchedSet(units, states);
|
||||
|
||||
// Slow path result
|
||||
Set<String> slowMatched = new LinkedHashSet<>();
|
||||
Set<String> stateSet = new HashSet<>(Arrays.asList(states));
|
||||
for (String unit : units) {
|
||||
UnitFileState state = ALL_STATES[Math.abs(unit.hashCode()) % ALL_STATES.length];
|
||||
String stateStr = state.name().toLowerCase();
|
||||
for (String s : states) {
|
||||
if (stateStr.equals(s)) { slowMatched.add(unit); break; }
|
||||
}
|
||||
}
|
||||
|
||||
check("correctness: slow matches expected", slowMatched.equals(expected));
|
||||
|
||||
// Fast path result
|
||||
Set<String> fastMatched = new LinkedHashSet<>();
|
||||
for (String unit : units) {
|
||||
UnitFileState state = ALL_STATES[Math.abs(unit.hashCode()) % ALL_STATES.length];
|
||||
if (stateSet.contains(state.name().toLowerCase())) fastMatched.add(unit);
|
||||
}
|
||||
check("correctness: fast matches expected", fastMatched.equals(expected));
|
||||
check("correctness: slow equals fast", slowMatched.equals(fastMatched));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Main
|
||||
// -----------------------------------------------------------------------
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== systemd-0002: unit_file_get_list states filter O(U*S) → O(U) ===");
|
||||
|
||||
testCorrectness();
|
||||
testCase(1000, 5);
|
||||
testCase(2000, 5);
|
||||
testCase(5000, 10);
|
||||
|
||||
System.out.println();
|
||||
System.out.printf("%d/%d PASS%n", pass, pass + fail);
|
||||
if (fail > 0) System.exit(1);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue