Control: tags 1138575 + patch Control: tags 1138575 + pending Control: tags 1142476 + patch Control: tags 1142476 + pending
Dear maintainer, I've prepared an NMU for jpeg-xl (versioned as 0.11.2-5.1) and uploaded it to DELAYED/7. Please feel free to tell me if I should cancel it. cu Adrian
diffstat for jpeg-xl-0.11.2 jpeg-xl-0.11.2 changelog | 11 + patches/0001-Fix-possible-overflow-in-APNG-decoder-4870.patch | 52 ++++ patches/0002-Further-undisable-tests-4041.patch | 29 ++ patches/CVE-2025-70103.patch | 107 ++++++++++ patches/series | 3 5 files changed, 202 insertions(+) diff -Nru jpeg-xl-0.11.2/debian/changelog jpeg-xl-0.11.2/debian/changelog --- jpeg-xl-0.11.2/debian/changelog 2026-05-26 06:19:04.000000000 +0300 +++ jpeg-xl-0.11.2/debian/changelog 2026-08-13 16:09:49.000000000 +0300 @@ -1,3 +1,14 @@ +jpeg-xl (0.11.2-5.1) unstable; urgency=medium + + * Non-maintainer upload. + * CVE-2025-70103: Buffer overflow in PNM decoder + (Closes: #1138575) + * CVE-2026-52584: Buffer overflow in APNG decoder + (Closes: #1142476) + * Backport upstream fix for FTBFS on x32. + + -- Adrian Bunk <[email protected]> Thu, 13 Aug 2026 16:09:49 +0300 + jpeg-xl (0.11.2-5) unstable; urgency=medium * Team upload. diff -Nru jpeg-xl-0.11.2/debian/patches/0001-Fix-possible-overflow-in-APNG-decoder-4870.patch jpeg-xl-0.11.2/debian/patches/0001-Fix-possible-overflow-in-APNG-decoder-4870.patch --- jpeg-xl-0.11.2/debian/patches/0001-Fix-possible-overflow-in-APNG-decoder-4870.patch 1970-01-01 02:00:00.000000000 +0200 +++ jpeg-xl-0.11.2/debian/patches/0001-Fix-possible-overflow-in-APNG-decoder-4870.patch 2026-08-13 16:09:49.000000000 +0300 @@ -0,0 +1,52 @@ +From 2ded3a46db6cf1e60c673181d54276f702074020 Mon Sep 17 00:00:00 2001 +From: Eugene Kliuchnikov <[email protected]> +Date: Fri, 26 Jun 2026 11:24:49 +0200 +Subject: Fix possible overflow in APNG decoder (#4870) + +Fixes #4803. + +Root reason: copy-paste in RectT::IsSame +Drive-by: use a.IsInside(b) instead of a.Intersect(b).IsSame(a) +Drive-by: harden ysize check before copying +--- + lib/extras/dec/apng.cc | 3 ++- + lib/jxl/base/rect.h | 2 +- + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/lib/extras/dec/apng.cc b/lib/extras/dec/apng.cc +index 24b4795d..a8c5c5f3 100644 +--- a/lib/extras/dec/apng.cc ++++ b/lib/extras/dec/apng.cc +@@ -878,6 +878,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, + const RectT<uint64_t>& vp = current_frame.viewport; + size_t xsize = static_cast<size_t>(vp.xsize()); + size_t ysize = static_cast<size_t>(vp.ysize()); ++ JXL_ENSURE(ctx.frameRaw.rows.size() <= ysize); + JXL_ASSIGN_OR_RETURN(PackedImage image, + PackedImage::Create(xsize, ysize, format)); + for (size_t y = 0; y < ysize; ++y) { +@@ -962,7 +963,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, + /*delay_den=*/png_get_uint_16(payload.data() + 22), raw_viewport, + static_cast<DisposeOp>(dispose_op), static_cast<BlendOp>(blend_op)}; + +- if (!raw_viewport.Intersection(image_rect).IsSame(raw_viewport)) { ++ if (!raw_viewport.IsInside(image_rect)) { + // Cropping happened. + return JXL_FAILURE("PNG frame is outside of image rect"); + } +diff --git a/lib/jxl/base/rect.h b/lib/jxl/base/rect.h +index 31fe12df..3f6632ed 100644 +--- a/lib/jxl/base/rect.h ++++ b/lib/jxl/base/rect.h +@@ -109,7 +109,7 @@ class RectT { + + bool IsSame(const RectT& other) const { + return x0_ == other.x0_ && xsize_ == other.xsize_ && y0_ == other.y0_ && +- ysize_ <= other.ysize_; ++ ysize_ == other.ysize_; + } + + // Returns true if this Rect fully resides in the given image. ImageT could be +-- +2.47.3 + diff -Nru jpeg-xl-0.11.2/debian/patches/0002-Further-undisable-tests-4041.patch jpeg-xl-0.11.2/debian/patches/0002-Further-undisable-tests-4041.patch --- jpeg-xl-0.11.2/debian/patches/0002-Further-undisable-tests-4041.patch 1970-01-01 02:00:00.000000000 +0200 +++ jpeg-xl-0.11.2/debian/patches/0002-Further-undisable-tests-4041.patch 2026-08-13 16:09:49.000000000 +0300 @@ -0,0 +1,29 @@ +From a1fa160e1677ea214667bac3c155b4ce6989fa90 Mon Sep 17 00:00:00 2001 +From: Eugene Kliuchnikov <[email protected]> +Date: Tue, 7 Jan 2025 10:24:06 +0100 +Subject: Further undisable tests (#4041) + +--- + lib/jxl/jxl_test.cc | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/lib/jxl/jxl_test.cc b/lib/jxl/jxl_test.cc +index ddf98427..e8ae20b2 100644 +--- a/lib/jxl/jxl_test.cc ++++ b/lib/jxl/jxl_test.cc +@@ -391,7 +391,11 @@ TEST(JxlTest, RoundtripLargeFast) { + JXL_X86_64_TEST(JxlTest, RoundtripLargeEmptyModular) { + ThreadPoolForTests pool(8); + TestImage t; +- ASSERT_TRUE(t.SetDimensions(4096, 4096)); ++ // On 32-bit machines memory could be limited to 2GiB. Current use factor ++ // is about 20x, that means hat 6MPx image is about the max available size. ++ constexpr size_t kDim = sizeof(size_t) == 4 ? 2400 : 4096; ++ const size_t kLim = std::min<size_t>(kDim, 1024); ++ ASSERT_TRUE(t.SetDimensions(kDim, kDim)); + t.SetDataType(JXL_TYPE_UINT8); + ASSERT_TRUE(t.SetChannels(4)); + JXL_TEST_ASSIGN_OR_DIE(auto frame, t.AddFrame()); +-- +2.47.3 + diff -Nru jpeg-xl-0.11.2/debian/patches/CVE-2025-70103.patch jpeg-xl-0.11.2/debian/patches/CVE-2025-70103.patch --- jpeg-xl-0.11.2/debian/patches/CVE-2025-70103.patch 1970-01-01 02:00:00.000000000 +0200 +++ jpeg-xl-0.11.2/debian/patches/CVE-2025-70103.patch 2026-08-13 16:09:49.000000000 +0300 @@ -0,0 +1,107 @@ +From 49fb89f23473e57fa1dac416adce7c7679e5d051 Mon Sep 17 00:00:00 2001 +From: Eugene Kliuchnikov <[email protected]> +Date: Fri, 8 Aug 2025 12:29:08 +0200 +Subject: [PATCH] Take EC into accound when checking required PNM inmput length + (#4380) + +--- jpeg-xl-0.11.2.orig/lib/extras/dec/pnm.cc ++++ jpeg-xl-0.11.2/lib/extras/dec/pnm.cc +@@ -497,13 +497,26 @@ Status DecodeImagePNM(const Span<const u + } + } + ++ // No align - pixels are tightly packed. ++ constexpr size_t kAlign = 0; ++ size_t twidth = PackedImage::BitsPerChannel(data_type) / 8; + const JxlPixelFormat format{ + /*num_channels=*/num_interleaved_channels, + /*data_type=*/data_type, + /*endianness=*/header.big_endian ? JXL_BIG_ENDIAN : JXL_LITTLE_ENDIAN, +- /*align=*/0, ++ kAlign, + }; +- const JxlPixelFormat ec_format{1, format.data_type, format.endianness, 0}; ++ // EC format is same as color, but 1-channel. ++ JxlPixelFormat ec_format = format; ++ ec_format.num_channels = 1; ++ size_t required_pnm_size = ++ header.ysize * header.xsize * ++ (num_interleaved_channels + header.ec_types.size()) * twidth; ++ size_t pnm_remaining_size = bytes.data() + bytes.size() - pos; ++ if (pnm_remaining_size < required_pnm_size) { ++ return JXL_FAILURE("PNM file too small"); ++ } ++ + ppf->frames.clear(); + { + JXL_ASSIGN_OR_RETURN( +@@ -512,42 +525,47 @@ Status DecodeImagePNM(const Span<const u + ppf->frames.emplace_back(std::move(frame)); + } + auto* frame = &ppf->frames.back(); ++ uint8_t* out = reinterpret_cast<uint8_t*>(frame->color.pixels()); ++ std::vector<uint8_t*> ec_out; + for (size_t i = 0; i < header.ec_types.size(); ++i) { + JXL_ASSIGN_OR_RETURN( + PackedImage ec, + PackedImage::Create(header.xsize, header.ysize, ec_format)); + frame->extra_channels.emplace_back(std::move(ec)); ++ ec_out.emplace_back( ++ reinterpret_cast<uint8_t*>(frame->extra_channels.back().pixels())); ++ JXL_DASSERT(frame->extra_channels.back().stride == header.xsize * twidth); + } +- size_t pnm_remaining_size = bytes.data() + bytes.size() - pos; +- if (pnm_remaining_size < frame->color.pixels_size) { +- return JXL_FAILURE("PNM file too small"); +- } +- +- uint8_t* out = reinterpret_cast<uint8_t*>(frame->color.pixels()); +- std::vector<uint8_t*> ec_out(header.ec_types.size()); +- for (size_t i = 0; i < ec_out.size(); ++i) { +- ec_out[i] = reinterpret_cast<uint8_t*>(frame->extra_channels[i].pixels()); +- } ++ JXL_DASSERT(frame->color.stride == ++ header.xsize * num_interleaved_channels * twidth); + if (ec_out.empty()) { +- const bool flipped_y = header.bits_per_sample == 32; // PFMs are flipped +- for (size_t y = 0; y < header.ysize; ++y) { +- size_t y_in = flipped_y ? header.ysize - 1 - y : y; +- const uint8_t* row_in = &pos[y_in * frame->color.stride]; +- uint8_t* row_out = &out[y * frame->color.stride]; +- memcpy(row_out, row_in, frame->color.stride); ++ const bool flipped_y = (header.bits_per_sample == 32); // PFMs are flipped ++ if (!flipped_y) { ++ // When there are no EC and input is not flipped we can copy the whole ++ // image at once. ++ memcpy(out, pos, header.ysize * frame->color.stride); ++ } else { ++ // Otherwise copy row-by-row. ++ for (size_t y = 0; y < header.ysize; ++y) { ++ size_t y_out = header.ysize - 1 - y; ++ const uint8_t* row_in = pos + y * frame->color.stride; ++ uint8_t* row_out = out + y_out * frame->color.stride; ++ memcpy(row_out, row_in, frame->color.stride); ++ } + } + } else { ++ // In case there are EC, we have to deinterleave data pixel-wise. + JXL_RETURN_IF_ERROR(PackedImage::ValidateDataType(data_type)); +- size_t pwidth = PackedImage::BitsPerChannel(data_type) / 8; ++ size_t color_stride = twidth * num_interleaved_channels; + for (size_t y = 0; y < header.ysize; ++y) { + for (size_t x = 0; x < header.xsize; ++x) { + memcpy(out, pos, frame->color.pixel_stride()); +- out += frame->color.pixel_stride(); +- pos += frame->color.pixel_stride(); ++ out += color_stride; ++ pos += color_stride; + for (auto& p : ec_out) { +- memcpy(p, pos, pwidth); +- pos += pwidth; +- p += pwidth; ++ memcpy(p, pos, twidth); ++ pos += twidth; ++ p += twidth; + } + } + } diff -Nru jpeg-xl-0.11.2/debian/patches/series jpeg-xl-0.11.2/debian/patches/series --- jpeg-xl-0.11.2/debian/patches/series 2026-05-12 10:00:29.000000000 +0300 +++ jpeg-xl-0.11.2/debian/patches/series 2026-08-13 16:09:49.000000000 +0300 @@ -4,3 +4,6 @@ Fix-modular-on-big-endian-machines-4095.patch 0001-Fix-test-on-loong64-4654.patch disable_test.patch +CVE-2025-70103.patch +0001-Fix-possible-overflow-in-APNG-decoder-4870.patch +0002-Further-undisable-tests-4041.patch

