sdl/box2d: CWE-407 findings
sdl-0001: SDL_gamepad.c SDL_PrivateAddMappingForGUID — O(M) tail walk of s_pSupportedGamepads linked list on every mapping insert → O(M²) bulk load. SDL_GameControllerDB ships >30 000 entries; fix: tail pointer s_pLastSupportedGamepad. box2d-0001: broad_phase.c b2UnBufferMove — linear scan through moveArray to find proxy key on destroy (acknowledged by code comment) → O(N²) on bulk destroy. Fix: index map (proxyKey → slot) for O(1) swap-remove.
This commit is contained in:
parent
db92c9428a
commit
0d33225dcc
4 changed files with 337 additions and 0 deletions
|
|
@ -0,0 +1,61 @@
|
|||
# UNDF: (leave blank)
|
||||
Box2D v3 CWE-407: b2UnBufferMove — linear scan through moveArray to find proxy → O(N²) on bulk destroy
|
||||
|
||||
b2UnBufferMove (broad_phase.c) is called from b2BroadPhase_DestroyProxy.
|
||||
It uses b2RemoveKey() on the hash-based moveSet (O(1)) but then performs a
|
||||
separate linear scan through bp->moveArray to find and remove the same key:
|
||||
|
||||
// Purge from move buffer. Linear search.
|
||||
// todo if I can iterate the move set then I don't need the moveArray
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (bp->moveArray.data[i] == proxyKey) {
|
||||
b2IntArray_RemoveSwap(&bp->moveArray, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
The code comment acknowledges the linear scan. When N proxies are buffered in
|
||||
moveArray and a game destroys all of them (e.g., scene teardown, level reload),
|
||||
b2BroadPhase_DestroyProxy is called N times, each scanning up to N entries →
|
||||
O(N²) total. At N=10 000 (large physics scene), this is 100 000 000 iterations.
|
||||
|
||||
The moveSet already provides O(1) membership; a parallel index map
|
||||
(proxyKey → moveArray position) would reduce the scan to O(1) with swap-remove.
|
||||
|
||||
Severity: HIGH — hits on every scene reload / bulk body destruction.
|
||||
Complexity: O(N²) → O(1) per remove with an index map.
|
||||
|
||||
--- a/src/broad_phase.c
|
||||
+++ b/src/broad_phase.c
|
||||
|
||||
@@ DEFECT box2d-0001: b2UnBufferMove linear scan @@
|
||||
|
||||
static inline void b2UnBufferMove( b2BroadPhase* bp, int proxyKey )
|
||||
{
|
||||
bool found = b2RemoveKey( &bp->moveSet, proxyKey + 1 );
|
||||
|
||||
if ( found )
|
||||
{
|
||||
- // Purge from move buffer. Linear search.
|
||||
- // todo if I can iterate the move set then I don't need the moveArray
|
||||
- int count = bp->moveArray.count;
|
||||
- for ( int i = 0; i < count; ++i )
|
||||
- {
|
||||
- if ( bp->moveArray.data[i] == proxyKey )
|
||||
- {
|
||||
- b2IntArray_RemoveSwap( &bp->moveArray, i );
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
+ // FIX box2d-0001: O(1) lookup via index map (proxyKey → slot in moveArray)
|
||||
+ // Requires a parallel b2HashTable<int,int> moveArrayIndex field in b2BroadPhase.
|
||||
+ //
|
||||
+ // Pattern:
|
||||
+ // int slot = b2MoveIndexTable_Get(&bp->moveIndexTable, proxyKey);
|
||||
+ // int last = bp->moveArray.data[bp->moveArray.count - 1];
|
||||
+ // bp->moveArray.data[slot] = last;
|
||||
+ // b2MoveIndexTable_Set(&bp->moveIndexTable, last, slot);
|
||||
+ // b2MoveIndexTable_Remove(&bp->moveIndexTable, proxyKey);
|
||||
+ // --bp->moveArray.count;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue