From be8792e2bfdf1d1ddc674b6334bb0c264d1ab798 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Sat, 11 Jul 2026 11:03:56 -0700 Subject: [PATCH 1/3] fix(raw): reject decompression-bomb / corrupt headers before unpack Add a pre-unpack guard that rejects when the declared raw data size dwarfs the actual file size by more than 10000x (real raw sensor data never compresses anywhere near that ratio), plus a check_open() call enforcing OIIO's standard global resolution/imagesize limits and LibRaw's own per-dimension and per-image channel caps, matching the pattern already used in other format readers. Assisted-by: Claude Code / Claude Sonnet 5 Signed-off-by: Larry Gritz --- src/raw.imageio/rawinput.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/raw.imageio/rawinput.cpp b/src/raw.imageio/rawinput.cpp index ecde4427e6..7129488781 100644 --- a/src/raw.imageio/rawinput.cpp +++ b/src/raw.imageio/rawinput.cpp @@ -445,6 +445,27 @@ RawInput::open_raw(bool unpack, bool process, const std::string& name, = config.get_int_attribute("raw:max_raw_memory_mb", 2048); #endif + // Guard against decompression bombs / corrupt headers before calling + // unpack(). LibRaw's own caps (65535/dimension, max_raw_memory_mb) are + // far larger than this and miss a bogus-but-plausible resolution from + // a truncated/fuzzed file; unpack() would then spend a long time + // decoding garbage instead of erroring out. Real raw sensor data + // never compresses anywhere near this ratio, so genuine files pass. + { + int64_t raw_width = m_processor->imgdata.sizes.raw_width; + int64_t raw_height = m_processor->imgdata.sizes.raw_height; + int64_t raw_bps = m_processor->imgdata.rawdata.color.raw_bps; + int64_t declared_bytes = raw_width * raw_height * raw_bps / 8; + int64_t filesize = Filesystem::file_size(name); + const int64_t bomb_ratio = 10000; + if (filesize > 0 && declared_bytes > filesize * bomb_ratio) { + errorfmt("Raw header for \"{}\" claims a {} MB image from a {} " + "byte file; probably a corrupt or truncated file", + m_filename, declared_bytes >> 20, filesize); + return false; + } + } + OIIO_ASSERT(!m_unpacked); if (unpack) { if ((ret = m_processor->unpack()) != LIBRAW_SUCCESS) { @@ -481,6 +502,12 @@ RawInput::open_raw(bool unpack, bool process, const std::string& name, // Move the exif attribs we already read into the spec we care about m_spec.extra_attribs.swap(exifspec.extra_attribs); + // Enforce OIIO's global decode-bomb limits (limits:resolution, + // limits:imagesize_MB) and LibRaw's own 65535-per-dimension cap. + // idata.colors maxes out at 4 (some 4-color CFA patterns). + if (!check_open(m_spec, { 0, 1 << 16, 0, 1 << 16, 0, 1, 0, 4 })) + return false; + // Output 16 bit images m_processor->imgdata.params.output_bps = 16; From 0d73b916a85dc2ff8ef4ba1522cfdc4c2a7a0eab Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Thu, 16 Jul 2026 22:11:42 -0700 Subject: [PATCH 2/3] raw: Use check_compression_ratio Signed-off-by: Larry Gritz --- src/raw.imageio/rawinput.cpp | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/raw.imageio/rawinput.cpp b/src/raw.imageio/rawinput.cpp index 7129488781..b197bea30f 100644 --- a/src/raw.imageio/rawinput.cpp +++ b/src/raw.imageio/rawinput.cpp @@ -449,21 +449,16 @@ RawInput::open_raw(bool unpack, bool process, const std::string& name, // unpack(). LibRaw's own caps (65535/dimension, max_raw_memory_mb) are // far larger than this and miss a bogus-but-plausible resolution from // a truncated/fuzzed file; unpack() would then spend a long time - // decoding garbage instead of erroring out. Real raw sensor data - // never compresses anywhere near this ratio, so genuine files pass. + // decoding garbage instead of erroring out. { - int64_t raw_width = m_processor->imgdata.sizes.raw_width; - int64_t raw_height = m_processor->imgdata.sizes.raw_height; - int64_t raw_bps = m_processor->imgdata.rawdata.color.raw_bps; - int64_t declared_bytes = raw_width * raw_height * raw_bps / 8; - int64_t filesize = Filesystem::file_size(name); - const int64_t bomb_ratio = 10000; - if (filesize > 0 && declared_bytes > filesize * bomb_ratio) { - errorfmt("Raw header for \"{}\" claims a {} MB image from a {} " - "byte file; probably a corrupt or truncated file", - m_filename, declared_bytes >> 20, filesize); + int64_t raw_width = m_processor->imgdata.sizes.raw_width; + int64_t raw_height = m_processor->imgdata.sizes.raw_height; + int64_t raw_bps = m_processor->imgdata.rawdata.color.raw_bps; + imagesize_t declared_bytes = imagesize_t(raw_width) * raw_height + * raw_bps / 8; + imagesize_t filesize = Filesystem::file_size(name); + if (!check_compression_ratio(declared_bytes, filesize)) return false; - } } OIIO_ASSERT(!m_unpacked); From 1e8981fe7c29fa37f532e0289a9b74659ff6177b Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Tue, 21 Jul 2026 22:45:24 -0700 Subject: [PATCH 3/3] Compensate for libraw overflow Signed-off-by: Larry Gritz --- src/raw.imageio/rawinput.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/raw.imageio/rawinput.cpp b/src/raw.imageio/rawinput.cpp index b197bea30f..38030c592f 100644 --- a/src/raw.imageio/rawinput.cpp +++ b/src/raw.imageio/rawinput.cpp @@ -441,8 +441,10 @@ RawInput::open_raw(bool unpack, bool process, const std::string& name, #if LIBRAW_VERSION >= LIBRAW_MAKE_VERSION(0, 21, 0) // Cap LibRaw's internal allocations. This must be set *before* unpack(). // Default max is 2048 MB. - m_processor->imgdata.rawparams.max_raw_memory_mb - = config.get_int_attribute("raw:max_raw_memory_mb", 2048); + int maxmem = config.get_int_attribute("raw:max_raw_memory_mb", 2048); + // In some versions of libraw, there is a known overflow if it's over + // 16384, so cap it. + m_processor->imgdata.rawparams.max_raw_memory_mb = std::min(maxmem, 16383); #endif // Guard against decompression bombs / corrupt headers before calling