Bug#1138575: jpeg-xl: diff for NMU version 0.11.2-5.1

Adrian Bunk <[email protected]>
Newsgroups gmane.linux.debian.devel.bugs.rc
Message-ID <an3M-BPQ1tta651t__29184.6859546908$1786629435$gmane$org@localhost>
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
jpeg-xl-0.11.2-5.1-nmu.diff (text/x-diff, 10.1 KB)
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.