From ca95eb8fd65e18c0d6e5630a616c89db5754cb7f Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Thu, 2 Jul 2026 23:33:01 -0400 Subject: [PATCH] fix(targa): reject implausible image dimensions before allocating An 18-byte TGA file consisting of just a header (no pixel data) can claim an arbitrary width/height/bpp -- e.g. a 1472x12000 24bpp image -- and readimg() would allocate the full ~53MB buffer and only discover the truncation once the first read hits EOF. Worse, the resulting partially-formed ImageSpec could still propagate to a downstream writer before the read failure was acted on. Cross-check the header's width/height/bpp against how many bytes of pixel data the file could plausibly contain before committing to the allocation, mirroring the existing pattern in iff.imageio and jpeg2000.imageio. The check compares against the *on-disk* pixel size (palette/16-bit storage is smaller per pixel than the decoded, unpacked ImageSpec) and allows up to 128x expansion for RLE-compressed images (a 1-byte run-length count can encode up to 128 output pixels). Updated testsuite/targa/ref/out.txt: several existing corrupt-file regression cases (crash1.tga, crash6.tga, crash3952.tga) are now caught earlier with clearer messages, and two real oiio-images TGA files (UCM8.TGA, UTC16.TGA) that use palette/16-bit storage smaller than their decoded size continue to read correctly. Assisted-by: Claude Code / Sonnet 5 Signed-off-by: Larry Gritz --- src/targa.imageio/targainput.cpp | 28 ++++++++++++++++++++++++++++ testsuite/targa/ref/out.txt | 7 ++----- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/src/targa.imageio/targainput.cpp b/src/targa.imageio/targainput.cpp index b168508374..d770ab9593 100644 --- a/src/targa.imageio/targainput.cpp +++ b/src/targa.imageio/targainput.cpp @@ -254,6 +254,34 @@ TGAInput::open(const std::string& name, ImageSpec& newspec) if (m_tga.type >= TYPE_PALETTED_RLE) m_spec.attribute("compression", "rle"); + // The width/height/bpp fields are not cross-checked against how much + // pixel data the file actually contains, so a corrupt header can claim + // an arbitrarily large image (e.g. thousands of pixels square) backed + // by only a few bytes of real data. Reject implausible claims before + // committing to a potentially huge allocation in readimg(). Compare + // against the *on-disk* pixel size (palette/16-bit storage is smaller + // per pixel than the decoded, unpacked ImageSpec) -- and RLE can expand + // at most 128x (a 1-byte run-length count can encode up to 128 output + // pixels from very little input) -- so give a generous bound based on + // how much data remains in the file. + { + int bytespp = (m_tga.bpp == 15) ? 2 : (m_tga.bpp / 8); + uint64_t raw_pixel_bytes = uint64_t(m_tga.width) + * uint64_t(m_tga.height) * uint64_t(bytespp); + uint64_t filesize(ioproxy()->size()); + uint64_t pos(iotell()); + uint64_t avail = filesize > pos ? filesize - pos : 0; + uint64_t max_plausible_bytes = (m_tga.type >= TYPE_PALETTED_RLE) + ? avail * 128 + : avail; + if (raw_pixel_bytes > max_plausible_bytes) { + errorfmt( + "TGA header claims image size {} bytes, implausible for {} bytes of remaining file data", + raw_pixel_bytes, avail); + return false; + } + } + /*std::cerr << "[tga] " << m_tga.width << "x" << m_tga.height << "@" << (int)m_tga.bpp << " (" << m_spec.nchannels << ") type " << (int)m_tga.type << "\n";*/ diff --git a/testsuite/targa/ref/out.txt b/testsuite/targa/ref/out.txt index c303fe1b1a..d7b8d03467 100644 --- a/testsuite/targa/ref/out.txt +++ b/testsuite/targa/ref/out.txt @@ -187,8 +187,7 @@ Reading ../oiio-images/targa/round_grill.tga Comparing "../oiio-images/targa/round_grill.tga" and "round_grill.tga" PASS Converting src/crash1.tga to crash1.exr -iconvert ERROR copying "src/crash1.tga" to "crash1.exr" : - Read error: hit end of file in targa reader +iconvert ERROR: TGA header claims image size 1506428560 bytes, implausible for 1886 bytes of remaining file data oiiotool ERROR: read : "src/crash2.tga": Palette image with no palette Full command line was: > oiiotool --oiioattrib try_all_readers 0 src/crash2.tga -o crash2.exr @@ -204,9 +203,7 @@ Full command line was: oiiotool ERROR: read : "src/crash1708.tga": Too big a color palette (382) for 8 bpp, assume corruption Full command line was: > oiiotool --oiioattrib try_all_readers 0 src/crash1708.tga -o crash1708.exr -oiiotool ERROR: read : "src/crash3952.tga": Uncompressed image size 15231.5 MB exceeds the 1024 MB limit. -Image claimed to be 60927x65535, 4-channel uint8. Possible corrupt input? -If this is a valid file, raise the OIIO attribute "limits:imagesize_MB". +oiiotool ERROR: read : "src/crash3952.tga": TGA header claims image size 15971403780 bytes, implausible for 3 bytes of remaining file data Full command line was: > oiiotool --oiioattrib limits:imagesize_MB 1024 --oiioattrib try_all_readers 0 src/crash3952.tga -o crash3952.exr oiiotool ERROR: read : "src/crash-20250423.tga": Corrupt palette index