java-topology/whitepaper/outreach/cocos2d-0001.md
russell@unturf.com aeb084c9ae feat: add 30 outreach docs (batches 9-10)
Batch 9 (15): bun, bzflag (3), cake_wallet (4), calligra, caprice32 (2),
  cataclysm (3), cemu
Batch 10 (15): cemu-0002, citra, clickhouse-java, cmake (3), cocos2d (3),
  conduit, cura (2), curaengine, clamav, contiki
2026-04-14 19:51:36 -04:00

2.9 KiB
Raw Blame History

Cocos2d-x — CWE-407 Disclosure Brief (cocos2d-0001)

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

Finding

One O(n²) defect in Cocos2d-x in the event dispatcher system. std::find() over _toRemovedListeners (a std::vector<EventListener*>) fires on every listener removal and every listener cleanup pass. Patched.

The Defects

cocos2d-0001 (PATCHED — HIGH): cocos/base/CCEventDispatcher.cpp:607 and CCEventDispatcher.h

// In removeEventListener — fires on every listener removal:
if (std::find(_toRemovedListeners.begin(), _toRemovedListeners.end(), listener)
    != _toRemovedListeners.end())  // O(R) linear scan
    return;

_toRemovedListeners is std::vector<EventListener*>. Three call sites use std::find() for membership checks: removeEventListener() (guards against double-remove), and two locations in updateListeners() that clean up removed listeners from scene-graph and fixed-priority lists.

During event dispatch with many listener additions and removals per frame, the vector grows and each std::find() costs O(R) where R = pending removals. Total cost per frame: O(E × R) where E = events dispatched.

Complexity Proof

At R=100 pending removals, E=50 events per frame:

  • Defective: 50 × 100 = 5,000 comparisons per frame + cleanup passes
  • Fixed: 50 × 1 = 50 hash lookups
  • 100× op reduction per frame.

Impact

Cocos2d-x powers thousands of mobile and desktop games worldwide. The event dispatcher handles touch input, keyboard events, physics callbacks, and custom game events. Games with many interactive objects (puzzle games with hundreds of tiles, strategy games with many units, UI-heavy games) create and destroy event listeners frequently. Every frame that processes events pays the linear scan cost.

The Fix

Replace std::vector<EventListener*> with std::unordered_set<EventListener*>:

// Before
std::vector<EventListener*> _toRemovedListeners;
std::find(_toRemovedListeners.begin(), _toRemovedListeners.end(), listener)
_toRemovedListeners.push_back(l);

// After — O(1) lookup, insert, erase
std::unordered_set<EventListener*> _toRemovedListeners;
_toRemovedListeners.count(listener)
_toRemovedListeners.insert(l);

Patch

Fix available: defects/cocos2d-0001/patch/cocos2d-0001.patch

Two-file patch across CCEventDispatcher.h and CCEventDispatcher.cpp.

Unit test: pass. 100× op reduction at 100 pending removals.

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign a GitHub issue reference (cocos2d/cocos2d-x).
  2. Assess severity — fires on every event dispatch frame with listener churn.
  3. Coordinate a disclosure date — we are targeting 90 days from first contact.
  4. We will credit the Cocos2d-x team in the public disclosure. Preferred acknowledgment format welcome.

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