OOB writes in the non-Cocoa AVIF, WebP, and JPEG XL decoders
Three decoders, three codec libraries, one out-of-bounds write
Component: WebCore image decoders | 3f8ee96
The GTK and WPE ports decode AVIF, WebP and JPEG XL through libavif, libwebp and libjxl; Cocoa ports route these formats through CoreGraphics and never run this code. Each decoder parses a container header that declares an overall canvas size, then hands individual frames to the codec library, which decodes at whatever dimensions are embedded in the frame's own bitstream — two numbers that are not guaranteed to agree, especially for adversarial files. ImageBackingStore is the shared pixel buffer decoders write into, sized to the declared container dimensions, and it also carries a per-frame rect used to composite frames during animation playback.
This commit fixes out-of-bounds writes in all three decoders. WebP and JPEG XL now clamp the pixel copy to the canvas-declared rect; AVIF takes the stricter route and fails the decode outright when the header size and libavif's decoded size disagree. Two supporting fixes land alongside: ImageBackingStore::create() returns nullptr on allocation failure instead of an unusable object, and the copy constructor now preserves the source's frame rect, which it had been dropping — the behaviour the new unit tests target.
Significance
Three separate decoder backends shared one flaw, and each is reachable from untrusted web content through a single <img> decode on the GTK and WPE ports. The invariant that a declared size matches the decode buffer's size was never enforced consistently, which is the shape that lets one class of malformed input produce heap corruption in three independent code paths.
Audit directions
The forward-facing pattern is a destination buffer sized from one source of truth and written using a length taken from another. Narrow: the remaining non-Cocoa decoders — GIF, PNG/APNG, JPEG, BMP, ICO — all follow the same container-header-then-frame-decode shape, so check each for a row-copy or memcpy bounded by a header-declared dimension rather than by the backing store's own rect. Wider: ImageBackingStore's contract just changed in two ways, and both are caller-visible — audit every create() call site for a null check that did not previously need to exist, and every copy of a backing store for code that had silently depended on the frame rect being reset. Widest: any decoder that consumes a third-party codec library inherits that library's freedom to return dimensions the caller did not ask for; the review tell is a copy loop whose bound comes from a struct field parsed out of the input rather than from the destination's own size.