Skip to content

fix: four critical security/DoS bugs (unsigned overflow, LZW stack overflow, WebP parser)#102

Merged
Hexagon merged 3 commits into
mainfrom
copilot/update-critical-entries
Apr 9, 2026
Merged

fix: four critical security/DoS bugs (unsigned overflow, LZW stack overflow, WebP parser)#102
Hexagon merged 3 commits into
mainfrom
copilot/update-critical-entries

Conversation

Copilot AI commented Apr 9, 2026

Copy link
Copy Markdown
Contributor

Four critical issues from TODO.md that allow crafted image files to crash or hang the decoder.

C1 & C2 — Signed 32-bit overflow in readUint32LE / readUint32

JS bitwise-OR always produces a signed 32-bit result. When bit 31 is set, the returned value is negative, breaking offset/length arithmetic across BMP, ICO, TIFF, and PNG decoders — e.g. a crafted PNG chunk length held negative causes an infinite parse loop (CPU DoS).

// Before — returns negative for values ≥ 0x80000000
return data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24);

// After
return (data[o] | (data[o+1] << 8) | (data[o+2] << 16) | (data[o+3] << 24)) >>> 0;

Fixed in both src/utils/byte_utils.ts (LE) and src/formats/png_base.ts (BE).

H4 — Duplicate signed readUint32LE in WebP decoder

src/utils/webp_decoder.ts defined its own readUint32LE that still returned a signed 32-bit value. A crafted WebP with a high-bit chunk size could make pos + chunkSize go negative, silently short-circuiting the parse loop. The local copy has been removed and replaced with an import of the shared (now unsigned-safe) readUint32LE from src/utils/byte_utils.ts.

C3 — LZW call-stack overflow on adversarial GIF

output.push(...entry) spreads dictionary entries as function arguments. A crafted GIF with a long repeated run triggers RangeError: Maximum call stack size exceeded, crashing the runtime process. Fixed at three call sites in src/utils/lzw.ts and src/utils/gif_decoder.ts:

// Before
output.push(...entry);

// After
for (const b of entry) output.push(b);

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Addresses critical decoder crash/DoS vectors caused by signed 32-bit overflow in uint32 readers and by large-argument spreads in LZW/GIF parsing code paths.

Changes:

  • Ensure 32-bit “unsigned” reads return unsigned values via >>> 0 in readUint32LE and PNG big-endian readUint32.
  • Replace array.push(...typedArray) patterns with element-wise loops to avoid RangeError call-stack/argument overflows during adversarial LZW/GIF decoding.
  • Document the security fixes in CHANGELOG.md and reformat the relevant TODO.md entries.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
TODO.md Markdown line-wrapping / table formatting updates for tracked issues.
src/utils/lzw.ts Prevent LZW output expansion from using spread-into-arguments.
src/utils/gif_decoder.ts Avoid spreading sub-block bytes into blocks.push(...).
src/utils/byte_utils.ts Make readUint32LE return a true unsigned 32-bit number (>>> 0).
src/formats/png_base.ts Make big-endian uint32 reads return unsigned (>>> 0).
CHANGELOG.md Add security-related “Fixed” entries describing the above changes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CHANGELOG.md Outdated
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@Hexagon Hexagon marked this pull request as ready for review April 9, 2026 16:49
@Hexagon

Hexagon commented Apr 9, 2026

Copy link
Copy Markdown
Member

@copilot have a look at the ci failures

Copilot AI commented Apr 9, 2026

Copy link
Copy Markdown
Contributor Author

@copilot have a look at the ci failures

Fixed in 8ac9b2e:

  1. CI failuredeno fmt --check was failing on CHANGELOG.md because the formatter wanted to reflow some wrapped lines. Fixed by running deno fmt on the file.

  2. webp_decoder.ts (H4, as flagged in the code review) — removed the local readUint32LE that still had the signed-overflow bug and replaced it with an import of the shared (now unsigned-safe) version from src/utils/byte_utils.ts. Also updated the CHANGELOG to document this fix.

Copilot AI changed the title fix: three critical security/DoS bugs (unsigned overflow, LZW stack overflow) fix: four critical security/DoS bugs (unsigned overflow, LZW stack overflow, WebP parser) Apr 9, 2026
Copilot AI requested a review from Hexagon April 9, 2026 17:10
@Hexagon Hexagon merged commit dc6918f into main Apr 9, 2026
5 checks passed
@Hexagon Hexagon deleted the copilot/update-critical-entries branch April 9, 2026 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants