From 54827d6eb7024fe3c2a25e9e81f515b479a00712 Mon Sep 17 00:00:00 2001 From: "russell@unturf.com" Date: Tue, 31 Mar 2026 19:37:31 -0400 Subject: [PATCH] ppsspp: add ppsspp-0004 CWE-407 mutex waitingThreads dedup, extend unit test to 8/8 PASS ppsspp-0004: sceKernelLockMutex/CB and sceKernelLockLwMutex/CB (4 sites) scan mutex->waitingThreads vector with std::find before push_back -- O(W) per lock attempt. Fix: parallel unordered_set for O(1) dedup. Unit test extended to cover ppsspp-0004; all 8/8 PASS (ratios 5-27x). --- ...04-kernel-mutex-waitingThreads-dedup.patch | 37 +++++++++++++++ defects/ppsspp/test/PpssppKernelDedup.class | Bin 5065 -> 5957 bytes defects/ppsspp/test/PpssppKernelDedup.java | 42 ++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 defects/ppsspp/patch/ppsspp-0004-kernel-mutex-waitingThreads-dedup.patch diff --git a/defects/ppsspp/patch/ppsspp-0004-kernel-mutex-waitingThreads-dedup.patch b/defects/ppsspp/patch/ppsspp-0004-kernel-mutex-waitingThreads-dedup.patch new file mode 100644 index 000000000..c2ff42113 --- /dev/null +++ b/defects/ppsspp/patch/ppsspp-0004-kernel-mutex-waitingThreads-dedup.patch @@ -0,0 +1,37 @@ +--- a/Core/HLE/sceKernelMutex.cpp ++++ b/Core/HLE/sceKernelMutex.cpp +@@ -547,3 +547,3 @@ + // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. +- if (std::find(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID) == mutex->waitingThreads.end()) ++ if (mutex->waitingThreadSet.insert(threadID).second) + mutex->waitingThreads.push_back(threadID); +@@ -569,3 +569,3 @@ + // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. +- if (std::find(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID) == mutex->waitingThreads.end()) ++ if (mutex->waitingThreadSet.insert(threadID).second) + mutex->waitingThreads.push_back(threadID); +@@ -950,3 +950,3 @@ + // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. +- if (std::find(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID) == mutex->waitingThreads.end()) ++ if (mutex->waitingThreadSet.insert(threadID).second) + mutex->waitingThreads.push_back(threadID); +@@ -985,3 +985,3 @@ + // May be in a tight loop timing out (where we don't remove from waitingThreads yet), don't want to add duplicates. +- if (std::find(mutex->waitingThreads.begin(), mutex->waitingThreads.end(), threadID) == mutex->waitingThreads.end()) ++ if (mutex->waitingThreadSet.insert(threadID).second) + mutex->waitingThreads.push_back(threadID); +# +# CWE-407: sceKernelLockMutex / sceKernelLockMutexCB / +# sceKernelLockLwMutex / sceKernelLockLwMutexCB each scan +# mutex->waitingThreads vector with std::find before push_back — +# O(W) per lock attempt where W = number of waiting threads. +# The code comment says "May be in a tight loop timing out" — that +# tight-loop scenario is exactly where O(W) dedup compounds: +# T attempts * W waiters = O(T*W) total work per timeout cycle. +# Fix: maintain parallel unordered_set waitingThreadSet for +# O(1) dedup; remove from set wherever waitingThreads is cleared/erased. +# Severity: MEDIUM — mutex lock is a hot HLE synchronization path; +# games with thread pools or producer-consumer patterns will trigger +# this on every lock contention timeout cycle. +# Sites: sceKernelLockMutex (line 548), sceKernelLockMutexCB (line 570), +# sceKernelLockLwMutex (line 951), sceKernelLockLwMutexCB (line 986). diff --git a/defects/ppsspp/test/PpssppKernelDedup.class b/defects/ppsspp/test/PpssppKernelDedup.class index 40191d5d22fc22e1014faf8a18927c57dffe3072..7694cf4d7d1fa9d5ff45f1cc626453c2f4dd7e65 100644 GIT binary patch delta 2035 zcmcgtZBSHY6n@^Xy}Ju2EUd5s&Wd7~BB+RnpIMp-QHC>$X0d~-Bgwk$@?nauCZM8b zdHqNvM9s+h;iv_uX+P8w%Sp4eX==*GsmYm|>W9scx}CcV8jdqH<4iMm&N=s-^PKaZ z=e_THJ03bS%zEzjv6BEE!M;P*0+nWiz+^Kj7@kP=JH1}N-|J1!%*@PkdfmPSOMGQ6 zPl-F%IZF?Sp~8$ReXdG2y%dRMSbk)s%3*!iGQ`f!FLhPAG8Vf$3o{DKeTzH`$7)!w zUohF(2K`6V4ECy?qm5)+^u^k6wpDM^MiiK_onbWV(XnKS&*v^F_qhH3TxVHDxqGRz z%C)H6?Q_m|&#&-0)3Tg?di4+PZNlqT>_mfxU6FVcjrvvXlHARZsz;eqg@!%)_vVSL zT6bFd*jLcBCNPo_Fe4qqz)`K|Ssor{M0(NuKn$W_LjvNklHLSZ0s>@ktRl#8j0i#Y z^?)Q0#6gh6dm*a{vIwOi$X}MBV~j3+ph)(o6{Mgy1|S{-2|OqSrM2CJgLO50nb2kM zgwVy-r1ZP1AJUbv1{TzymL{sEk-nw3Xh2;TDM7G6lN!|;GL)k6u80;`PG{eZKv9OllPc>$eR~+gn zMlvKf!{#t`o0@Eaq(X-=+HT$nvu%6}lx^ZM#QT9(Ofj668pc{+s^KgqqFrx@tYzi8 zGpZm!7+a+}0bV2$+o2T}$;6~$!)QhEtfVGrvK>ngg*JgW;3ljlJA-)1ghUo1F$#8M zqaQ{i4LP){W5}C}@feG#WFH{kL6orb zn9r`k&Hg|syMY&25R17)8IOX8$C2&}FHeDw4}m|xvrx{*pn^|ECC?*0150@kmhlBx z&MQ&PS7QZlBYhkzc?Z_;i>TpWqn2Mo9sdn$`7P86f%PH+^&$=%#5imeh1euq*dpA} zg$G+j6}E|0*e*6SGdPM}GgL9;lAJ>m=O72lBl0dI=ycuV|= z7OCNFX~O|IjdVWVk#lfJ&ck8p!Vy`D_oN^1%WAaBI<(2nXqVe@RPH8wKl$Fp2l6aV z$nS7c{(@8Tcbt};I3t7jNZF_3W0i~!H3;Wa20l?)_*CWNyqby6lnWQsi@2z&@wqxo zx)qnyX#S8ht()PDJF&0>BY8$Y3nr9(?h$;5E;UT@`)qr8!rWe zDO|lPPXo{C*(WKy-cvCt|HFC%54zsli0$ID6REc_im^#FdoP&pk{Zv5z;3ed@h9W{ zWG944h>J}R5d8*)27A)rPxau>;5p(B&v%mkjpTu_ghAobMs?cKo3OA>Bz8hM!c$Pv zvqt}aW_3?;p91%v9(E6vuz}-ZyJI%u!CbMq!=VLb*p>~bW`qr7d~ zgWowriZXj5SY}*??fTF|s>_as3JW&LPFX0i9zn{fK+|Qfgl1KwD>)dkxlAID7!fwh z$)I9vkwCLmAaj`OL#n+0LW%?ulh2fikhcWVN}CTE@b0TgPbnlIwuEeEFqu+{xQk-S ze5ma97sYCPU{cWQi2LZ@ys70kx1TlM(M^bLY!^md5d1?a@gXB{#w=>RVQ+tGueN(WAlkC zmt<&;OOay~0hfB0K9`;H6*(2*boY%&tmNOMi>|(P&eY60d4(~%Eb9G#D@AX&lAT#F zeX%gha1w?{Sf6GR^o6;Z*BP!8M2ls}>?|lxj_Z?_#<{&X+5RKPXJwkQZc%WeSa-|+ j#cuyM*3TaCQ8^!y-3ENS&$kD7M=Bm6#ZiuNoZ^1~DAW0= diff --git a/defects/ppsspp/test/PpssppKernelDedup.java b/defects/ppsspp/test/PpssppKernelDedup.java index 94c1a6634..a4a405f56 100644 --- a/defects/ppsspp/test/PpssppKernelDedup.java +++ b/defects/ppsspp/test/PpssppKernelDedup.java @@ -6,6 +6,7 @@ import java.util.*; * ppsspp-0001: sceKernelThread waitingThreads vector dedup O(W^2) * ppsspp-0002: sceKernelSemaphore waitingThreads vector dedup O(W^2) * ppsspp-0003: IRJit byPage_ block removal O(P*B) + * ppsspp-0004: sceKernelMutex waitingThreads vector dedup O(W^2) -- 4 sites */ public class PpssppKernelDedup { @@ -169,6 +170,47 @@ public class PpssppKernelDedup { if (ok) passed++; else failed++; } + // --- Test 7: ppsspp-0004 correctness (mutex, 4 sites, same pattern) --- + // Models sceKernelLockMutex / sceKernelLockMutexCB / + // sceKernelLockLwMutex / sceKernelLockLwMutexCB tight-loop timeout. + { + int nThreads = 30; + int nRetries = 20; // each thread retries 20x under timeout + List defect = new ArrayList<>(); + List fixed = new ArrayList<>(); + Set fixedSet = new HashSet<>(); + for (int retry = 0; retry < nRetries; retry++) { + for (int tid = 0; tid < nThreads; tid++) { + addWaiter_defective(defect, tid); + addWaiter_fixed(fixed, fixedSet, tid); + } + } + boolean ok = defect.size() == nThreads && fixed.size() == nThreads; + System.out.println((ok ? "PASS" : "FAIL") + " ppsspp-0004 correctness: mutex waiter dedup (4 sites)"); + if (ok) passed++; else failed++; + } + + // --- Test 8: ppsspp-0004 performance (mutex contention scale) --- + { + int W = 3000; + List defect = new ArrayList<>(); + long t0 = System.nanoTime(); + for (int tid = 0; tid < W; tid++) addWaiter_defective(defect, tid); + long defectNs = System.nanoTime() - t0; + + List fixed = new ArrayList<>(); + Set fixedSet = new HashSet<>(); + t0 = System.nanoTime(); + for (int tid = 0; tid < W; tid++) addWaiter_fixed(fixed, fixedSet, tid); + long fixedNs = System.nanoTime() - t0; + + double ratio = (double) defectNs / Math.max(fixedNs, 1); + boolean ok = ratio > 5.0; + System.out.printf("%s ppsspp-0004 performance: W=%d defect=%.1fms fixed=%.1fms ratio=%.1fx%n", + ok ? "PASS" : "FAIL", W, defectNs / 1e6, fixedNs / 1e6, ratio); + if (ok) passed++; else failed++; + } + System.out.printf("%n%d/%d tests passed%n", passed, passed + failed); if (failed > 0) System.exit(1); }