java-topology/whitepaper/outreach/bullet3.md
russell@unturf.com 9a78d1afbe feat: add 15 outreach docs (15 defects) for 1-patch projects
0ad (4), aranym, ardour, argo-cd, aria2, azahar, bcoin, bind9,
btcpayserver (3), bullet3. Mix of CWE-407 and CWE-312.
2026-04-14 14:33:17 -04:00

3.6 KiB
Raw Blame History

Bullet Physics (bullet3) — CWE-407 Disclosure Brief (bullet3-0001)

2026-04-13 · Patch available — awaiting upstream merge

Finding

One O(N²) defect in Bullet Physics's ghost object overlap tracking. btGhostObject::addOverlappingObjectInternal and removeOverlappingObjectInternal use findLinearSearch for membership testing on every broadphase callback, making per-tick pair updates quadratic in the number of overlapping objects.

The Defect

bullet3-0001 (PATCHED — HIGH): src/BulletCollision/CollisionDispatch/btGhostObject.cpp:32,44

// In btGhostObject::addOverlappingObjectInternal():
int index = m_overlappingObjects.findLinearSearch(otherObject);
if (index == m_overlappingObjects.size())
{
    m_overlappingObjects.push_back(otherObject);
}

// In btGhostObject::removeOverlappingObjectInternal():
int index = m_overlappingObjects.findLinearSearch(otherObject);
if (index < m_overlappingObjects.size())
{
    m_overlappingObjects[index] = m_overlappingObjects[m_overlappingObjects.size() - 1];
    m_overlappingObjects.pop_back();
}

m_overlappingObjects is a btAlignedObjectArray<btCollisionObject*>. findLinearSearch scans the entire array on every broadphase add/remove callback. Both btGhostObject and btPairCachingGhostObject exhibit the same pattern. With P overlapping objects, each tick costs O(P) per callback × P callbacks = O(P²) total.

Complexity Proof

At P=500 overlapping objects per ghost object at 60Hz:

  • Defective: 500 × 250 average = 125,000 pointer comparisons per tick per ghost
  • Fixed: 500 × O(1) hash lookups = 500 operations per tick per ghost
  • ~250× op reduction per tick per ghost object.

Impact

Bullet Physics is used in games, robotics simulation (ROS/Gazebo), and scientific computing. Ghost objects serve as triggers, sensors, and area detectors. Any scene with many overlapping physics bodies (crowds, particle effects, explosion radii, sensor zones) hits the quadratic path every physics tick. The original source code contains a comment acknowledging this: "if this linearSearch becomes too slow (too many overlapping objects) we should add a more appropriate data structure."

The Fix

Add a btHashMap<btHashPtr, int> shadow index mapping object pointers to their array indices:

// Before
int index = m_overlappingObjects.findLinearSearch(otherObject);

// After
// CWE-407 fix: btHashMap for O(1) lookup instead of O(N) findLinearSearch.
btHashMap<btHashPtr, int> m_overlappingObjectsIndex;
if (m_overlappingObjectsIndex.find(btHashPtr(otherObject)) == NULL)
{
    int index = m_overlappingObjects.size();
    m_overlappingObjects.push_back(otherObject);
    m_overlappingObjectsIndex.insert(btHashPtr(otherObject), index);
}

Removal uses swap-with-last and updates the moved element's index entry in the hash map.

Patch

Fix available: defects/bullet3/patch/bullet3-0001-ghost-object-overlapping-hashmap.patch

Two-file patch across btGhostObject.h and btGhostObject.cpp. Adds m_overlappingObjectsIndex hash map, updates all four add/remove methods (two in btGhostObject, two in btPairCachingGhostObject).

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign a GitHub issue reference (bulletphysics/bullet3).
  2. Assess severity — fires every physics tick for every ghost object with overlaps.
  3. Coordinate a disclosure date — we target 90 days from first contact.
  4. We will credit the Bullet Physics team in the public disclosure. Preferred acknowledgment format welcome.

Contact: see cover email. This brief is confidential until coordinated disclosure.