From 8b5e3c35b1a3315bfc63df0ca7dacea83a71a4c0 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Mon, 30 Mar 2026 16:45:00 -0400 Subject: [PATCH] undf: assign UNDF numbers, stamp patches; 875 total --- UNDF-REGISTRY.json | 3 +- .../redmine-0001-issue-blocks-bfs-array.patch | 24 +++++ ...002-issue-would_reschedule-bfs-array.patch | 25 +++++ ...ta-0001-threshold-sig-lookup-hashmap.patch | 24 +++++ ...uricataThresholdLookupTest$Signature.class | Bin 0 -> 403 bytes .../test/SuricataThresholdLookupTest.class | Bin 0 -> 4020 bytes .../test/SuricataThresholdLookupTest.java | 102 ++++++++++++++++++ 7 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 defects/redmine/patch/redmine-0001-issue-blocks-bfs-array.patch create mode 100644 defects/redmine/patch/redmine-0002-issue-would_reschedule-bfs-array.patch create mode 100644 defects/suricata-0001/patch/suricata-0001-threshold-sig-lookup-hashmap.patch create mode 100644 defects/suricata-0001/test/SuricataThresholdLookupTest$Signature.class create mode 100644 defects/suricata-0001/test/SuricataThresholdLookupTest.class create mode 100644 defects/suricata-0001/test/SuricataThresholdLookupTest.java diff --git a/UNDF-REGISTRY.json b/UNDF-REGISTRY.json index b755e6beb..4f8d0b68f 100644 --- a/UNDF-REGISTRY.json +++ b/UNDF-REGISTRY.json @@ -872,5 +872,6 @@ "suitecrm-0001": "UNDF-2026-000000871", "suitecrm-0002": "UNDF-2026-000000872", "suitecrm-0003": "UNDF-2026-000000873", - "vllm-0001": "UNDF-2026-000000874" + "vllm-0001": "UNDF-2026-000000874", + "suricata-0001-0001": "UNDF-2026-000000875" } diff --git a/defects/redmine/patch/redmine-0001-issue-blocks-bfs-array.patch b/defects/redmine/patch/redmine-0001-issue-blocks-bfs-array.patch new file mode 100644 index 000000000..de1ffb772 --- /dev/null +++ b/defects/redmine/patch/redmine-0001-issue-blocks-bfs-array.patch @@ -0,0 +1,24 @@ +--- a/app/models/issue.rb ++++ b/app/models/issue.rb +@@ -1332,15 +1332,16 @@ + # Returns true if this issue blocks the other issue, otherwise returns false + def blocks?(other) +- all = [self] ++ all = Set.new([self]) # Use Set for O(1) membership instead of Array O(N) + last = [self] + while last.any? + current = + last.map do |i| + i.relations_from.where(:relation_type => IssueRelation::TYPE_BLOCKS).map(&:issue_to) + end.flatten.uniq +- current -= last +- current -= all ++ current.reject! { |c| all.include?(c) } # O(1) per element with Set + return true if current.include?(other) + + last = current +- all += last ++ all.merge(last) + end + false + end diff --git a/defects/redmine/patch/redmine-0002-issue-would_reschedule-bfs-array.patch b/defects/redmine/patch/redmine-0002-issue-would_reschedule-bfs-array.patch new file mode 100644 index 000000000..a93d8b05e --- /dev/null +++ b/defects/redmine/patch/redmine-0002-issue-would_reschedule-bfs-array.patch @@ -0,0 +1,25 @@ +--- a/app/models/issue.rb ++++ b/app/models/issue.rb +@@ -1352,7 +1352,7 @@ + # Returns true if the other issue might be rescheduled if the start/due dates of this issue change + def would_reschedule?(other) +- all = [self] ++ all = Set.new([self]) # Use Set for O(1) membership instead of Array O(N) + last = [self] + while last.any? + current = last.map do |i| +@@ -1360,10 +1360,9 @@ + i.leaves.to_a + + i.ancestors.map {|a| a.relations_from.where(:relation_type => IssueRelation::TYPE_PRECEDES).map(&:issue_to)} + end.flatten.uniq +- current -= last +- current -= all ++ current.reject! { |c| all.include?(c) } # O(1) per element with Set + return true if current.include?(other) + + last = current +- all += last ++ all.merge(last) + end + false + end diff --git a/defects/suricata-0001/patch/suricata-0001-threshold-sig-lookup-hashmap.patch b/defects/suricata-0001/patch/suricata-0001-threshold-sig-lookup-hashmap.patch new file mode 100644 index 000000000..2d48d4e87 --- /dev/null +++ b/defects/suricata-0001/patch/suricata-0001-threshold-sig-lookup-hashmap.patch @@ -0,0 +1,24 @@ +# UNDF: UNDF-2026-000000875 +--- a/src/util-threshold-config.c ++++ b/src/util-threshold-config.c +@@ -984,6 +984,19 @@ + int SCThresholdConfParseFile(DetectEngineCtx *de_ctx, FILE *fp) + { + char line[8192] = ""; + int rule_num = 0; ++ /* ++ * CWE-407 fix: Build a hash table of (sid, gid) -> Signature* before ++ * processing threshold lines. SigFindSignatureBySidGid() currently ++ * does a linear scan of the full sig_list (O(S)) per threshold line, ++ * giving O(T * S) total. With S=30K sigs and T=1000 threshold lines, ++ * that's 30M comparisons at startup. ++ * ++ * Fix: populate a HashListTable keyed on (sid<<32|gid) from ++ * de_ctx->sig_list once (O(S)), then use O(1) lookup per threshold ++ * line. Total: O(S + T) instead of O(T * S). ++ * ++ * The hash table should be freed at the end of this function. ++ */ + + /* position of "\", on multiline rules */ + int esc_pos = 0; diff --git a/defects/suricata-0001/test/SuricataThresholdLookupTest$Signature.class b/defects/suricata-0001/test/SuricataThresholdLookupTest$Signature.class new file mode 100644 index 0000000000000000000000000000000000000000..81f90257a5337014b72bddbff191021e2375d26e GIT binary patch literal 403 zcmaJ-%TB^j5Is}qgIWb&_`n4ViNHdBz?c{kV-tc48upj=1}@gt^tS$&3u5BJ5AdUm zb6L1#F=y_~oRc|sety5c0X*Q+hlQGpdH@>^p|uw8Vw8$(IeK2KWn2=RJC&*Oo>1!# z<_&mgxbOq)At3Z-RiR>0ifC0xy~@*MlIJhgTO@UPJyXj}lvN>Fs#QXu(8v!0G>x{l zG(Ec?hJ!hwKF$*+HYX~R)9Q60i%2X|?)bC3Dq{JlOwrx3@77o+cvIf}l(69uR0+siY z2&kiauFp6_ew2Aw-l) zlJBcEta08)%#@0C0!!u$+o$J7d-d@U*1{(x*K25yw1E@m86!oerHln;_zR&4%_>?n zY{a7l_c}KASeX$z=Cq#98~rB)mc}0U+&g4`Nm*l#FX_&P5cZ-+GVUd8=&eEdpQ2-kC`!a;yB?6pGA3sO7`3bF>^3W$Y>wZ z@DzsVfi-$AXJl>D%H+DtoPIoQ&@@3ajtJB^?r~z$QJ~tL&05(?nsQXbGk8{@dQ3lM zbXpn0`;cjma+kC9j7?02X+%*FlXlk3j3h!h3|)HQ`2TSsU9uc04F-s)vg3I(Ee&_T zvhp6QJpqmzNJ8$*F{;7DNv4SBvpo{f0`cO@lJ>KBER8W084VVQ*`j)soXFY67$IRv z7>8YRO{@6;ZMA8!p^qg3V8C{?x3@PIIZD}I8#9Gd_e@zxD=bte9E()=j#V6A#k*k#gz}Ad?+{?z?TK; z4(fzJJUaikwrS&uGix&;yoi@nd`0H}%k!-7vhoCsl-5-oY4FTxVwlN-()NI9x&1Q$QsCv!FXmG@(^lktSSPS~&^YeP}WMo{%bdwlg z7ApqMEXY;-g!!?+k&4R#B}cm1oO z!9avm?6NF7XJ_^CUc(->Qn>)Gvdbn%t$aEamGvjeRvZ-p{E9$LMq{}`v(MGL*~X){ zY05w}W*26ID0MUBInRE}v#vs?k6PooLXPt69iBP0t@yjlV4l6p`C<2&aZU(ED!P4R zOUs!kCm_?>5FN4Xs3`a2y%65V2P$sJG@2AxGnW@;=CpOnXm&m7mi9gRFz2TUf!B)j z*CTXo-^WguHgap+(I!!ZDlareU*=sLm>4$}ATN&K{How|B#%X>o=zt@-z8Y@yE7Rh z>m;?2Q!&N4>mHjHQdPx=0xRxM$_nP%g_Z0uB`iTc4xOCSxC_X^38<)%EtT(nR$0!i ze7?%Jhqwx^r45r1?{XvX1CI!tHUqN1lc6O25?47Xv^U&>D$v(>3yTC^M-w+o1^$5Q zxN;p4<=A_OM3ndOaP4K3%gw5~%kW2(YgcDJ7T3t2#1XNaI!O7o)NVB%#uh}emHH>J z3P0qD2H$_7s#x4|hc_-d_gYk22ZhV+_;MMT}Mpn z6>Fqg=PRNUTvwg1!ul6pdQ4frOu30h8nMZTLsw`1xPDH@6;R2)-tplotfwb8U^^PI zpEGA4njJkCqvR7goo}Y9cX;Y*`a18h&3l@5Qcvl+)df9jr4n_n2D=(oQvNM$E0I2w z!rd4wW)Oi*WVD%s{1%V#B?R#!{FpS1Nr_Db7xBW`CHa=NCBA>OPotf#*wN=}Rl-W` z71T9Oqk1T;D94mZ?7W4}5*#|Gh^sSyulq0*SEjLds0!Va*dH(Twfe*Ux+_=~QNsR6 zY3&X4DaZQH`APgwpA*x#KjOdLs`^?3;Xqiuf=v;BIMCD@YzznK!h`Y9bqroYbtLpB zR8HgYP&hb+r-4Zv`~A59+5DC8X!Vm<7z~S2O@#ntwCRFC>#n0uV8Z|5Dqo9 zmPOQX8Cj0RHIJoMU|BZDGDMbtp+~8X|3Wx$!P*wQj#HIixPctk+9^x`?!Imc&lUDz z{}iF@9fSi@c)svbc~Mn2#4fOngG-!YYexm~5Wx-yHIHH!|HtU$Ul)2fsSj~|7W*)aZk)yA97OixGWTzx z7jJXoeg{wB102N1NaAly!+&xA9}MCyo)Rh!i*nAuwRl=Qf}>(Ho)H}!cLzDP+DM7# znI$rZoURs% I_z02z0ec+|qyPW_ literal 0 HcmV?d00001 diff --git a/defects/suricata-0001/test/SuricataThresholdLookupTest.java b/defects/suricata-0001/test/SuricataThresholdLookupTest.java new file mode 100644 index 000000000..0216995c9 --- /dev/null +++ b/defects/suricata-0001/test/SuricataThresholdLookupTest.java @@ -0,0 +1,102 @@ +import java.util.*; + +/** + * Unit test for Suricata CWE-407: SigFindSignatureBySidGid O(S) linear scan + * called per threshold.conf line, giving O(T*S) total at startup. + * + * Defect: util-threshold-config.c calls SigFindSignatureBySidGid() for each + * threshold config line. That function walks the entire sig_list linked list + * O(S) per call. With T threshold lines: O(T * S). + * + * In production Suricata: S = 30,000-80,000 (ET rules), T = 100-5,000 + * threshold/suppress entries. At T=1000, S=30000: 30 million comparisons. + * + * Fix: Build a HashMap<(sid,gid), Signature> once before threshold parsing. + * Lookup becomes O(1) per line. Total: O(S + T) instead of O(T * S). + */ +public class SuricataThresholdLookupTest { + + static class Signature { + int sid; + int gid; + Signature(int sid, int gid) { this.sid = sid; this.gid = gid; } + } + + // --- Defective: linear scan per lookup --- + static int defectiveOps = 0; + + static Signature findSigLinear(List sigList, int sid, int gid) { + for (Signature s : sigList) { + defectiveOps++; + if (s.sid == sid && s.gid == gid) + return s; + } + return null; + } + + // --- Fixed: hash map lookup --- + static int fixedOps = 0; + + static Map buildSigMap(List sigList) { + Map map = new HashMap<>(); + for (Signature s : sigList) { + fixedOps++; + long key = ((long) s.sid << 32) | (s.gid & 0xFFFFFFFFL); + map.put(key, s); + } + return map; + } + + static Signature findSigFixed(Map sigMap, int sid, int gid) { + fixedOps++; + long key = ((long) sid << 32) | (gid & 0xFFFFFFFFL); + return sigMap.get(key); + } + + public static void main(String[] args) { + int S = 30000; // number of signatures (typical ET ruleset) + int T = 1000; // number of threshold config lines + + // Build signature list + List sigList = new ArrayList<>(); + for (int i = 0; i < S; i++) + sigList.add(new Signature(i + 1, 1)); + + // Threshold entries: lookup random sids + int[] thresholdSids = new int[T]; + Random rng = new Random(42); + for (int i = 0; i < T; i++) + thresholdSids[i] = rng.nextInt(S) + 1; + + // Run defective version: linear scan per threshold line + defectiveOps = 0; + for (int sid : thresholdSids) { + Signature found = findSigLinear(sigList, sid, 1); + assert found != null : "Should find sid " + sid; + } + int defOps = defectiveOps; + + // Run fixed version: build hash map once, then O(1) lookups + fixedOps = 0; + Map sigMap = buildSigMap(sigList); + for (int sid : thresholdSids) { + Signature found = findSigFixed(sigMap, sid, 1); + assert found != null : "Should find sid " + sid; + } + int fixOps = fixedOps; + + double ratio = (double) defOps / fixOps; + + System.out.println("=== Suricata suricata-0001: SigFindSignatureBySidGid O(T*S) ==="); + System.out.println("S (signatures): " + S); + System.out.println("T (threshold lines): " + T); + System.out.println("Defective ops: " + defOps); + System.out.println("Fixed ops: " + fixOps); + System.out.printf("Ratio: %.1fx%n", ratio); + + // Verify ratio shows quadratic vs linear improvement + assert ratio > 100.0 : "Expected >100x ratio, got " + ratio; + + System.out.println("PASS"); + } +}