From 6396f7c020917635dd3a5832ca4954ad8626c4a6 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Fri, 6 Dec 2024 11:48:26 -0800 Subject: [PATCH] fix: broken pnm files with invalid resolution (#4561) Fixes #4553 Caught during fuzzing with address sanitizer. The file appeared to have a resolution so big it would not be able to satisfy the memory allocation. Solution: add the check_open to take an early abort if resolutions are bigger than could possibly be valid. Also have Strutil::stoi hande 32 bit overflow without UB overflow that the sanitizer complains about (that was the other cascading error that this same test case encountered in the sanitizer after the bad allocation). Signed-off-by: Larry Gritz --- src/libOpenImageIO/imagebuf.cpp | 4 ++++ src/oiiotool/oiiotool.cpp | 4 +++- src/pnm.imageio/pnminput.cpp | 4 +++- testsuite/pnm/ref/out.txt | 11 +++++++++++ testsuite/pnm/run.py | 2 +- testsuite/pnm/src/bad-4552.pgm | 4 ++++ 6 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 testsuite/pnm/src/bad-4552.pgm diff --git a/src/libOpenImageIO/imagebuf.cpp b/src/libOpenImageIO/imagebuf.cpp index 47ff3eba53..dccbc131bf 100644 --- a/src/libOpenImageIO/imagebuf.cpp +++ b/src/libOpenImageIO/imagebuf.cpp @@ -687,6 +687,7 @@ ImageBufImpl::new_pixels(size_t size, const void* data) // consider this an uninitialized ImageBuf, issue an error, and hope // it's handled well downstream. m_pixels.reset(); + m_bufspan = make_span(nullptr, 0); OIIO::debugfmt("ImageBuf unable to allocate {} bytes ({})\n", size, e.what()); error("ImageBuf unable to allocate {} bytes ({})\n", size, e.what()); @@ -720,6 +721,8 @@ ImageBufImpl::free_pixels() m_allocated_size = 0; } m_pixels.reset(); + // print("IB Freed pixels of length {}\n", m_bufspan.size()); + m_bufspan = make_span(nullptr, 0); m_deepdata.free(); m_storage = ImageBuf::UNINITIALIZED; m_blackpixel.clear(); @@ -806,6 +809,7 @@ ImageBufImpl::clear() m_spec = ImageSpec(); m_nativespec = ImageSpec(); m_pixels.reset(); + m_bufspan = make_span(nullptr, 0); m_localpixels = nullptr; m_spec_valid = false; m_pixels_valid = false; diff --git a/src/oiiotool/oiiotool.cpp b/src/oiiotool/oiiotool.cpp index 0afbe76a46..89bb032776 100644 --- a/src/oiiotool/oiiotool.cpp +++ b/src/oiiotool/oiiotool.cpp @@ -5801,7 +5801,8 @@ action_printstats(Oiiotool& ot, cspan argv) auto options = ot.extract_options(command); bool allsubimages = options.get_int("allsubimages", ot.allsubimages); - ot.read(); + if (!ot.read()) + return; ImageRecRef top = ot.top(); print_info_options opt = ot.info_opts(); @@ -5818,6 +5819,7 @@ action_printstats(Oiiotool& ot, cspan argv) opt.roi.chend); } std::string errstring; + OIIO_ASSERT(top.get()); print_info(std::cout, ot, top.get(), opt, errstring); ot.printed_info = true; diff --git a/src/pnm.imageio/pnminput.cpp b/src/pnm.imageio/pnminput.cpp index 3c9e7d3a4a..396ac83ee8 100644 --- a/src/pnm.imageio/pnminput.cpp +++ b/src/pnm.imageio/pnminput.cpp @@ -223,8 +223,10 @@ PNMInput::read_file_scanline(void* data, int y) numbytes = m_spec.nchannels * 4 * m_spec.width; else numbytes = m_spec.scanline_bytes(); - if (size_t(numbytes) > m_remaining.size()) + if (size_t(numbytes) > m_remaining.size()) { + errorfmt("Premature end of file"); return false; + } buf.assign(m_remaining.begin(), m_remaining.begin() + numbytes); m_remaining.remove_prefix(numbytes); diff --git a/testsuite/pnm/ref/out.txt b/testsuite/pnm/ref/out.txt index 8c1a957016..c1d480004f 100644 --- a/testsuite/pnm/ref/out.txt +++ b/testsuite/pnm/ref/out.txt @@ -69,6 +69,17 @@ Reading ../oiio-images/pnm/test-3.pfm oiio:ColorSpace: "Rec709" pnm:bigendian: 1 pnm:binary: 1 +Reading src/bad-4552.pgm +oiiotool ERROR: -info : SHA-1: Premature end of file +Full command line was: +> oiiotool --info -v -a --hash --oiioattrib try_all_readers 0 --printstats src/bad-4552.pgm +src/bad-4552.pgm : 9 x 1, 1 channel, uint8 pnm + channel list: Y + oiio:ColorSpace: "Rec709" + pnm:binary: 1 +oiiotool ERROR: read : "src/bad-4552.pgm": Premature end of file +Full command line was: +> oiiotool --info -v -a --hash --oiioattrib try_all_readers 0 --printstats src/bad-4552.pgm oiiotool ERROR: read : "src/bad-4553.pgm": pnm image resolution may not exceed 65535x65535, but the file appears to be 2147483647x255. Possible corrupt input? Full command line was: > oiiotool --info -v -a --hash --oiioattrib try_all_readers 0 --printstats src/bad-4553.pgm diff --git a/testsuite/pnm/run.py b/testsuite/pnm/run.py index 76f7297a88..be6a094e82 100755 --- a/testsuite/pnm/run.py +++ b/testsuite/pnm/run.py @@ -20,6 +20,6 @@ safematch=True, hash=True) # Damaged files -files = [ "src/bad-4553.pgm" ] +files = [ "src/bad-4552.pgm", "src/bad-4553.pgm" ] for f in files: command += info_command (f, extraargs="--oiioattrib try_all_readers 0 --printstats", failureok=True) diff --git a/testsuite/pnm/src/bad-4552.pgm b/testsuite/pnm/src/bad-4552.pgm new file mode 100644 index 0000000000..e0522edd86 --- /dev/null +++ b/testsuite/pnm/src/bad-4552.pgm @@ -0,0 +1,4 @@ +P5 +9 1 +255 +þ \ No newline at end of file