diff --git a/defects/swift/unit/SwiftTest.java b/defects/swift/unit/SwiftTest.java new file mode 100644 index 000000000..d38dbaf6c --- /dev/null +++ b/defects/swift/unit/SwiftTest.java @@ -0,0 +1,143 @@ +import java.util.*; + +/** + * CWE-407 simulation: Swift LoadableByAddress pass linear membership tests. + * + * The StructLoweringState in LoadableByAddress.cpp uses SmallVector with + * std::find for membership tests on largeLoadableArgs, applies, and other + * instruction vectors. For each instruction in the function, std::find + * performs O(A) linear scan where A = number of large loadable args. + * + * Defect: O(I x A) where I = instructions, A = large loadable args + * Fix: SmallPtrSet shadow for O(1) membership tests -> O(I + A) + * + * swift-0001: LoadableByAddress largeLoadableArgs/applies linear membership + */ +public class SwiftTest { + + // --- Defective: SmallVector + std::find pattern --- + static long visitApplyDefective(List largeLoadableArgs, + List funcSigArgs, + List applies, + List applyOperands) { + long ops = 0; + for (Object[] operands : applyOperands) { + for (Object operand : operands) { + ops++; + // std::find(largeLoadableArgs.begin(), ..., currOperand) + boolean found = false; + for (Object arg : largeLoadableArgs) { + ops++; + if (arg == operand) { found = true; break; } + } + if (!found) { + // std::find(funcSigArgs.begin(), ..., currOperand) + for (Object arg : funcSigArgs) { + ops++; + if (arg == operand) { found = true; break; } + } + } + if (found) { + // Dedup check: std::find(applies.begin(), ..., instr) + Object instr = operands; // proxy for instruction + for (Object a : applies) { + ops++; + if (a == instr) { found = false; break; } + } + if (found) applies.add(instr); + } + } + } + return ops; + } + + // --- Fixed: SmallPtrSet shadow for O(1) lookup --- + static long visitApplyFixed(Set largeLoadableArgsSet, + Set funcSigArgsSet, + Set appliesSet, + List applyOperands) { + long ops = 0; + for (Object[] operands : applyOperands) { + for (Object operand : operands) { + ops++; + boolean found = largeLoadableArgsSet.contains(operand); + ops++; + if (!found) { + found = funcSigArgsSet.contains(operand); + ops++; + } + if (found) { + Object instr = operands; + if (appliesSet.add(instr)) { + ops++; + } + } + } + } + return ops; + } + + public static void main(String[] args) { + // Simulate a function with A large loadable args and I apply instructions + int[] sizes = {10, 50, 100, 200, 500}; + + System.out.println("swift-0001: LoadableByAddress largeLoadableArgs linear membership"); + System.out.println("================================================================="); + System.out.printf("%-8s %-14s %-14s %-10s %-6s%n", + "A(args)", "Defective ops", "Fixed ops", "Ratio", "Status"); + + boolean allPass = true; + for (int A : sizes) { + // Create A large loadable args + List largeLoadableArgs = new ArrayList<>(); + Set largeLoadableArgsSet = new HashSet<>(); + List funcSigArgs = new ArrayList<>(); + Set funcSigArgsSet = new HashSet<>(); + List applies = new ArrayList<>(); + Set appliesSet = new HashSet<>(); + + Object[] argPool = new Object[A]; + for (int i = 0; i < A; i++) { + argPool[i] = new Object(); + largeLoadableArgs.add(argPool[i]); + largeLoadableArgsSet.add(argPool[i]); + funcSigArgs.add(argPool[i]); + funcSigArgsSet.add(argPool[i]); + } + + // Create I apply instructions, each with 3 operands from the arg pool + int I = A * 2; // instructions proportional to args + List applyOperands = new ArrayList<>(); + Random rng = new Random(42); + for (int i = 0; i < I; i++) { + Object[] ops = new Object[3]; + for (int j = 0; j < 3; j++) { + ops[j] = argPool[rng.nextInt(A)]; + } + applyOperands.add(ops); + } + + long defectOps = visitApplyDefective( + new ArrayList<>(largeLoadableArgs), + new ArrayList<>(funcSigArgs), + new ArrayList<>(applies), + applyOperands); + long fixedOps = visitApplyFixed( + new HashSet<>(largeLoadableArgsSet), + new HashSet<>(funcSigArgsSet), + new HashSet<>(appliesSet), + applyOperands); + + double ratio = (double) defectOps / fixedOps; + boolean pass = ratio > 2.0; + allPass &= pass; + + System.out.printf("%-8d %-14d %-14d %-10.1f %-6s%n", + A, defectOps, fixedOps, ratio, pass ? "PASS" : "FAIL"); + } + + System.out.println(); + System.out.println(allPass ? "ALL PASS" : "SOME FAIL"); + System.exit(allPass ? 0 : 1); + } +}