java-topology/whitepaper/outreach/cemu-0002.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

3 KiB
Raw Blame History

Cemu — CWE-407 Disclosure Brief

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

Finding

One O(n²) defect in Cemu (Wii U emulator) in the graphic pack texture rule filtering system. Four std::find() calls over std::vector<sint32> fire on every texture lookup during rendering. Patched.

The Defects

cemu-0002 (PATCHED — HIGH): src/Cafe/GraphicPack/GraphicPack2.h and src/Cafe/HW/Latte/Core/LatteTexture.cpp:1243

// In LatteTexture_init — fires on every texture lookup during rendering:
if (!rule.filter_settings.format_whitelist.empty()
    && std::find(rule.filter_settings.format_whitelist.begin(),
                 rule.filter_settings.format_whitelist.end(),
                 (uint32)format) == rule.filter_settings.format_whitelist.end())  // O(F) linear scan
    continue;

Four filter lists (format_whitelist, format_blacklist, tilemode_whitelist, tilemode_blacklist) stored as std::vector<sint32>. Each texture lookup scans all four lists linearly using std::find(). With R texture rules and F filter entries per list, each texture operation costs O(R × F) per filter check, four checks total: O(4 × R × F).

Complexity Proof

At R=20 rules, F=16 formats per whitelist/blacklist:

  • Defective: 20 × 4 × 16 = 1,280 comparisons per texture lookup
  • Fixed: 20 × 4 × 1 = 80 hash lookups
  • 16× op reduction per texture lookup. Fires thousands of times per frame in graphically intensive Wii U titles.

Impact

Cemu emulates Wii U games at high resolution using graphic packs with texture replacement rules. Games like Breath of the Wild use dozens of texture rules with format and tilemode filters. Every texture lookup during rendering hits all four filter paths. At 60 fps with thousands of textures per frame, this path fires millions of times per second.

The Fix

Replace std::vector<sint32> with std::unordered_set<sint32> for all four filter lists:

// Before
std::vector<sint32> format_whitelist{};
std::find(rule.filter_settings.format_whitelist.begin(),
          rule.filter_settings.format_whitelist.end(), (uint32)format)

// After — O(1) hash lookup
std::unordered_set<sint32> format_whitelist{};
rule.filter_settings.format_whitelist.find((sint32)format)

Parse-time conversion from ParseList<sint32> result to unordered_set at graphic pack load.

Patch

Fix available: defects/cemu-0002/patch/cemu-0002.patch

Three-file patch across GraphicPack2.h, LatteTexture.cpp, and GraphicPack2.cpp.

Unit test: 6/6 pass. 16× speedup at 20 rules × 16 formats.

What We Ask

A patch is ready for review.

  1. Confirm receipt and assign a GitHub issue reference (cemu-project/Cemu).
  2. Assess severity — fires on every texture lookup during rendering, thousands of times per frame.
  3. Coordinate a disclosure date — we are targeting 90 days from first contact.
  4. We will credit the Cemu team in the public disclosure. Preferred acknowledgment format welcome.

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