A malformed WebP chunk size hangs the agent forever: RIFF size decoded with a signed shift #1554
Jiaaqiliu
started this conversation in
Bug reports
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Environment. prime-agent 0.7.3,
mainat f8f0036, Node 22.17.0, macOS 15 (darwin 25.5.0).Summary
findWebpTiffOffsetdecodes the RIFF chunk size with a signed shift, so a size with the high bit set becomes negative and the scan loop stops advancing. The loop is synchronous, so it pins a core and blocks the event loop: the TUI stops repainting and Ctrl-C does nothing. Because the function never returns, thetry/catchinresizeImagenever runs.Any image the agent handles goes through this, including one it merely finds in the repository it is pointed at.
Reproduction
utils/exif-orientation.ts:65:Running the function body against a minimal RIFF/WEBP container with one chunk:
08 00 00 00F8 FF FF FFoffset + 8 - 8 + -0— offset never changesF9 FF FF FFoffset + 8 - 7 + -1— offset never changes00 00 00 80A valid WebP with eight trailing bytes — a 4-byte chunk id plus
F8 FF FF FF— and a widened RIFF size is enough to trigger it. Decoders stop at theVP8chunk, so the file still opens normally in every other viewer.Reachability
getExifOrientationis called fromutils/image-resize.ts:74andutils/image-convert.ts:26, which are reached fromcli/file-processor.ts:48(a file passed on the command line),interactive-mode.ts:4301(a clipboard image paste), and the image-attachment path generally.Related, same file
readOrientationFromTiff's little-endianread32at:17is missing the>>> 0that its big-endian sibling on the next line has, soifdOffsetcan go negative past the upper-bound-only guard on:23. That one degrades toentryCount = 0rather than hanging, but it is the same class of mistake.Suggested fix
Read the size unsigned (
>>> 0) and refuse to continue when the computed next offset does not move forward. The regression test has to drive the function from a child process, because no in-process timeout can interrupt a synchronous loop.I have a fix with a regression test on a branch:
fix/webp-exif-scan-hang.npm run checkpasses and the surrounding suites still pass. I opened it as a PR first and the contribution gate closed it, which is what CONTRIBUTING.md says should happen, so I am bringing it here instead. Happy to leave it as is, adjust it, or drop it entirely if you would rather fix this differently.All reactions