From 4cc2766d4da5f4e086040326b598a6174612f2b9 Mon Sep 17 00:00:00 2001 From: cinemaONE <35371193+cinema-ONE@users.noreply.github.com> Date: Sun, 30 Aug 2026 07:23:26 +0200 Subject: [PATCH 1/3] Update Decode() for ImageDecoder API 3.1.0 xbmc/xbmc#29068 added size_t pixelBufferSize to the C++ Decode() signature. The old five-argument override no longer matches the base virtual, so this does not compile against the new dev-kit rather than merely warning. Signature only; the argument is used in the next commit. Co-Authored-By: Claude Opus 5 --- src/MPOPicture.cpp | 1 + src/MPOPicture.h | 1 + 2 files changed, 2 insertions(+) diff --git a/src/MPOPicture.cpp b/src/MPOPicture.cpp index 31755a0..e8c5f1f 100644 --- a/src/MPOPicture.cpp +++ b/src/MPOPicture.cpp @@ -154,6 +154,7 @@ bool MPOPicture::LoadImageFromMemory(const std::string& mimetype, } bool MPOPicture::Decode(uint8_t* pixels, + size_t pixelBufferSize, unsigned int width, unsigned int height, unsigned int pitch, diff --git a/src/MPOPicture.h b/src/MPOPicture.h index b32cce3..b4a4116 100644 --- a/src/MPOPicture.h +++ b/src/MPOPicture.h @@ -28,6 +28,7 @@ class ATTR_DLL_LOCAL MPOPicture : public kodi::addon::CInstanceImageDecoder unsigned int& width, unsigned int& height) override; bool Decode(uint8_t* pixels, + size_t pixelBufferSize, unsigned int width, unsigned int height, unsigned int pitch, From adf56648a52ca20d0383087f977d11343e54270d Mon Sep 17 00:00:00 2001 From: cinemaONE <35371193+cinema-ONE@users.noreply.github.com> Date: Sun, 30 Aug 2026 07:24:09 +0200 Subject: [PATCH 2/3] Refuse a decode that would not fit the output buffer Use the pixelBufferSize argument added in the previous commit: work out what the copy loop actually reaches and return false rather than write past the end. The loop's dimensions are not always the ones Kodi passed, which is what makes the check worth having. Co-Authored-By: Claude Opus 5 --- src/MPOPicture.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/MPOPicture.cpp b/src/MPOPicture.cpp index e8c5f1f..22c2133 100644 --- a/src/MPOPicture.cpp +++ b/src/MPOPicture.cpp @@ -166,6 +166,22 @@ bool MPOPicture::Decode(uint8_t* pixels, mpo_start_decompress(&m_mpoinfo); JSAMPARRAY buffer; int row_stride = m_mpoinfo.cinfo.cinfo.output_width * m_mpoinfo.cinfo.cinfo.output_components; + + // Mirrors the destination offset in the loop below exactly - the integer + // division must be applied in the same order or an odd m_width + // underestimates the reach for images after the first. + const size_t tileOffset = image * m_width / 2 * 4; + if (m_height == 0 || row_stride <= 0 || + static_cast(m_height - 1) * pitch + tileOffset + + static_cast(row_stride / 3) * 4 > + pixelBufferSize) + { + kodi::Log(ADDON_LOG_ERROR, "%s: Output buffer too small for image %zu of %ux%u at pitch %u", + __func__, image, m_width, m_height, pitch); + mpo_finish_decompress(&m_mpoinfo); + return false; + } + size_t lines = 0; while (lines < m_height) { From 4e246b594347f9e649523756b74c8bbada329681 Mon Sep 17 00:00:00 2001 From: cinemaONE <35371193+cinema-ONE@users.noreply.github.com> Date: Sun, 30 Aug 2026 07:35:16 +0200 Subject: [PATCH 3/3] Refuse formats the add-on does not implement The copy loop writes B,G,R and fills the fourth byte only for A8R8G8B8, so that is the only format actually produced. Every other value fell through to that same loop and returned true with output that does not match the request. ADDON_IMG_FMT_A8 was the worst of them: one byte per pixel is asked for and three are written. The previous commit's bounds check now refuses that, but it should not be reached at all. Latent today only because CTexture::LoadIImage() asks for A8R8G8B8. Co-Authored-By: Claude Opus 5 --- src/MPOPicture.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/MPOPicture.cpp b/src/MPOPicture.cpp index 22c2133..21dd72b 100644 --- a/src/MPOPicture.cpp +++ b/src/MPOPicture.cpp @@ -160,6 +160,15 @@ bool MPOPicture::Decode(uint8_t* pixels, unsigned int pitch, ADDON_IMG_FMT format) { + // The copy loop below writes B,G,R and only fills the fourth byte for + // A8R8G8B8, so that is the only format implemented here. + if (format != ADDON_IMG_FMT_A8R8G8B8) + { + kodi::Log(ADDON_LOG_ERROR, "%s: Unsupported target format (%d)", __func__, + static_cast(format)); + return false; + } + size_t image = 0; while (image < m_images) {