From 7629dad94ac310a285099f488618cbebc5eb9a85 Mon Sep 17 00:00:00 2001 From: Kimball Thurston Date: Mon, 1 Aug 2022 08:36:58 +1200 Subject: [PATCH] Address OSS Fuzz Issues (#1268) * Add utility function header Add location to extract internal helper functions to reduce code duplication Signed-off-by: Kimball Thurston * Fix issues with odd y_sampling numbers Use new compute_sampled_lines when the y_sampling is odd (sampling % 2 == 1), where the number of lines per chunk will oscillate. Signed-off-by: Kimball Thurston * Fix integer overflow computing deep chunk index Signed-off-by: Kimball Thurston * Fix decompression scratch buffer size Use a minimum size to handle that b44 is always 4x4 blocks Signed-off-by: Kimball Thurston * Use appropriate buffer size when decompressing use the scratch buffer size instead of decompressed buffer size, allow some extra padding in scratch buffer when appropriate Signed-off-by: Kimball Thurston --- src/lib/OpenEXRCore/chunk.c | 74 +++++++++++++---------------- src/lib/OpenEXRCore/coding.c | 25 ++-------- src/lib/OpenEXRCore/internal_b44.c | 39 ++++++++++++--- src/lib/OpenEXRCore/internal_util.h | 40 ++++++++++++++++ src/lib/OpenEXRCore/internal_zip.c | 8 +++- 5 files changed, 116 insertions(+), 70 deletions(-) create mode 100644 src/lib/OpenEXRCore/internal_util.h diff --git a/src/lib/OpenEXRCore/chunk.c b/src/lib/OpenEXRCore/chunk.c index 0422acd23c..c034fecb3d 100644 --- a/src/lib/OpenEXRCore/chunk.c +++ b/src/lib/OpenEXRCore/chunk.c @@ -7,6 +7,7 @@ #include "internal_coding.h" #include "internal_structs.h" +#include "internal_util.h" #include "internal_xdr.h" #include @@ -408,8 +409,21 @@ read_and_validate_chunk_leader ( if (part->storage_mode == EXR_STORAGE_SCANLINE || part->storage_mode == EXR_STORAGE_DEEP_SCANLINE) { - *indexio = (leader.scanline_y - part->data_window.min.y) / - part->lines_per_chunk; + int64_t chunk = (int64_t) leader.scanline_y; + chunk -= (int64_t) part->data_window.min.y; + chunk /= part->lines_per_chunk; + if (chunk < 0 || chunk > INT32_MAX) + return ctxt->print_error ( + ctxt, + EXR_ERR_BAD_CHUNK_LEADER, + "Invalid chunk index: %" PRId64 + " reading scanline %d (datawindow min %d) with lines per chunk %d", + chunk, + leader.scanline_y, + part->data_window.min.y, + part->lines_per_chunk); + + *indexio = (int) chunk; } else { @@ -527,10 +541,10 @@ extract_chunk_table ( EXR_CONST_CAST (atomic_uintptr_t*, &(part->chunk_table))); if (ctable == NULL) { - int64_t nread = 0; - uintptr_t eptr = 0, nptr = 0; - int complete = 1; - uint64_t maxoff = ((uint64_t) -1); + int64_t nread = 0; + uintptr_t eptr = 0, nptr = 0; + int complete = 1; + uint64_t maxoff = ((uint64_t) -1); exr_result_t rv; if (part->chunk_count <= 0) @@ -590,10 +604,7 @@ extract_chunk_table ( } } } - else - { - priv_to_native64 (ctable, part->chunk_count); - } + else { priv_to_native64 (ctable, part->chunk_count); } nptr = (uintptr_t) ctable; // see if we win or not @@ -670,21 +681,11 @@ compute_chunk_unpack_size ( { const exr_attr_chlist_entry_t* curc = (chanlist->entries + c); uint64_t chansz = ((curc->pixel_type == EXR_PIXEL_HALF) ? 2 : 4); - chansz *= ((uint64_t) width); + + chansz *= (uint64_t) width; if (curc->x_sampling > 1) chansz /= ((uint64_t) curc->x_sampling); - chansz *= ((uint64_t) height); - if (curc->y_sampling > 1) - { - if (height > 1) - { - if (curc->y_sampling > height) - chansz /= ((uint64_t) height); - else - chansz /= ((uint64_t) curc->y_sampling); - } - else if ((y % ((int) curc->y_sampling)) != 0) - chansz = 0; - } + chansz *= + (uint64_t) compute_sampled_lines (height, curc->y_sampling, y); unpacksize += chansz; } } @@ -761,10 +762,7 @@ exr_read_scanline_chunk_info ( cinfo->start_y = dw.min.y; cinfo->height -= (dw.min.y - miny); } - else if ((miny + lpc) > dw.max.y) - { - cinfo->height = (dw.max.y - miny + 1); - } + else if ((miny + lpc) > dw.max.y) { cinfo->height = (dw.max.y - miny + 1); } cinfo->level_x = 0; cinfo->level_y = 0; @@ -932,10 +930,11 @@ exr_read_scanline_chunk_info ( pctxt, EXR_ERR_BAD_CHUNK_LEADER, "Preparing to read scanline %d (chunk %d), found corrupt leader: packed size %" PRIu64 - ", file size %" PRId64, + ", file offset %" PRIu64 ", size %" PRId64, y, cidx, - (uint64_t) data[rdcnt], + cinfo->packed_size, + cinfo->data_offset, fsize); } } @@ -1637,10 +1636,7 @@ write_scan_chunk ( /* just in case we look at it again? */ priv_to_native64 (ctable, part->chunk_count); } - else - { - pctxt->last_output_chunk = cidx; - } + else { pctxt->last_output_chunk = cidx; } } return rv; @@ -1722,10 +1718,7 @@ exr_write_scanline_chunk_info ( cinfo->start_y = dw.min.y; cinfo->height -= (dw.min.y - miny); } - else if ((miny + lpc) > dw.max.y) - { - cinfo->height = (dw.max.y - miny + 1); - } + else if ((miny + lpc) > dw.max.y) { cinfo->height = (dw.max.y - miny + 1); } cinfo->level_x = 0; cinfo->level_y = 0; @@ -2060,10 +2053,7 @@ write_tile_chunk ( /* just in case we look at it again? */ priv_to_native64 (ctable, part->chunk_count); } - else - { - pctxt->last_output_chunk = cidx; - } + else { pctxt->last_output_chunk = cidx; } } return rv; diff --git a/src/lib/OpenEXRCore/coding.c b/src/lib/OpenEXRCore/coding.c index 96a97edf12..104e566994 100644 --- a/src/lib/OpenEXRCore/coding.c +++ b/src/lib/OpenEXRCore/coding.c @@ -4,6 +4,7 @@ */ #include "internal_coding.h" +#include "internal_util.h" #include @@ -40,16 +41,8 @@ internal_coding_fill_channel_info ( decc->channel_name = curc->name.str; - if (curc->y_sampling > 1) - { - if (cinfo->height == 1) - decc->height = ((cinfo->start_y % curc->y_sampling) == 0) ? 1 - : 0; - else - decc->height = cinfo->height / curc->y_sampling; - } - else - decc->height = cinfo->height; + decc->height = compute_sampled_lines ( + cinfo->height, curc->y_sampling, cinfo->start_y); if (curc->x_sampling > 1) decc->width = cinfo->width / curc->x_sampling; @@ -106,16 +99,8 @@ internal_coding_update_channel_info ( ccic->channel_name = curc->name.str; - if (curc->y_sampling > 1) - { - if (cinfo->height == 1) - ccic->height = ((cinfo->start_y % curc->y_sampling) == 0) ? 1 - : 0; - else - ccic->height = cinfo->height / curc->y_sampling; - } - else - ccic->height = cinfo->height; + ccic->height = compute_sampled_lines ( + cinfo->height, curc->y_sampling, cinfo->start_y); if (curc->x_sampling > 1) ccic->width = cinfo->width / curc->x_sampling; diff --git a/src/lib/OpenEXRCore/internal_b44.c b/src/lib/OpenEXRCore/internal_b44.c index 16400a00a0..ce4b43af9c 100644 --- a/src/lib/OpenEXRCore/internal_b44.c +++ b/src/lib/OpenEXRCore/internal_b44.c @@ -345,10 +345,7 @@ compress_b44_impl (exr_encode_pipeline_t* encode, int flat_field) } tmp += ((uint64_t) (y / curc->y_samples)) * bpl; } - else - { - tmp += ((uint64_t) y) * bpl; - } + else { tmp += ((uint64_t) y) * bpl; } memcpy (tmp, packed, bpl); if (curc->data_type == EXR_PIXEL_HALF) priv_to_native16 (tmp, nx); @@ -607,6 +604,36 @@ uncompress_b44_impl ( return EXR_ERR_SUCCESS; } +/**************************************/ + +static uint64_t +compute_scratch_buffer_size ( + exr_decode_pipeline_t* decode, uint64_t uncompressed_size) +{ + const exr_coding_channel_info_t* curc; + int nx, ny; + uint64_t ret = uncompressed_size; + uint64_t comp = 0; + + for (int c = 0; c < decode->channel_count; ++c) + { + curc = decode->channels + c; + + nx = curc->width; + ny = curc->height; + + if (nx % 4) nx += 4 - nx % 4; + if (ny % 4) ny += 4 - ny % 4; + + comp += (uint64_t) (ny) * (uint64_t) (nx) * + (uint64_t) (curc->bytes_per_element); + } + if (comp > ret) ret = comp; + return ret; +} + +/**************************************/ + exr_result_t internal_exr_undo_b44 ( exr_decode_pipeline_t* decode, @@ -621,7 +648,7 @@ internal_exr_undo_b44 ( EXR_TRANSCODE_BUFFER_SCRATCH1, &(decode->scratch_buffer_1), &(decode->scratch_alloc_size_1), - uncompressed_size); + compute_scratch_buffer_size (decode, uncompressed_size)); if (rv != EXR_ERR_SUCCESS) return rv; return uncompress_b44_impl ( @@ -646,7 +673,7 @@ internal_exr_undo_b44a ( EXR_TRANSCODE_BUFFER_SCRATCH1, &(decode->scratch_buffer_1), &(decode->scratch_alloc_size_1), - uncompressed_size); + compute_scratch_buffer_size (decode, uncompressed_size)); if (rv != EXR_ERR_SUCCESS) return rv; return uncompress_b44_impl ( diff --git a/src/lib/OpenEXRCore/internal_util.h b/src/lib/OpenEXRCore/internal_util.h new file mode 100644 index 0000000000..0e76b52f01 --- /dev/null +++ b/src/lib/OpenEXRCore/internal_util.h @@ -0,0 +1,40 @@ +/* +** SPDX-License-Identifier: BSD-3-Clause +** Copyright Contributors to the OpenEXR Project. +*/ + +#include + +static inline int +compute_sampled_lines (int height, int y_sampling, int start_y) +{ + int nlines; + + if (y_sampling <= 1) return height; + + if (height == 1) + nlines = (start_y % y_sampling) == 0 ? 1 : 0; + else + { + int start, end; + + /* computed the number of times y % ysampling == 0, by + * computing interval based on first and last time that occurs + * on the given range + */ + start = start_y % y_sampling; + if (start != 0) + start = start_y + (y_sampling - start); + else + start = start_y; + end = start_y + height - 1; + end -= (end % y_sampling); + + if (start > end) + nlines = 0; + else + nlines = (end - start) / y_sampling + 1; + } + + return nlines; +} diff --git a/src/lib/OpenEXRCore/internal_zip.c b/src/lib/OpenEXRCore/internal_zip.c index 7a1b4dc05b..21e61579b8 100644 --- a/src/lib/OpenEXRCore/internal_zip.c +++ b/src/lib/OpenEXRCore/internal_zip.c @@ -158,7 +158,7 @@ undo_zip_impl ( void* scratch_data, uint64_t scratch_size) { - uLong outSize = (uLong) uncompressed_size; + uLong outSize = (uLong) scratch_size; int rstat; if (scratch_size < uncompressed_size) return EXR_ERR_INVALID_ARGUMENT; @@ -200,12 +200,16 @@ internal_exr_undo_zip ( uint64_t uncompressed_size) { exr_result_t rv; + uint64_t scratchbufsz = uncompressed_size; + if ( comp_buf_size > scratchbufsz ) + scratchbufsz = comp_buf_size; + rv = internal_decode_alloc_buffer ( decode, EXR_TRANSCODE_BUFFER_SCRATCH1, &(decode->scratch_buffer_1), &(decode->scratch_alloc_size_1), - uncompressed_size); + scratchbufsz); if (rv != EXR_ERR_SUCCESS) return rv; return undo_zip_impl ( compressed_data,