From 5eda3cf0127fe59e60d0533d6243f1b30c629826 Mon Sep 17 00:00:00 2001 From: rootkiller6788 Date: Thu, 20 Aug 2026 22:35:52 +0800 Subject: [PATCH] Reject mismatched frame size in libjpeg encoder The extras JPEG encoder reads ppf.info.ysize rows and ppf.info.xsize columns from the frame's pixel buffer, but the shared VerifyImageSize check for frame size matching the image size is commented out. A PackedPixelFile whose info dimensions are larger than the frame buffer causes an out-of-bounds read in the encoder copy loops. Add an explicit frame size check in JPEGEncoder::Encode and a test that mirrors the existing jpegli encoder mismatch test. --- lib/extras/enc/jpg.cc | 4 ++++ lib/extras/jpegli_test.cc | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/lib/extras/enc/jpg.cc b/lib/extras/enc/jpg.cc index 43fb24b0..35b01b61 100644 --- a/lib/extras/enc/jpg.cc +++ b/lib/extras/enc/jpg.cc @@ -623,6 +623,10 @@ class JPEGEncoder : public Encoder { encoded_image->bitstreams.reserve(ppf.frames.size()); for (const auto& frame : ppf.frames) { JPEGLI_RETURN_IF_ERROR(VerifyPackedImage(frame.color, ppf.info)); + if (frame.color.xsize != ppf.info.xsize || + frame.color.ysize != ppf.info.ysize) { + return JPEGLI_FAILURE("Frame size does not match image size."); + } encoded_image->bitstreams.emplace_back(); JPEGLI_RETURN_IF_ERROR(EncodeImageJPG( frame.color, ppf.info, ppf.icc, ppf.metadata.exif, jpeg_encoder, diff --git a/lib/extras/jpegli_test.cc b/lib/extras/jpegli_test.cc index 65541c58..80c01d0d 100644 --- a/lib/extras/jpegli_test.cc +++ b/lib/extras/jpegli_test.cc @@ -380,6 +380,38 @@ TEST(JpegliTest, JpegliEncodeRejectsMismatchedFrameSize) { EXPECT_FALSE(EncodeJpeg(ppf_in, settings, nullptr, &compressed)); } +TEST(JpegliTest, JpegliLibjpegEncodeRejectsMismatchedFrameSize) { + TEST_LIBJPEG_SUPPORT(); + std::string testimage = "jxl/flower/flower_small.rgb.depth8.ppm"; + PackedPixelFile ppf_in; + ASSERT_TRUE(ReadTestImage(testimage, &ppf_in)); + ASSERT_FALSE(ppf_in.frames.empty()); + const uint32_t orig_xsize = ppf_in.info.xsize; + const uint32_t orig_ysize = ppf_in.info.ysize; + ASSERT_EQ(static_cast(orig_xsize), ppf_in.frames[0].color.xsize); + ASSERT_EQ(static_cast(orig_ysize), ppf_in.frames[0].color.ysize); + + std::vector compressed; + + // Sanity: original ppf encodes successfully. + EXPECT_TRUE(EncodeWithLibjpeg(ppf_in, 90, &compressed)); + + // Mismatch ysize (info > frame): pre-fix this read past the pixel buffer + // in the encoder copy loops. + ppf_in.info.ysize = orig_ysize + 1; + EXPECT_FALSE(EncodeWithLibjpeg(ppf_in, 90, &compressed)); + + // Mismatch xsize (info > frame). + ppf_in.info.ysize = orig_ysize; + ppf_in.info.xsize = orig_xsize + 1; + EXPECT_FALSE(EncodeWithLibjpeg(ppf_in, 90, &compressed)); + + // Mismatch in the other direction (info < frame). + ppf_in.info.xsize = orig_xsize - 1; + ppf_in.info.ysize = orig_ysize; + EXPECT_FALSE(EncodeWithLibjpeg(ppf_in, 90, &compressed)); +} + struct TestConfig { int num_colors; int passes;