package unit; import java.util.*; /** * Linux0007Test — CWE-407 benchmark for linux-0007 and linux-0008 * * linux-0007 (PKTGEN_THREAD_DEV_XARRAY): * Models net/core/pktgen.c __pktgen_NN_threads() and pktgen_change_name(): * SLOW: for each thread [O(T)]: * pktgen_find_dev() scans if_list [O(D)] per thread * Total: O(T × D) per device lookup or rename event * FAST: xarray/HashMap keyed by net_device* — O(1) lookup * Total: O(1) per lookup * * linux-0008 (TASKSTATS_LISTENER_HASHSET): * Models kernel/taskstats.c add_del_listener() REGISTER path: * SLOW: for each CPU in mask [O(C)]: * list_for_each_entry over listener_array [O(L)] to check pid exists * Total: O(C × L) per TASKSTATS_CMD_ATTR_REGISTER_CPUMASK call * FAST: per-CPU hash table keyed by pid — O(1) membership test * Total: O(C) per registration */ public class Linux0007Test { // ========================================================================= // linux-0007: pktgen __pktgen_NN_threads O(T×D) vs O(1) // ========================================================================= /** Simulates struct pktgen_dev — one device managed by pktgen. */ static class PktgenDev { final String devName; // odevname final Object netdev; // odev pointer (net_device*) PktgenDev(String devName, Object netdev) { this.devName = devName; this.netdev = netdev; } } /** Simulates struct pktgen_thread — one pktgen thread managing a device list. */ static class PktgenThread { final List ifList = new ArrayList<>(); } /** * SLOW: __pktgen_NN_threads — O(T × D) double list scan. * Outer: iterate all threads. Inner: scan each thread's if_list by name. * @return count of inner loop iterations (models computational work) */ static long pktgenFindDev_slow(List threads, String targetName, long[] iterOut) { long iters = 0; PktgenDev found = null; for (PktgenThread t : threads) { for (PktgenDev dev : t.ifList) { iters++; if (dev.devName.equals(targetName)) { found = dev; break; } } if (found != null) break; } iterOut[0] += iters; return iters; } /** * FAST: O(1) HashMap lookup by device name. * Simulates xarray/hashtable keyed by net_device* or name. */ static long pktgenFindDev_fast(Map devMap, String targetName, long[] iterOut) { iterOut[0] += 1; // single hash lookup = O(1) return devMap.containsKey(targetName) ? 1 : 1; } /** * SLOW: pktgen_change_name — O(T × D) double list scan for net_device* match. */ static long pktgenChangeName_slow(List threads, Object targetNetdev, String newName, long[] iterOut) { long iters = 0; for (PktgenThread t : threads) { for (PktgenDev dev : t.ifList) { iters++; if (dev.netdev == targetNetdev) { // proc_remove + proc_create_data (simulated) break; } } } iterOut[0] += iters; return iters; } /** * FAST: pktgen_change_name with xarray — O(1) lookup by net_device*. */ static long pktgenChangeName_fast(Map devByNetdev, Object targetNetdev, String newName, long[] iterOut) { iterOut[0] += 1; return 1; } // ========================================================================= // linux-0008: taskstats add_del_listener O(C×L) vs O(C) // ========================================================================= /** Simulates struct listener — one registered taskstats listener (pid). */ static class Listener { final int pid; boolean valid; Listener(int pid) { this.pid = pid; this.valid = true; } } /** Simulates struct listener_array — per-CPU listener list. */ static class ListenerArray { final List list = new ArrayList<>(); } /** * SLOW: add_del_listener REGISTER path — O(C × L). * For each CPU in cpumask: scan listener list to check if pid already registered. * @return total inner loop iterations */ static long registerListener_slow(ListenerArray[] perCpuListeners, boolean[] cpuMask, int newPid, long[] iterOut) { long iters = 0; for (int cpu = 0; cpu < cpuMask.length; cpu++) { if (!cpuMask[cpu]) continue; ListenerArray la = perCpuListeners[cpu]; boolean exists = false; for (Listener l : la.list) { // O(L) linear scan iters++; if (l.pid == newPid && l.valid) { exists = true; break; } } if (!exists) { la.list.add(new Listener(newPid)); } } iterOut[0] += iters; return iters; } /** * FAST: add_del_listener with per-CPU HashSet — O(C). * hash_for_each_possible over DECLARE_HASHTABLE → O(1) pid lookup per CPU. */ static long registerListener_fast(ListenerArray[] perCpuListeners, Set[] perCpuPidSets, boolean[] cpuMask, int newPid, long[] iterOut) { long iters = 0; for (int cpu = 0; cpu < cpuMask.length; cpu++) { if (!cpuMask[cpu]) continue; iters++; // one O(1) hash lookup per CPU if (!perCpuPidSets[cpu].contains(newPid)) { perCpuListeners[cpu].list.add(new Listener(newPid)); perCpuPidSets[cpu].add(newPid); } } iterOut[0] += iters; return iters; } // ========================================================================= // main // ========================================================================= public static void main(String[] args) { System.out.println("Linux0007Test — CWE-407 (linux-0007 pktgen, linux-0008 taskstats)"); System.out.println("=".repeat(72)); // ------------------------------------------------------------------ // linux-0007: pktgen find/rename O(T×D) vs O(1) // ------------------------------------------------------------------ System.out.println("\nlinux-0007: pktgen __pktgen_NN_threads O(T×D) vs O(1)"); { int T = 10; // pktgen threads (one per CPU in practice) int D = 100; // devices per thread List threads = new ArrayList<>(T); Map devByName = new HashMap<>(T * D * 2); Map devByNetdev = new IdentityHashMap<>(T * D * 2); // Build: each thread owns D devices, target device is in last thread for (int t = 0; t < T; t++) { PktgenThread thread = new PktgenThread(); for (int d = 0; d < D; d++) { String name = "eth" + (t * D + d); Object netdev = new Object(); PktgenDev dev = new PktgenDev(name, netdev); thread.ifList.add(dev); devByName.put(name, dev); devByNetdev.put(netdev, dev); } threads.add(thread); } // Target: last device in last thread (worst case for linear scan) PktgenThread lastThread = threads.get(T - 1); PktgenDev targetDev = lastThread.ifList.get(D - 1); String targetName = targetDev.devName; Object targetNetdev = targetDev.netdev; int LOOKUPS = 200; // pktgenFindDev: slow vs fast long[] sLookupIter = {0}, fLookupIter = {0}; for (int i = 0; i < LOOKUPS; i++) { pktgenFindDev_slow(threads, targetName, sLookupIter); pktgenFindDev_fast(devByName, targetName, fLookupIter); } System.out.printf(" find T=%d D=%d × %d lookups: slow=%d fast=%d%n", T, D, LOOKUPS, sLookupIter[0], fLookupIter[0]); assert sLookupIter[0] > fLookupIter[0] * 5 : "FAIL linux-0007 find: slow=" + sLookupIter[0] + " fast=" + fLookupIter[0]; System.out.println(" PASS (find slowpath >> fastpath)"); // pktgenChangeName: slow vs fast long[] sRenameIter = {0}, fRenameIter = {0}; int RENAMES = 200; for (int i = 0; i < RENAMES; i++) { pktgenChangeName_slow(threads, targetNetdev, "eth_renamed", sRenameIter); pktgenChangeName_fast(devByNetdev, targetNetdev, "eth_renamed", fRenameIter); } System.out.printf(" rename T=%d D=%d × %d events: slow=%d fast=%d%n", T, D, RENAMES, sRenameIter[0], fRenameIter[0]); assert sRenameIter[0] > fRenameIter[0] * 5 : "FAIL linux-0007 rename: slow=" + sRenameIter[0] + " fast=" + fRenameIter[0]; System.out.println(" PASS (rename slowpath >> fastpath)"); } // ------------------------------------------------------------------ // linux-0007 larger scale // ------------------------------------------------------------------ { int T = 32, D = 500; List threads = new ArrayList<>(T); Map devByName = new HashMap<>(T * D * 2); Map devByNetdev = new IdentityHashMap<>(T * D * 2); for (int t = 0; t < T; t++) { PktgenThread thread = new PktgenThread(); for (int d = 0; d < D; d++) { String name = "veth" + (t * D + d); Object netdev = new Object(); PktgenDev dev = new PktgenDev(name, netdev); thread.ifList.add(dev); devByName.put(name, dev); devByNetdev.put(netdev, dev); } threads.add(thread); } PktgenDev targetDev = threads.get(T - 1).ifList.get(D - 1); String targetName = targetDev.devName; Object targetNetdev = targetDev.netdev; int LOOKUPS = 100; long[] sIter = {0}, fIter = {0}; for (int i = 0; i < LOOKUPS; i++) { pktgenFindDev_slow(threads, targetName, sIter); pktgenFindDev_fast(devByName, targetName, fIter); } System.out.printf(" find T=%d D=%d × %d lookups: slow=%d fast=%d%n", T, D, LOOKUPS, sIter[0], fIter[0]); assert sIter[0] > fIter[0] * 100 : "FAIL linux-0007 large find: slow=" + sIter[0] + " fast=" + fIter[0]; System.out.println(" PASS"); } // ------------------------------------------------------------------ // linux-0008: taskstats register_listener O(C×L) vs O(C) // ------------------------------------------------------------------ System.out.println("\nlinux-0008: taskstats add_del_listener O(C×L) vs O(C)"); { int C = 64; // CPUs int L = 50; // existing listeners per CPU ListenerArray[] perCpu = new ListenerArray[C]; @SuppressWarnings("unchecked") Set[] perCpuSets = new Set[C]; boolean[] allCpus = new boolean[C]; for (int cpu = 0; cpu < C; cpu++) { perCpu[cpu] = new ListenerArray(); perCpuSets[cpu] = new HashSet<>(); allCpus[cpu] = true; // Pre-register L existing listeners (pids 1000..1000+L-1) for (int l = 0; l < L; l++) { int existingPid = 1000 + l; perCpu[cpu].list.add(new Listener(existingPid)); perCpuSets[cpu].add(existingPid); } } // Register a new pid (not yet present) — worst case: scan all L entries int newPid = 9999; int REGISTRATIONS = 100; long[] sIter = {0}, fIter = {0}; for (int i = 0; i < REGISTRATIONS; i++) { // Remove to re-trigger not-found path each iteration for (int cpu = 0; cpu < C; cpu++) { perCpu[cpu].list.removeIf(l -> l.pid == newPid); perCpuSets[cpu].remove(newPid); } registerListener_slow(perCpu, allCpus, newPid, sIter); registerListener_fast(perCpu, perCpuSets, allCpus, newPid, fIter); } System.out.printf(" C=%d L=%d × %d registrations: slow=%d fast=%d%n", C, L, REGISTRATIONS, sIter[0], fIter[0]); assert sIter[0] > fIter[0] * 5 : "FAIL linux-0008 C=" + C + " L=" + L + ": slow=" + sIter[0] + " fast=" + fIter[0]; System.out.println(" PASS (slow >> fast)"); } // ------------------------------------------------------------------ // linux-0008 larger scale // ------------------------------------------------------------------ { int C = 128, L = 200; ListenerArray[] perCpu = new ListenerArray[C]; @SuppressWarnings("unchecked") Set[] perCpuSets = new Set[C]; boolean[] allCpus = new boolean[C]; for (int cpu = 0; cpu < C; cpu++) { perCpu[cpu] = new ListenerArray(); perCpuSets[cpu] = new HashSet<>(); allCpus[cpu] = true; for (int l = 0; l < L; l++) { int existingPid = 1000 + l; perCpu[cpu].list.add(new Listener(existingPid)); perCpuSets[cpu].add(existingPid); } } int newPid = 88888; int REGISTRATIONS = 50; long[] sIter = {0}, fIter = {0}; for (int i = 0; i < REGISTRATIONS; i++) { for (int cpu = 0; cpu < C; cpu++) { perCpu[cpu].list.removeIf(l -> l.pid == newPid); perCpuSets[cpu].remove(newPid); } registerListener_slow(perCpu, allCpus, newPid, sIter); registerListener_fast(perCpu, perCpuSets, allCpus, newPid, fIter); } System.out.printf(" C=%d L=%d × %d registrations: slow=%d fast=%d%n", C, L, REGISTRATIONS, sIter[0], fIter[0]); assert sIter[0] > fIter[0] * 20 : "FAIL linux-0008 large: slow=" + sIter[0] + " fast=" + fIter[0]; System.out.println(" PASS"); } System.out.println("\n=".repeat(72).substring(1)); System.out.println("Linux0007Test PASSED — linux-0007 and linux-0008 confirmed O(n²)→O(n)"); } }