diff --git a/defects/llvm/patch/llvm-0006-partialinlining-IsSingleExit-BlockList-O-N2.patch b/defects/llvm/patch/llvm-0006-partialinlining-IsSingleExit-BlockList-O-N2.patch new file mode 100644 index 000000000..18593aebe --- /dev/null +++ b/defects/llvm/patch/llvm-0006-partialinlining-IsSingleExit-BlockList-O-N2.patch @@ -0,0 +1,28 @@ +# UNDF: (leave blank) +# CWE-407: PartialInlinerImpl IsSingleExit BlockList membership O(N²) +# +# In computeOutliningColdRegionsInfo, the IsSingleExit lambda iterates +# over every block in BlockList and for each successor calls +# is_contained(BlockList, Succ), which is O(N) per call. +# Total: O(N × S × N) = O(N² × S) where N = region blocks, S = avg successors. +# +# Fix: build a SmallPtrSet from BlockList for O(1) membership lookups. +# +# Severity: LOW-MEDIUM — only runs with instrumentation profiling data, +# but region sizes can be large in profiled builds of complex functions. +# +--- a/llvm/lib/Transforms/IPO/PartialInlining.cpp ++++ b/llvm/lib/Transforms/IPO/PartialInlining.cpp +@@ -368,10 +368,12 @@ + auto IsSingleExit = + [&ORE](SmallVectorImpl &BlockList) -> BasicBlock * { + BasicBlock *ExitBlock = nullptr; ++ // Build set for O(1) membership tests (was O(N) linear scan). ++ SmallPtrSet BlockSet(BlockList.begin(), BlockList.end()); + for (auto *Block : BlockList) { + for (BasicBlock *Succ : successors(Block)) { +- if (!is_contained(BlockList, Succ)) { ++ if (!BlockSet.count(Succ)) { + if (ExitBlock) { + ORE.emit([&]() { + return OptimizationRemarkMissed(DEBUG_TYPE, "MultiExitRegion", diff --git a/defects/llvm/unit/LlvmTest.java b/defects/llvm/unit/LlvmTest.java new file mode 100644 index 000000000..03edf8213 --- /dev/null +++ b/defects/llvm/unit/LlvmTest.java @@ -0,0 +1,77 @@ +import java.util.*; + +/** + * CWE-407 unit tests for LLVM defects (new: llvm-0006). + * + * llvm-0006: PartialInlining IsSingleExit — is_contained(BlockList, ...) O(N²) + * + * (llvm-0001 through llvm-0005 have separate unit tests or are covered + * by LlvmAliasSetTest.java, LlvmDomConditionCacheTest.java, etc.) + */ +public class LlvmTest { + + // --------------------------------------------------------------- + // llvm-0006: PartialInlining IsSingleExit BlockList membership + // is_contained(BlockList, Succ) inside nested for loops → O(N² × S) + // --------------------------------------------------------------- + + /** Defective: linear scan for block list membership */ + static int isSingleExitDefective(int blockCount) { + List blockList = new ArrayList<>(); + for (int i = 0; i < blockCount; i++) blockList.add(i); + int ops = 0; + + for (int block : blockList) { + // Each block has ~2 successors + int[] successors = { block + 1, block + blockCount / 3 }; + for (int succ : successors) { + // Linear scan: is_contained(BlockList, Succ) + for (int b : blockList) { + ops++; + if (b == succ) break; + } + } + } + return ops; + } + + /** Fixed: SmallPtrSet for block list membership */ + static int isSingleExitFixed(int blockCount) { + Set blockSet = new HashSet<>(); + for (int i = 0; i < blockCount; i++) blockSet.add(i); + int ops = 0; + + for (int block = 0; block < blockCount; block++) { + int[] successors = { block + 1, block + blockCount / 3 }; + for (int succ : successors) { + ops++; // O(1) hash lookup + blockSet.contains(succ); + } + } + return ops; + } + + // --------------------------------------------------------------- + // Test runner + // --------------------------------------------------------------- + + static void check(String name, boolean cond) { + System.out.println((cond ? "PASS" : "FAIL") + " " + name); + if (!cond) System.exit(1); + } + + public static void main(String[] args) { + // llvm-0006: PartialInlining IsSingleExit + { + int N = 500; + int defectOps = isSingleExitDefective(N); + int fixedOps = isSingleExitFixed(N); + double ratio = (double) defectOps / fixedOps; + System.out.printf("llvm-0006: defect=%d fixed=%d ratio=%.1fx%n", defectOps, fixedOps, ratio); + check("llvm-0006-quadratic", defectOps > fixedOps * 10); + check("llvm-0006-fixed-linear", fixedOps <= N * 3); + } + + System.out.println("ALL PASS"); + } +}