Skip to content

fix: decode a copy of JPEG data - #3603

Open
iliev-blnk wants to merge 1 commit into
diegomura:masterfrom
iliev-blnk:fix/jpeg-buffer-mutation
Open

iliev-blnk wants to merge 1 commit into
diegomura:masterfrom
iliev-blnk:fix/jpeg-buffer-mutation

Conversation

@iliev-blnk

Copy link
Copy Markdown

Fixes #3567. Credit to @fuunnx for tracking down the root cause.

jay-peg's EXIF parser reads each tag ID through buffer.slice(), which returns a view for a Node Buffer, and then calls .reverse() on it for little-endian (II) data. So every decode() swaps the tag bytes in the image data in place: 12 01 (orientation) becomes 01 12.

@react-pdf/image decodes during resolution and then passes the same buffer to pdfkit. pdfkit's parseExifOrientation can't find tag 0x0112 anymore and falls back to 1. Layout has already swapped width and height for the rotated image, so the photo is drawn unrotated and stretched.

The fix decodes a copy in JPEG's constructor.

Repro

A 40脳20 JPEG with little-endian EXIF orientation 6, rendered 4 times in one process, logging image.orientation from PDFDocument.prototype.openImage:

  • master: 1,1,1,1 (pdfkit reads 6 from the untouched file)
  • this branch: 6,6,6,6

With pdfkit 0.20.1 this hits every render, not only the later ones as in the issue. The issue was on the older @react-pdf/pdfkit. pdfkit 0.20.1's orientation parser only reads the buffer, so the IMAGE_CACHE clone proposed in the issue isn't needed anymore, and this PR doesn't copy cached buffers on every render.

Tests

packages/image/tests/jpeg.test.ts uses the fixture above. It checks that the dimensions are swapped, that the image data is unchanged after parsing, and that a second parse of the same data still reads the orientation. Without the fix, the last two tests fail. All image tests pass (55).

jay-peg could also be fixed at the source by reversing a copy of the tag bytes (Uint8Array.prototype.slice.call(tagAddress).reverse()). I'm happy to open that PR as well.

jay-peg reverses the tag bytes of little-endian EXIF data in place, so
after @react-pdf/image read the orientation, pdfkit could no longer find
the orientation tag and drew rotated photos unrotated into a box sized
for the rotated image.

Fixes diegomura#3567
@vercel

vercel Bot commented Oct 22, 2026

Copy link
Copy Markdown

@iliev-blnk is attempting to deploy a commit to the diegomura Team on Vercel.

A member of the Team first needs to authorize it.

This branch has not been deployed

No deployments
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.

JPEG buffer mutated in place during EXIF parsing: corrupts orientation across renders in long-lived processes

1 participant