diff --git a/src/lib/OpenEXRCore/chunk.c b/src/lib/OpenEXRCore/chunk.c index d92a8af4f3..b341523501 100644 --- a/src/lib/OpenEXRCore/chunk.c +++ b/src/lib/OpenEXRCore/chunk.c @@ -51,17 +51,20 @@ static exr_result_t extract_chunk_table ( const struct _internal_exr_context* ctxt, const struct _internal_exr_part* part, - uint64_t** chunktable) + uint64_t** chunktable, + uint64_t* chunkminoffset) { - uint64_t* ctable = NULL; + uint64_t* ctable = NULL; + uint64_t chunkoff = part->chunk_table_offset; + uint64_t chunkbytes = sizeof (uint64_t) * (uint64_t) part->chunk_count; + + *chunkminoffset = chunkoff + chunkbytes; ctable = (uint64_t*) atomic_load ( EXR_CONST_CAST (atomic_uintptr_t*, &(part->chunk_table))); if (ctable == NULL) { - uint64_t chunkoff = part->chunk_table_offset; - uint64_t chunkbytes = sizeof (uint64_t) * (uint64_t) part->chunk_count; - int64_t nread = 0; + int64_t nread = 0; uintptr_t eptr = 0, nptr = 0; exr_result_t rv; @@ -198,7 +201,7 @@ exr_read_scanline_chunk_info ( int32_t data[3]; int64_t ddata[3]; int64_t fsize; - uint64_t dataoff; + uint64_t chunkmin, dataoff; exr_attr_box2i_t dw; uint64_t* ctable; EXR_PROMOTE_READ_CONST_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index); @@ -263,10 +266,23 @@ exr_read_scanline_chunk_info ( cinfo->level_y = 0; /* need to read from the file to get the packed chunk size */ - rv = extract_chunk_table (pctxt, part, &ctable); + rv = extract_chunk_table (pctxt, part, &ctable, &chunkmin); if (rv != EXR_ERR_SUCCESS) return rv; + fsize = pctxt->file_size; + dataoff = ctable[cidx]; + if (dataoff < chunkmin || (fsize > 0 && dataoff > (uint64_t) fsize)) + { + return pctxt->print_error ( + pctxt, + EXR_ERR_BAD_CHUNK_LEADER, + "Corrupt chunk offset table: scanline %d, chunk index %d recorded at file offset %" PRIu64, + y, + cidx, + dataoff); + } + /* multi part files have the part for validation */ rdcnt = (pctxt->is_multipart) ? 2 : 1; /* deep has 64-bit data, so be variable about what we read */ @@ -312,7 +328,6 @@ exr_read_scanline_chunk_info ( miny); } - fsize = pctxt->file_size; if (part->storage_mode == EXR_STORAGE_DEEP_SCANLINE) { rv = pctxt->do_read ( @@ -584,8 +599,8 @@ exr_read_tile_chunk_info ( int32_t data[6]; int32_t* tdata; int32_t cidx, ntoread; - uint64_t dataoff; - int64_t fsize, tend, dend; + uint64_t chunkmin, dataoff; + int64_t nread, fsize, tend, dend; const exr_attr_chlist_t* chanlist; const exr_attr_tiledesc_t* tiledesc; int tilew, tileh; @@ -655,14 +670,15 @@ exr_read_tile_chunk_info ( cinfo->level_y = (uint8_t) levely; chanlist = part->channels->chlist; - texels = (uint64_t) tilew * (uint64_t) tileh; + texels = (uint64_t) tilew * (uint64_t) tileh; for (int c = 0; c < chanlist->num_channels; ++c) { const exr_attr_chlist_entry_t* curc = (chanlist->entries + c); - unpacksize += texels * (uint64_t) ((curc->pixel_type == EXR_PIXEL_HALF) ? 2 : 4); + unpacksize += + texels * (uint64_t) ((curc->pixel_type == EXR_PIXEL_HALF) ? 2 : 4); } - rv = extract_chunk_table (pctxt, part, &ctable); + rv = extract_chunk_table (pctxt, part, &ctable, &chunkmin); if (rv != EXR_ERR_SUCCESS) return rv; if (part->storage_mode == EXR_STORAGE_DEEP_TILED) @@ -677,13 +693,29 @@ exr_read_tile_chunk_info ( else ntoread = 5; + fsize = pctxt->file_size; + dataoff = ctable[cidx]; - rv = pctxt->do_read ( + if (dataoff < chunkmin || (fsize > 0 && dataoff > (uint64_t) fsize)) + { + return pctxt->print_error ( + pctxt, + EXR_ERR_BAD_CHUNK_LEADER, + "Corrupt chunk offset table: tile (%d, %d), level (%d, %d), chunk index %d recorded at file offset %" PRIu64, + tilex, + tiley, + levelx, + levely, + cidx, + dataoff); + } + + rv = pctxt->do_read ( pctxt, data, (uint64_t) (ntoread) * sizeof (int32_t), &dataoff, - &fsize, + &nread, EXR_MUST_READ_ALL); if (rv != EXR_ERR_SUCCESS) { @@ -698,7 +730,7 @@ exr_read_tile_chunk_info ( levely, (uint64_t) (ntoread) * sizeof (int32_t), ctable[cidx], - fsize); + (uint64_t) nread); } priv_to_native32 (data, ntoread); @@ -778,7 +810,6 @@ exr_read_tile_chunk_info ( levely); } - fsize = pctxt->file_size; if (part->storage_mode == EXR_STORAGE_DEEP_TILED) { int64_t ddata[3]; diff --git a/src/lib/OpenEXRCore/coding.c b/src/lib/OpenEXRCore/coding.c index fc7beaa92a..ef71d2aa92 100644 --- a/src/lib/OpenEXRCore/coding.c +++ b/src/lib/OpenEXRCore/coding.c @@ -254,6 +254,15 @@ internal_decode_alloc_buffer ( size_t newsz) { void* curbuf = *buf; + + /* We might have a zero size here due to y sampling on a scanline + * image where there is an attempt to read that portion of the + * image. Just shortcut here and handle at a higher level where + * there is more context + */ + if (newsz == 0) + return EXR_ERR_SUCCESS; + if (!curbuf || *cursz < newsz) { internal_decode_free_buffer (decode, bufid, buf, cursz); diff --git a/src/lib/OpenEXRCore/internal_huf.c b/src/lib/OpenEXRCore/internal_huf.c index d26003d1df..af9b6e6a4e 100644 --- a/src/lib/OpenEXRCore/internal_huf.c +++ b/src/lib/OpenEXRCore/internal_huf.c @@ -796,7 +796,7 @@ hufDecode ( const uint8_t* in, // i : compressed input buffer uint64_t ni, // i : input size (in bits) uint32_t rlc, // i : run-length code - uint64_t no, // i : expected output size (in bytes) + uint64_t no, // i : expected output size (count of uint16 items) uint16_t* out) { uint64_t c = 0; @@ -894,7 +894,7 @@ hufDecode ( return EXR_ERR_CORRUPT_CHUNK; } - if ((((uintptr_t) out) - ((uintptr_t) outb)) != no) + if (out != oe) return EXR_ERR_OUT_OF_MEMORY; return EXR_ERR_SUCCESS; } diff --git a/src/lib/OpenEXRUtil/ImfCheckFile.cpp b/src/lib/OpenEXRUtil/ImfCheckFile.cpp index 19ff1eb536..ad7ed2f760 100644 --- a/src/lib/OpenEXRUtil/ImfCheckFile.cpp +++ b/src/lib/OpenEXRUtil/ImfCheckFile.cpp @@ -27,6 +27,7 @@ #include #include +#include OPENEXR_IMF_INTERNAL_NAMESPACE_SOURCE_ENTER @@ -1349,10 +1350,10 @@ bool readCoreTiledPart(exr_context_t f, int part, bool reduceMemory, bool reduce { exr_coding_channel_info_t & outc = decoder.channels[c]; // fake addr for default rouines - outc.decode_to_ptr = (uint8_t*)0x1000; + outc.decode_to_ptr = (uint8_t*)0x1000 + bytes; outc.user_pixel_stride = outc.user_bytes_per_element; outc.user_line_stride = outc.user_pixel_stride * curtw; - bytes += curtw * (uint64_t)outc.user_bytes_per_element * (uint64_t)curth; + bytes += (uint64_t)curtw * (uint64_t)outc.user_bytes_per_element * (uint64_t)curth; } doread = true; @@ -1390,8 +1391,8 @@ bool readCoreTiledPart(exr_context_t f, int part, bool reduceMemory, bool reduce exr_coding_channel_info_t & outc = decoder.channels[c]; outc.decode_to_ptr = dptr; outc.user_pixel_stride = outc.user_bytes_per_element; - outc.user_line_stride = outc.user_pixel_stride * curth; - dptr += curtw * (uint64_t)outc.user_bytes_per_element * (uint64_t)curth; + outc.user_line_stride = outc.user_pixel_stride * curtw; + dptr += (uint64_t)curtw * (uint64_t)outc.user_bytes_per_element * (uint64_t)curth; } rv = exr_decoding_run (f, part, &decoder); @@ -1453,6 +1454,24 @@ bool checkCoreFile(exr_context_t f, bool reduceMemory, bool reduceTime) //////////////////////////////////////// +static void +core_error_handler_cb (exr_const_context_t f, int code, const char* msg) +{ + if (getenv ("EXR_CHECK_ENABLE_PRINTS") != NULL) + { + const char* fn; + if (EXR_ERR_SUCCESS != exr_get_file_name (f, &fn)) fn = ""; + fprintf ( + stderr, + "ERROR '%s' (%s): %s\n", + fn, + exr_get_error_code_as_string (code), + msg); + } +} + +//////////////////////////////////////// + bool runCoreChecks (const char *filename, bool reduceMemory, bool reduceTime) { @@ -1461,6 +1480,8 @@ runCoreChecks (const char *filename, bool reduceMemory, bool reduceTime) exr_context_t f; exr_context_initializer_t cinit = EXR_DEFAULT_CONTEXT_INITIALIZER; + cinit.error_handler_fn = &core_error_handler_cb; + rv = exr_start_read (&f, filename, &cinit); if (rv != EXR_ERR_SUCCESS) return true; @@ -1480,7 +1501,7 @@ struct memdata size_t bytes; }; -int64_t +static int64_t memstream_read ( exr_const_context_t f, void* userdata, @@ -1504,7 +1525,7 @@ memstream_read ( return rdsz; } -int64_t memstream_size ( +static int64_t memstream_size ( exr_const_context_t ctxt, void* userdata) { if (userdata) @@ -1530,6 +1551,7 @@ runCoreChecks (const char *data, size_t numBytes, bool reduceMemory, bool reduce cinit.user_data = &md; cinit.read_fn = &memstream_read; cinit.size_fn = &memstream_size; + cinit.error_handler_fn = &core_error_handler_cb; rv = exr_start_read (&f, "", &cinit); if (rv != EXR_ERR_SUCCESS)