java-topology/defects/linux/unit/Linux0007Test.java

378 lines
15 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<PktgenDev> 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<PktgenThread> 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<String, PktgenDev> 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<PktgenThread> 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<Object, PktgenDev> 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<Listener> 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<Integer>[] 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<PktgenThread> threads = new ArrayList<>(T);
Map<String, PktgenDev> devByName = new HashMap<>(T * D * 2);
Map<Object, PktgenDev> 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<PktgenThread> threads = new ArrayList<>(T);
Map<String, PktgenDev> devByName = new HashMap<>(T * D * 2);
Map<Object, PktgenDev> 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<Integer>[] 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<Integer>[] 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)");
}
}