From 4d2d38fe6ed57625ea651ae1305ef0ab3ebe1f07 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Mon, 30 Mar 2026 17:21:56 -0400 Subject: [PATCH] undf: assign UNDF numbers, stamp patches; 886 total --- UNDF-REGISTRY.json | 6 +- .../openscad-0001-amf-vertex-dedup.patch | 1 + ...x2-0001-patch-reloadenabledlists-set.patch | 61 +++++++ .../test/PatchReloadEnabledListsTest.class | Bin 0 -> 4459 bytes .../test/PatchReloadEnabledListsTest.java | 165 ++++++++++++++++++ ...pcsx2-0002-gscapture-codec-dedup-set.patch | 21 +++ .../test/GSCaptureCodecDedupTest.class | Bin 0 -> 2884 bytes .../test/GSCaptureCodecDedupTest.java | 80 +++++++++ .../wine-0001-loader-dependency-dedup.patch | 28 +++ 9 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 defects/pcsx2-0001/patch/pcsx2-0001-patch-reloadenabledlists-set.patch create mode 100644 defects/pcsx2-0001/test/PatchReloadEnabledListsTest.class create mode 100644 defects/pcsx2-0001/test/PatchReloadEnabledListsTest.java create mode 100644 defects/pcsx2-0002/patch/pcsx2-0002-gscapture-codec-dedup-set.patch create mode 100644 defects/pcsx2-0002/test/GSCaptureCodecDedupTest.class create mode 100644 defects/pcsx2-0002/test/GSCaptureCodecDedupTest.java create mode 100644 defects/wine-0001/patch/wine-0001-loader-dependency-dedup.patch diff --git a/UNDF-REGISTRY.json b/UNDF-REGISTRY.json index a2945dd76..2cd56609c 100644 --- a/UNDF-REGISTRY.json +++ b/UNDF-REGISTRY.json @@ -880,5 +880,9 @@ "nagioscore-0001-0001": "UNDF-2026-000000879", "nagioscore-0002-0002": "UNDF-2026-000000880", "zabbix-0001-0001": "UNDF-2026-000000881", - "zabbix-0002-0002": "UNDF-2026-000000882" + "zabbix-0002-0002": "UNDF-2026-000000882", + "openscad-0001": "UNDF-2026-000000883", + "pcsx2-0001-0001": "UNDF-2026-000000884", + "pcsx2-0002-0002": "UNDF-2026-000000885", + "wine-0001-0001": "UNDF-2026-000000886" } diff --git a/defects/openscad/patch/openscad-0001-amf-vertex-dedup.patch b/defects/openscad/patch/openscad-0001-amf-vertex-dedup.patch index 7f3a4caa0..1fe6c9bce 100644 --- a/defects/openscad/patch/openscad-0001-amf-vertex-dedup.patch +++ b/defects/openscad/patch/openscad-0001-amf-vertex-dedup.patch @@ -1,3 +1,4 @@ +# UNDF: UNDF-2026-000000883 --- a/src/io/export_amf.cc +++ b/src/io/export_amf.cc @@ -36,6 +36,7 @@ diff --git a/defects/pcsx2-0001/patch/pcsx2-0001-patch-reloadenabledlists-set.patch b/defects/pcsx2-0001/patch/pcsx2-0001-patch-reloadenabledlists-set.patch new file mode 100644 index 000000000..148b97dff --- /dev/null +++ b/defects/pcsx2-0001/patch/pcsx2-0001-patch-reloadenabledlists-set.patch @@ -0,0 +1,61 @@ +# UNDF: UNDF-2026-000000884 +--- a/pcsx2/Patch.cpp ++++ b/pcsx2/Patch.cpp +@@ -583,6 +583,8 @@ + + void Patch::ReloadEnabledLists() + { ++ // Convert lookup targets to sets for O(1) membership testing instead of ++ // O(N) linear scans on std::vector, eliminating four O(N^2) loops below. + const std::vector prev_enabled_cheats = std::move(s_enabled_cheats); + if (EmuConfig.EnableCheats && !Achievements::IsHardcoreModeActive()) + s_enabled_cheats = Host::GetStringListSetting(CHEATS_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY); +@@ -591,6 +593,10 @@ + + const std::vector prev_enabled_patches = std::exchange(s_enabled_patches, Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_ENABLE_CONFIG_KEY)); + const std::vector disabled_patches = Host::GetStringListSetting(PATCHES_CONFIG_SECTION, PATCH_DISABLE_CONFIG_KEY); ++ const std::unordered_set disabled_patches_set(disabled_patches.begin(), disabled_patches.end()); ++ const std::unordered_set prev_enabled_cheats_set(prev_enabled_cheats.begin(), prev_enabled_cheats.end()); ++ const std::unordered_set prev_enabled_patches_set(prev_enabled_patches.begin(), prev_enabled_patches.end()); ++ + + // Name based matching for widescreen/NI settings. + if (EmuConfig.EnableWideScreenPatches) +@@ -612,7 +618,7 @@ + + for (auto it = s_enabled_patches.begin(); it != s_enabled_patches.end();) + { +- if (std::find(disabled_patches.begin(), disabled_patches.end(), *it) != disabled_patches.end()) ++ if (disabled_patches_set.count(*it)) + { + it = s_enabled_patches.erase(it); + } +@@ -626,13 +632,13 @@ + s_just_enabled_patches.clear(); + for (const auto& p : s_enabled_cheats) + { +- if (std::find(prev_enabled_cheats.begin(), prev_enabled_cheats.end(), p) == prev_enabled_cheats.end()) ++ if (!prev_enabled_cheats_set.count(p)) + { + s_just_enabled_cheats.emplace_back(p); + } + } + for (const auto& p : s_enabled_patches) + { +- if (std::find(prev_enabled_patches.begin(), prev_enabled_patches.end(), p) == prev_enabled_patches.end()) ++ if (!prev_enabled_patches_set.count(p)) + { + s_just_enabled_patches.emplace_back(p); + } +@@ -644,10 +650,11 @@ + u32 Patch::EnablePatches(const std::vector* patches, const std::vector& enable_list, const std::vector* enable_immediately_list) + { ++ const std::unordered_set enable_set(enable_list.begin(), enable_list.end()); + u32 count = 0; + for (const PatchGroup& p : *patches) + { + // For compatibility, we auto enable anything that's not labelled. + // Also for gamedb patches. +- if (!p.name.empty() && std::find(enable_list.begin(), enable_list.end(), p.name) == enable_list.end()) ++ if (!p.name.empty() && !enable_set.count(p.name)) + continue; diff --git a/defects/pcsx2-0001/test/PatchReloadEnabledListsTest.class b/defects/pcsx2-0001/test/PatchReloadEnabledListsTest.class new file mode 100644 index 0000000000000000000000000000000000000000..587f7eb036cb1741c178bf35edd9b3ac887a477c GIT binary patch literal 4459 zcmb_fTTC3+8UD`RW?2Rd%kG*5+u7L01_P$P1lugWa zgHyFCLY#Bv{O6qS|Nig)&*5kHZhZn^Cq4|p1Fr<13_k)4CC8L;r6Hr}Lk;`-kEsI| zL!epHHESD#x4Pz#gdjtiFdDP8OvBDxPMPe~Oe+Kl-a-^YmQW<47>gK+XYIHNLr}BS zoMIU{hFEn?=j?Qc^F3LJQiLUx$%r7z5T0+sAPp;Kw>m-l5uzh}#bO!dh%@+f@i~&; z+vP`6LggIyQdUmWhdAP;GM3>HLN{ROmZIq0pVd|NdqFVxVhr_9WZ9A9@VFNZwcuYnkHZfH0 zSFC~IgKEZ5(rvoZpHb61HfE2?15}xtH_@sNa?Il@!!EZod{xpF!Uf0f7z+cAW{Zp_ z4rTHD!`ih8HC;e9%`&!vG*`A#F-Ci28NTuYL#+A&Nx^Iw|fjOFWERunp zYo|_<=n=vYh9zixvtx7oZ8gS7ygXK|Zq?hLN%a|wO3-B($mT27T$f2WSxx|3jjW260Qu7@WiRWPy`80k{7&bT0@4e7KdM|0;MOo(O|u@0zh4;GUD+2+B5 zv*1Pyd;f2sYC4FQQMxs_mff0};akEtNq`GXQLZ)*WSq-2lrqM018Td*`AmF4kzUK= zMVYY6Ff7x`DcLU78aC2q5U=yt^HTOS3*tKr>)R%>RJzr4V%z$Ogqc;<^jJ1AXyg*} z)4VP*WLODW_a;OAL)Xm_xwB+Z0B?nG5pPR)hfDSn!^&AnY5KS^qBhuM*tb@@GC(hY zNrnsVtaA))XZ9$1I-{B^I}KxGEbEBFeW|%5G-qMYWL90k+)cjytVz0ShOsj* zPSI#7wTxQ-n(JUc&TlXJVJghM>{=`(pX8QxeAk5jptB}Q)Ima zfoYUTD3nlIa|fQ8!5SLiC9(I*v?IM9XiRRiGI&uy-_PI1ioV+GsOsjOI_fm}^3M1j zDgqp-;|%p2XQcYLbQ9H_ilVrm_Z?^Q%LCkVPNl>r#rkUAbJhomf#A#r0p7Pa_;oxm z%SI!L2<1A$x*lcNNS;1MUNoYOJlTgWIEW;Uq8ZN;Kc`3uXRrgW)9Z$_(tyByB1;my z5H<8$BYrte0Ow{Jr8aA|bBUs-icJ)idJhQ;NP4&0baYdQg)?iY?Z=)2IDlg+eRJw(=KvS?TuM?7-qnP-NcA`=+D-zS0 zp|$?ZWs;{*T7pt_N+M^!UsoUZ-N5=L$?j}!3Vwv-Ma1J0-*dZ=^lj|w3%6cJ`%mbk zzzuXX7TBGx#*na%L<^#!_fRAZ_j5yUV_~$g6rWv2+&Q7wIiPo{kk0*6sq~A+!W%fa zN%l4tM~kC{mr_^1d$ym9TtLe6Km?tY<>3g-H!mT~2`vxb!Wh6=b!N-m zv*ouik)J&rp1g*y@*;2N*qSy(6`L_g^eGzl|&|;5dE^6CcCEPYKBH>A1g8#r-?gnSWCM9z7SmI6=4mNfyTQ ztb*DEUSQRDk=5fA+k}_cc52)33hTnF>=0gK{q&l7ncCNIhMmP(_BPJ3E7X1$Z_pKW zo?W9C#HYBxKBM;MxX3=oJM0hiDEKSh^_1e0CyMty8}NbW0IqoY@S#Y-L4@y9VGK&N zudB-OT1PkCUC{sT6?ag22mWc)+XmC9X6!CJ><*URgUo!?iA-aWXz!uh=XHOWM%w3H zN(JFtGk5Doaf#A38s2aXCoV$rU4_s4BP3@-kiTbn5%$9)@1Xynh*ut=GdN3agvJ+W YpL62x8{+SIyh}47`uZL|z!k**4GS<-cmMzZ literal 0 HcmV?d00001 diff --git a/defects/pcsx2-0001/test/PatchReloadEnabledListsTest.java b/defects/pcsx2-0001/test/PatchReloadEnabledListsTest.java new file mode 100644 index 000000000..793e065e3 --- /dev/null +++ b/defects/pcsx2-0001/test/PatchReloadEnabledListsTest.java @@ -0,0 +1,165 @@ +import java.util.*; + +/** + * Unit test for PCSX2 pcsx2-0001: Patch::ReloadEnabledLists and EnablePatches + * use std::find on std::vector for membership checks inside loops, giving O(N^2). + * + * Four sites in ReloadEnabledLists: + * 1. disabled_patches lookup O(E*D) + * 2. prev_enabled_cheats lookup O(C*P) + * 3. prev_enabled_patches lookup O(P*P) + * 4. EnablePatches enable_list lookup O(G*E) + * + * Fix: convert lookup vectors to HashSet for O(1) membership. + * + * Defect file: pcsx2/Patch.cpp lines 616, 630, 637, 651 + */ +public class PatchReloadEnabledListsTest { + + // --- Defective: linear scan on list --- + static List reloadEnabledListsDefective( + List enabledPatches, + List disabledPatches, + List prevEnabledPatches) { + + // Filter disabled patches: O(E*D) with list.contains + List filtered = new ArrayList<>(); + for (String patch : enabledPatches) { + if (!disabledPatches.contains(patch)) { // O(D) scan per patch + filtered.add(patch); + } + } + + // Find newly enabled: O(F*P) with list.contains + List justEnabled = new ArrayList<>(); + for (String p : filtered) { + if (!prevEnabledPatches.contains(p)) { // O(P) scan per patch + justEnabled.add(p); + } + } + return justEnabled; + } + + // --- Fixed: hash set for O(1) lookups --- + static List reloadEnabledListsFixed( + List enabledPatches, + List disabledPatches, + List prevEnabledPatches) { + + Set disabledSet = new HashSet<>(disabledPatches); + Set prevSet = new HashSet<>(prevEnabledPatches); + + List filtered = new ArrayList<>(); + for (String patch : enabledPatches) { + if (!disabledSet.contains(patch)) { // O(1) + filtered.add(patch); + } + } + + List justEnabled = new ArrayList<>(); + for (String p : filtered) { + if (!prevSet.contains(p)) { // O(1) + justEnabled.add(p); + } + } + return justEnabled; + } + + // --- EnablePatches defective: O(G*E) --- + static int enablePatchesDefective(List patchGroups, List enableList) { + int count = 0; + for (String name : patchGroups) { + if (!name.isEmpty() && !enableList.contains(name)) // O(E) per group + continue; + count++; + } + return count; + } + + // --- EnablePatches fixed: O(G+E) --- + static int enablePatchesFixed(List patchGroups, List enableList) { + Set enableSet = new HashSet<>(enableList); + int count = 0; + for (String name : patchGroups) { + if (!name.isEmpty() && !enableSet.contains(name)) // O(1) + continue; + count++; + } + return count; + } + + public static void main(String[] args) { + int N = 500; + + // Build test data + List enabled = new ArrayList<>(); + List disabled = new ArrayList<>(); + List prevEnabled = new ArrayList<>(); + List patchGroups = new ArrayList<>(); + List enableList = new ArrayList<>(); + + for (int i = 0; i < N; i++) { + String name = "patch_" + i; + enabled.add(name); + patchGroups.add(name); + enableList.add(name); + if (i % 5 == 0) disabled.add(name); + if (i < N / 2) prevEnabled.add(name); + } + + // Correctness check + List resultDef = reloadEnabledListsDefective(enabled, disabled, prevEnabled); + List resultFix = reloadEnabledListsFixed(enabled, disabled, prevEnabled); + assert resultDef.equals(resultFix) : "ReloadEnabledLists mismatch"; + + int countDef = enablePatchesDefective(patchGroups, enableList); + int countFix = enablePatchesFixed(patchGroups, enableList); + assert countDef == countFix : "EnablePatches mismatch"; + + // Warm up + for (int i = 0; i < 200; i++) { + reloadEnabledListsDefective(enabled, disabled, prevEnabled); + reloadEnabledListsFixed(enabled, disabled, prevEnabled); + } + + // Benchmark ReloadEnabledLists + int ITER = 2000; + long t0 = System.nanoTime(); + for (int i = 0; i < ITER; i++) { + reloadEnabledListsDefective(enabled, disabled, prevEnabled); + } + long defectNs = System.nanoTime() - t0; + + t0 = System.nanoTime(); + for (int i = 0; i < ITER; i++) { + reloadEnabledListsFixed(enabled, disabled, prevEnabled); + } + long fixedNs = System.nanoTime() - t0; + + double ratio = (double) defectNs / fixedNs; + System.out.printf("ReloadEnabledLists N=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n", + N, defectNs / 1e6, fixedNs / 1e6, ratio); + + // Benchmark EnablePatches + t0 = System.nanoTime(); + for (int i = 0; i < ITER; i++) { + enablePatchesDefective(patchGroups, enableList); + } + long epDefNs = System.nanoTime() - t0; + + t0 = System.nanoTime(); + for (int i = 0; i < ITER; i++) { + enablePatchesFixed(patchGroups, enableList); + } + long epFixNs = System.nanoTime() - t0; + + double epRatio = (double) epDefNs / epFixNs; + System.out.printf("EnablePatches N=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n", + N, epDefNs / 1e6, epFixNs / 1e6, epRatio); + + assert ratio > 2.0 : "Expected >2x speedup for ReloadEnabledLists, got " + ratio; + assert epRatio > 2.0 : "Expected >2x speedup for EnablePatches, got " + epRatio; + + System.out.println("PASS"); + } +} diff --git a/defects/pcsx2-0002/patch/pcsx2-0002-gscapture-codec-dedup-set.patch b/defects/pcsx2-0002/patch/pcsx2-0002-gscapture-codec-dedup-set.patch new file mode 100644 index 000000000..e8ab6a715 --- /dev/null +++ b/defects/pcsx2-0002/patch/pcsx2-0002-gscapture-codec-dedup-set.patch @@ -0,0 +1,21 @@ +# UNDF: UNDF-2026-000000885 +--- a/pcsx2/GS/GSCapture.cpp ++++ b/pcsx2/GS/GSCapture.cpp +@@ -1488,6 +1488,7 @@ + } + + void* iter = nullptr; + const AVCodec* codec; ++ std::unordered_set seen_codecs; + while ((codec = wrap_av_codec_iterate(&iter)) != nullptr) + { + // only get audio codecs +@@ -1498,7 +1499,7 @@ + if (!wrap_avformat_query_codec(output_format, codec->id, FF_COMPLIANCE_NORMAL)) + continue; + +- if (std::find_if(ret.begin(), ret.end(), [codec](const auto& it) { return it.first == codec->name; }) != ret.end()) ++ if (!seen_codecs.insert(codec->name).second) + continue; + + ret.emplace_back(codec->name, codec->long_name ? codec->long_name : codec->name); diff --git a/defects/pcsx2-0002/test/GSCaptureCodecDedupTest.class b/defects/pcsx2-0002/test/GSCaptureCodecDedupTest.class new file mode 100644 index 0000000000000000000000000000000000000000..dca8215e4691aa1b7676d132a4d4cb11ae3906ee GIT binary patch literal 2884 zcmaJ@%X1V(9R7M=vzZJbyUB(;2y7q=fxLJb9vdDZ2@ps^bR#jK!m!&(hV0HPGqW-A zi5e9nCof2u*gcx4EKG^Sepe3J4JqO)SgIt7)eV({rsclko@0v>S*HOuHFY*W)mWmKb9!3Jz3 zqWp4BO0+T`)G62`LcMA#C83@nv;^>kYKvzI>?aS@tCq!0TO-(Anx$S$^ArPfRH~KG&QR$vcNlamgm#;zVFp2F3ifGM zMzxb;GU7NQ+}H6R!k>}Ri7pA<3Xb9U@;#igY@VTHx~dz4T82oHB~BL6gdPPaaf;eT zPDqm9P|au^pk22~QLbjkU(_+06hcN*Ldj^f|6nW?rG}92V60{5NXCjrM}!QE zEwZv^IIYQ;F5@%?Bs{C&IV2X0;jr(~Z9dA$^U{Qx&hgVD!u`v3*o}w>I$EZ$kVPP4 z2roz&7Rk;oqugcWNNOQfSD{Dp-Q6`aR~g@d}BHTgU%G$0i1g+6y(c5W5x zuPR>PhyY5zFc?z8(<@QAIfEFbvpLX_NC~`_!!4E=p*l zI?XX@?Bvs@qrE^07u2B@CuY?kLXc54B398*DCZ(GGHiP??cS<#!pNCPeoPYp*DsoJ z`JrwRX%g2^a*2}x8_mi@1tnp^d~ zhB2PY##d@q(6emf;8d2c5?&}+{smgl+&N2bicU41PS9f#XHa@{otus&xFz8#Lu>`7 z)u2k4Vc78hp>$UAyiiA1pNGuON-M?vM>Rj$o}NtlUZz$pl}z+*zYF%I2!u&rF$^dO ziaVPUh|r&?l&Bni0BQIxih8%tL1yS{nnNkW4Gd7Fg5f(<_j}uXjn3c!f0f@It`oKV z_<=xGp!p8g-$0SC&36ldTTgz8_-0Yu*gS`*2Sc}>eBSs4E#+~*>>?mQDS-*mD;7o! zWpq)NqZbjnyDI3asl@ZB!dZF~FJe77v9f5!Mg$&Xt%PC;p?T2dUd^ah{|KJHq0kq$ zvqX*()5Ae+!SWiFek!{bSU$jJVjJtrv+XB@4%--2TsFR@D&HKoct``KQB4Pf2g0OiUOO6NaY!l;F2e+0{1xJsjN|@U{gb!HEARN}QPKAPxUP zBNYL@H|R#l5qTkk637J+az${aLY^`9E4OhX+$%kVXV_bF5B-T@9}Sgb(71;)iD5ql zEj9v`is-z9b3+dLno6Y{>h(_%7T7D5`$z(UfXF#=2V=s=dFxyvO90LAVF#ILCmClK zslOB3N&QyrrG4Lr0eZ;Kp&b`+kgOBOIF8WG-GLnK%nTXrV|3#>J>j3>IKD;?9?}E- zJx<|wD*vRHID~#yMMe~s+YNahC1kT=_6RkP5p%Y)_#b$vy>1=~^HC*4wsgys&r7D5 zTs)6^oP!61!Yy}@o`pom!{_}9ZA+XM&Zbz;507$${w!e1q5LXdqq2m^U!gp&JKs0) PKK04e{s151BUJqhX?Mj5 literal 0 HcmV?d00001 diff --git a/defects/pcsx2-0002/test/GSCaptureCodecDedupTest.java b/defects/pcsx2-0002/test/GSCaptureCodecDedupTest.java new file mode 100644 index 000000000..f8bb98116 --- /dev/null +++ b/defects/pcsx2-0002/test/GSCaptureCodecDedupTest.java @@ -0,0 +1,80 @@ +import java.util.*; + +/** + * Unit test for PCSX2 pcsx2-0002: GSCapture::GetCodecListForContainer + * uses std::find_if on vector to deduplicate codec names during enumeration, + * giving O(N^2) where N = number of codecs iterated. + * + * Fix: track seen codec names in an unordered_set for O(1) dedup. + * + * Defect file: pcsx2/GS/GSCapture.cpp line 1501 + */ +public class GSCaptureCodecDedupTest { + + // --- Defective: linear scan on list for dedup --- + static List getCodecListDefective(List codecs) { + List ret = new ArrayList<>(); + for (String name : codecs) { + boolean found = false; + for (String existing : ret) { + if (existing.equals(name)) { + found = true; + break; + } + } + if (!found) { + ret.add(name); + } + } + return ret; + } + + // --- Fixed: hash set dedup --- + static List getCodecListFixed(List codecs) { + List ret = new ArrayList<>(); + Set seen = new HashSet<>(); + for (String name : codecs) { + if (seen.add(name)) { + ret.add(name); + } + } + return ret; + } + + public static void main(String[] args) { + int N = 500; + + // Build codec list with ~50% duplicates + List codecs = new ArrayList<>(); + for (int i = 0; i < N; i++) { + codecs.add("codec_" + (i % (N / 2))); + } + + // Correctness + List resDef = getCodecListDefective(codecs); + List resFix = getCodecListFixed(codecs); + assert resDef.equals(resFix) : "Mismatch"; + + // Warmup + for (int i = 0; i < 500; i++) { + getCodecListDefective(codecs); + getCodecListFixed(codecs); + } + + int ITER = 5000; + long t0 = System.nanoTime(); + for (int i = 0; i < ITER; i++) getCodecListDefective(codecs); + long defNs = System.nanoTime() - t0; + + t0 = System.nanoTime(); + for (int i = 0; i < ITER; i++) getCodecListFixed(codecs); + long fixNs = System.nanoTime() - t0; + + double ratio = (double) defNs / fixNs; + System.out.printf("GSCapture codec dedup N=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n", + N, defNs / 1e6, fixNs / 1e6, ratio); + + assert ratio > 2.0 : "Expected >2x speedup, got " + ratio; + System.out.println("PASS"); + } +} diff --git a/defects/wine-0001/patch/wine-0001-loader-dependency-dedup.patch b/defects/wine-0001/patch/wine-0001-loader-dependency-dedup.patch new file mode 100644 index 000000000..12d9efdc0 --- /dev/null +++ b/defects/wine-0001/patch/wine-0001-loader-dependency-dedup.patch @@ -0,0 +1,28 @@ +# UNDF: UNDF-2026-000000886 +--- a/dlls/ntdll/loader.c ++++ b/dlls/ntdll/loader.c +@@ -860,16 +860,22 @@ + static LDR_DEPENDENCY *find_module_dependency( LDR_DDAG_NODE *from, LDR_DDAG_NODE *to ) + { ++ /* DEFECT: O(D) linear scan through circular singly-linked list of ++ * dependencies. Called from add_module_dependency_after() on every ++ * DLL import, yielding O(I*D) per module where I = imports and ++ * D = accumulated dependencies. For complex DLL trees with hundreds ++ * of imports this is quadratic. ++ * ++ * Ideal fix: maintain a hash set (e.g. wine_rb_tree keyed on ++ * dependency_to pointer) alongside the linked list. Check the ++ * hash set for O(1) dedup instead of walking the list. ++ * The linked list must be preserved for Windows ABI compatibility. */ + SINGLE_LIST_ENTRY *entry, *mark = from->Dependencies.Tail; + + if (!mark) return NULL; + + for (entry = mark->Next; entry != mark; entry = entry->Next) + { + LDR_DEPENDENCY *dep = CONTAINING_RECORD( entry, LDR_DEPENDENCY, dependency_to_entry ); + if (dep->dependency_to == to && dep->dependency_from == from) return dep; + } + + return NULL; + }