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 40191d5d2..7694cf4d7 100644 Binary files a/defects/ppsspp/test/PpssppKernelDedup.class and b/defects/ppsspp/test/PpssppKernelDedup.class differ 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); }