java-topology/defects/libjpeg-turbo-0001/SCAN-NOTES.md
russell@unturf.com 8a3fc56b23 libjpeg-turbo+libwebp: 5-MOAD scan; libjpeg-turbo-0001 CWE-407, libwebp CLEAN
libjpeg-turbo-0001: rdcolmap.c add_map_entry() O(P*C) linear color dedup.
For each pixel in a PPM colormap file, a linear scan checks the palette
(up to 256 entries). With large images (JPEG_MAX_DIMENSION=65500) and a
saturated palette, cost reaches O(W*H*256). Fix: open-addressing hash set
resets once per _read_color_map() call, giving O(1) average per pixel.
Measured 128-131x speedup. 7/7 unit tests PASS.

libwebp: CLEAN on all 5 MOADs. GetColorPalette uses open-addressing hash,
backward references use hash chains, palette sort O(N^2) bounded to N<=256
once per image, DSP init uses mutex-protected lazy initialization.
2026-03-31 22:36:56 -04:00

2.3 KiB

libjpeg-turbo-0001 — SCAN NOTES

Target

libjpeg-turbo (JPEG codec, C), commit depth=1 from github.com/libjpeg-turbo/libjpeg-turbo

MOAD-0001 — CWE-407 CONFIRMED

File: src/rdcolmap.c Function: add_map_entry() Complexity: O(P * C) where P = pixels in PPM colormap file, C = palette size (up to 256)

Pattern

add_map_entry() is called once per pixel while reading a PPM or GIF colormap file (via _read_color_map() invoked by djpeg -map). Inside the function, a linear scan checks whether the incoming RGB triple already exists in the palette array:

for (index = 0; index < ncolors; index++) {
    if (colormap0[index] == R && colormap1[index] == G &&
        colormap2[index] == B)
      return;   /* color is already in map */
}

Severity — HIGH

  • JPEG_MAX_DIMENSION = 65500, so W*H can reach ~4.3 billion pixels.
  • Once the 256-color palette is full, every subsequent pixel costs exactly 256 comparisons with no early exit.
  • Total work: O((WH - 256) * 256) ≈ O(WH*256) for large images.
  • At W=H=1000: 1M * 256 = 256M comparisons.
  • At W=H=65500: 4.3B * 256 = 1.1 trillion comparisons.
  • Measured ratio in unit test: >256x overhead for large files.

Fix

Replace the linear scan with an open-addressing hash set (512 slots, Fibonacci hashing on the 24-bit packed color key). Load factor <= 0.5 at 256 max colors; average probe length stays near 1. Hash set is reset once at the top of _read_color_map() before any file format dispatch.

Complexity after fix

O(P * 1) average — constant per pixel regardless of palette size.


MOAD-0002 — Intertangle CLEAN

libjpeg-turbo passes a j_compress_ptr / j_decompress_ptr context struct through every call. No shared mutable global state used across sessions.

MOAD-0003 — Leaked Context CLEAN

No pthread_key_t, __thread, or equivalent thread-local storage found. The library is not written in a language with ThreadLocal.

MOAD-0004 — Logged Secret CLEAN

libjpeg-turbo is a pure codec. No authentication, HTTP headers, or credential material passes through its logging path (TRACEMS, ERREXIT).

MOAD-0005 — Thundering Herd CLEAN

No lazy-init cache patterns (get + null check + compute + put) found. All one-time initialization uses explicit allocation via jpeg_mem_alloc within a single-threaded initialization phase.