java-topology/defects/bullet/bullet-0001.md

76 lines
2.9 KiB
Markdown

# bullet-0001: O(N²) ghost-object overlap tracking — `findLinearSearch` in `addOverlappingObjectInternal`
**Severity:** HIGH
**CWE:** CWE-407 (Algorithmic Complexity — Insufficient Control of Quadratic Complexity)
**Target:** bulletphysics/bullet3
**File:** `src/BulletCollision/CollisionDispatch/btGhostObject.cpp`
**Lines:** 37, 49, 75, 90
**Status:** PATCHED (unit test PASS)
## Description
`btGhostObject::addOverlappingObjectInternal` and `removeOverlappingObjectInternal` use
`btAlignedObjectArray::findLinearSearch` to maintain the `m_overlappingObjects` list:
```cpp
// btGhostObject.cpp:37
///if this linearSearch becomes too slow (too many overlapping objects) we should add a more appropriate data structure
int index = m_overlappingObjects.findLinearSearch(otherObject);
if (index == m_overlappingObjects.size())
{
//not found
m_overlappingObjects.push_back(otherObject);
}
```
These methods are called every physics step via `btGhostPairCallback::addOverlappingPair`
and `removeOverlappingPair`, which are invoked by the broadphase
`processAllOverlappingPairs`. For a ghost object with N overlapping bodies, each
add/remove is O(N). When N new bodies enter the ghost zone in one step, the
total work is O(N²).
The developer comment acknowledges the defect explicitly:
> "if this linearSearch becomes too slow (too many overlapping objects) we should add a more appropriate data structure"
Real-world impact: a ghost object used as a trigger zone (e.g. a character controller,
a portal, a sensor area) that overlaps a crowd of 500+ NPCs performs 250 000+
comparisons per step just for deduplication.
The same pattern appears in `btPairCachingGhostObject` (lines 75, 90).
## Root Cause
`m_overlappingObjects` is a `btAlignedObjectArray<btCollisionObject*>` — a plain
array with no membership index. The dedup check is O(N) per call.
## Fix
Maintain a parallel `btHashMap<btHashPtr, int> m_overlappingIndex` that maps each
`btCollisionObject*` to its index in `m_overlappingObjects`. Replace
`findLinearSearch` with an O(1) hash lookup. On `removeOverlappingObjectInternal`,
perform the existing swap-with-last removal and update the displaced element's
entry in the hash map.
**Patch:** `patch/bullet-0001-ghostobject-hashset-overlapping.patch`
## Complexity
| Scenario | Before | After |
|----------|--------|-------|
| N bodies overlapping ghost, add all | O(N²) | O(N) |
| N bodies overlapping ghost, remove all | O(N²) | O(N) |
| Per-step broadphase at N=500 | ~250 000 comparisons | ~500 ops |
| Speedup at N=500 | — | ~250x |
## Unit Test
`unit/BulletAlgorithm.java` — tests bullet-0001 (and bullet-0002, bullet-0003).
Correctness: both paths produce identical final states with duplicate inputs.
Performance: op-count ratio >= 5x verified at P=500 (measured 250x).
Run:
```
javac -d /tmp/out defects/bullet/unit/BulletAlgorithm.java
java -cp /tmp/out unit.BulletAlgorithm
```
Output: `6/6 PASS`