# raylib — CWE-407 Disclosure Brief **Project:** raylib **Disclosure date:** 2026-03-27 **Severity:** HIGH **Speedup:** varies by defect **Status:** PATCHED --- ## Finding Two independent defects in raylib use linear search where O(1) hash structures are appropriate. The first (raylib-0001) performs O(G) glyph index lookup per codepoint on every text draw call, making text rendering O(G) per character. The second (raylib-0002) uses `std::find`-style linear deduplication when generating cellular noise images, producing O(n²) sequence deduplication. ## The Defect(s) | ID | Location | Pattern | Complexity | |----|----------|---------|------------| | raylib-0001 | `src/rtext.c` | `GetGlyphIndex()` O(G) linear scan per codepoint per text draw call | O(G) per character, O(G×L) per string of length L | | raylib-0002 | `src/rshapes.c` | `GenerateImageCellular()` random-sequence dedup O(n²) linear find | O(n²) per image generation | ## Complexity Proof **raylib-0001:** Let G = number of glyphs loaded in a `Font` (typically 95–512 for ASCII/extended sets), L = length of string being rendered. `GetGlyphIndex()` searches for a codepoint by scanning the `Font.glyphs` array linearly: ```c for (int i = 0; i < font.glyphCount; i++) { if (font.glyphs[i].value == codepoint) return i; } ``` This is O(G) per call. `DrawText()` and related functions call `GetGlyphIndex()` once per character in the string: ``` L characters × O(G) scan = O(L×G) per DrawText call ``` With a pre-built `unordered_map` from codepoint to glyph index, each lookup is O(1): ``` O(G) to build map + O(L) for rendering = O(G + L) ``` For G=256, L=100: defective = 25,600 ops per DrawText; fixed = 356 ops. **raylib-0002:** Let n = number of seeds/points used in cellular noise generation. `GenerateImageCellular()` generates n random seed positions and deduplicates them using a growing array with linear-scan membership check: ``` Insert seed 1: check list of 0 → O(0) Insert seed 2: check list of 1 → O(1) ... Insert seed n: check list of n-1 → O(n-1) Total: 0+1+...+(n-1) = n(n-1)/2 = O(n²) ``` With a `HashSet` tracking inserted positions: O(n) total. ## Impact raylib-0001 affects any raylib application rendering text with non-trivial string length or font sizes, including game UIs, debug overlays, and text-heavy applications. Since `GetGlyphIndex()` is on the hot path of every `DrawText` call in the game loop (typically called 60+ times per second), the O(G) overhead per character is multiplied by frame rate and string length. raylib-0002 affects applications using `GenerateImageCellular()` for procedural texture generation. While not a per-frame operation, large cellular images with many seed points experience quadratic generation time. ## The Fix **raylib-0001:** At `LoadFont` time, build a `HashMap` alongside the glyphs array. In `GetGlyphIndex()`, perform a hash lookup instead of a linear scan. **raylib-0002:** In `GenerateImageCellular()`, replace the dedup array with an `unordered_set` (encoding x,y as a single 64-bit key). Check and insert in O(1). ## Patch ```diff // raylib-0001: rtext.c + // At LoadFont time: + // font.glyphMap = HashMap mapping codepoint -> index (built once) int GetGlyphIndex(Font font, int codepoint) { - for (int i = 0; i < font.glyphCount; i++) { - if (font.glyphs[i].value == codepoint) return i; - } - return 0; + return font.glyphMap[codepoint]; // O(1) hash lookup } // raylib-0002: rshapes.c - int *seeds = ...; int seedCount = 0; - // ... linear dedup ... - bool found = false; - for (int j = 0; j < seedCount; j++) - if (seeds[j] == pos) { found = true; break; } - if (!found) seeds[seedCount++] = pos; + unordered_set seedSet; + // O(1) insert-and-check: + if (seedSet.insert(pos).second) seeds[seedCount++] = pos; ``` ## What We Ask Please review, apply, and coordinate a 90-day disclosure window before public release. Reply to security@undefect.com. --- *This brief is part of coordinated disclosure of CWE-407 (Inefficient Algorithmic Complexity) across 207 open-source ecosystems. Full report: https://undefect.com*