/** * CWE-407 simulation: Firefox nsDOMTokenList O(T²) classList add/remove. * * Simulates AddInternal() and RemoveInternal() from dom/base/nsDOMTokenList.cpp. * The defective version uses ArrayList.contains() for dedup (O(T²)), * the fixed version uses HashSet (O(T)). */ import java.util.*; public class Firefox0002DOMTokenListTest { // --- Defective AddInternal: addedClasses is ArrayList --- static int addInternalDefective(List existingClasses, List tokens) { List addedClasses = new ArrayList<>(); int ops = 0; for (String token : tokens) { // Check if already in existing classes (linear) ops++; if (existingClasses.contains(token)) continue; // Check if already added (linear in addedClasses) ops++; if (addedClasses.contains(token)) continue; addedClasses.add(token); } return ops; } // --- Fixed AddInternal: addedClasses is HashSet --- static int addInternalFixed(List existingClasses, List tokens) { Set existingSet = new HashSet<>(existingClasses); Set addedClasses = new HashSet<>(); int ops = 0; for (String token : tokens) { ops++; if (existingSet.contains(token)) continue; ops++; if (addedClasses.contains(token)) continue; addedClasses.add(token); } return ops; } // --- Defective RemoveInternal: aTokens.Contains() linear --- static int removeInternalDefective(List existingAtoms, List tokens) { int ops = 0; for (String atom : existingAtoms) { ops++; if (tokens.contains(atom)) continue; // O(T) per atom // would append to result } return ops; } // --- Fixed RemoveInternal: tokenSet is HashSet --- static int removeInternalFixed(List existingAtoms, List tokens) { Set tokenSet = new HashSet<>(tokens); int ops = 0; for (String atom : existingAtoms) { ops++; if (tokenSet.contains(atom)) continue; // O(1) } return ops; } public static void main(String[] args) { System.out.println("=== Firefox-0002: nsDOMTokenList O(T²) classList add/remove ===\n"); int[] tokenCounts = {50, 200, 500, 1000}; int pass = 0, fail = 0; // Test AddInternal System.out.println("--- AddInternal ---"); for (int T : tokenCounts) { List existing = new ArrayList<>(); for (int i = 0; i < 20; i++) existing.add("existing-" + i); List tokens = new ArrayList<>(); for (int i = 0; i < T; i++) tokens.add("new-token-" + i); // Warmup addInternalDefective(existing, tokens); addInternalFixed(existing, tokens); long t0 = System.nanoTime(); for (int r = 0; r < 200; r++) addInternalDefective(existing, tokens); long defectiveNs = System.nanoTime() - t0; t0 = System.nanoTime(); for (int r = 0; r < 200; r++) addInternalFixed(existing, tokens); long fixedNs = System.nanoTime() - t0; double ratio = (double) defectiveNs / fixedNs; boolean ok = ratio > 1.5; System.out.printf("T=%4d | defective=%8.3fms fixed=%8.3fms ratio=%.1fx %s%n", T, defectiveNs / 1e6, fixedNs / 1e6, ratio, ok ? "PASS" : "FAIL"); if (ok) pass++; else fail++; } // Test RemoveInternal System.out.println("\n--- RemoveInternal ---"); for (int T : tokenCounts) { List atoms = new ArrayList<>(); for (int i = 0; i < T; i++) atoms.add("class-" + i); List tokensToRemove = new ArrayList<>(); for (int i = 0; i < T / 2; i++) tokensToRemove.add("class-" + (i * 2)); // Warmup removeInternalDefective(atoms, tokensToRemove); removeInternalFixed(atoms, tokensToRemove); long t0 = System.nanoTime(); for (int r = 0; r < 200; r++) removeInternalDefective(atoms, tokensToRemove); long defectiveNs = System.nanoTime() - t0; t0 = System.nanoTime(); for (int r = 0; r < 200; r++) removeInternalFixed(atoms, tokensToRemove); long fixedNs = System.nanoTime() - t0; double ratio = (double) defectiveNs / fixedNs; boolean ok = ratio > 1.5; System.out.printf("T=%4d | defective=%8.3fms fixed=%8.3fms ratio=%.1fx %s%n", T, defectiveNs / 1e6, fixedNs / 1e6, ratio, ok ? "PASS" : "FAIL"); if (ok) pass++; else fail++; } System.out.printf("%nResults: %d PASS, %d FAIL%n", pass, fail); if (fail > 0) System.exit(1); } }