From bc8fa3a477e46d1fd3661666e4de79f455d41e19 Mon Sep 17 00:00:00 2001 From: jhendersonHDF Date: Tue, 11 Apr 2023 14:47:51 -0500 Subject: Add buffer overrun checks to H5O__layout_decode and H5O__sdspace_decode (#2679) Co-authored-by: Larry Knox --- release_docs/RELEASE.txt | 8 ++ src/H5Olayout.c | 256 ++++++++++++++++++++++++++++++++++++++++------- src/H5Osdspace.c | 62 ++++++++---- src/H5private.h | 5 + 4 files changed, 276 insertions(+), 55 deletions(-) diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt index f00ce32..b460141 100644 --- a/release_docs/RELEASE.txt +++ b/release_docs/RELEASE.txt @@ -151,6 +151,14 @@ Bug Fixes since HDF5-1.13.3 release =================================== Library ------- + - Fixed potential buffer overrun issues in some object header decode routines + + Several checks were added to H5O__layout_decode and H5O__sdspace_decode to + ensure that memory buffers don't get overrun when decoding buffers read from + a (possibly corrupted) HDF5 file. + + (JTH - 2023/04/05) + - Fixed a heap buffer overflow that occurs when reading from a dataset with a compact layout within a malformed HDF5 file diff --git a/src/H5Olayout.c b/src/H5Olayout.c index 595c73e..a58fc0c 100644 --- a/src/H5Olayout.c +++ b/src/H5Olayout.c @@ -91,11 +91,11 @@ static void * H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNUSED mesg_flags, unsigned H5_ATTR_UNUSED *ioflags, size_t p_size, const uint8_t *p) { + const uint8_t *p_end = p + p_size - 1; /* End of the p buffer */ H5O_layout_t *mesg = NULL; uint8_t *heap_block = NULL; unsigned u; - const uint8_t *p_end = p + p_size - 1; /* End of the p buffer */ - void *ret_value = NULL; /* Return value */ + void *ret_value = NULL; /* Return value */ FUNC_ENTER_PACKAGE @@ -105,39 +105,55 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU /* decode */ if (NULL == (mesg = H5FL_CALLOC(H5O_layout_t))) - HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "memory allocation failed") mesg->storage.type = H5D_LAYOUT_ERROR; + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") mesg->version = *p++; + if (mesg->version < H5O_LAYOUT_VERSION_1 || mesg->version > H5O_LAYOUT_VERSION_4) - HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "bad version number for layout message") + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "bad version number for layout message") if (mesg->version < H5O_LAYOUT_VERSION_3) { unsigned ndims; /* Num dimensions in chunk */ /* Dimensionality */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") ndims = *p++; + if (!ndims || ndims > H5O_LAYOUT_NDIMS) - HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "dimensionality is out of range") + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "dimensionality is out of range") /* Layout class */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") mesg->type = (H5D_layout_t)*p++; - HDassert(H5D_CONTIGUOUS == mesg->type || H5D_CHUNKED == mesg->type || H5D_COMPACT == mesg->type); + + if (H5D_CONTIGUOUS != mesg->type && H5D_CHUNKED != mesg->type && H5D_COMPACT != mesg->type) + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "bad layout type for layout message") /* Set the storage type */ mesg->storage.type = mesg->type; /* Reserved bytes */ + if (H5_IS_BUFFER_OVERFLOW(p, 5, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") p += 5; /* Address */ if (mesg->type == H5D_CONTIGUOUS) { + if (H5_IS_BUFFER_OVERFLOW(p, H5F_SIZEOF_ADDR(f), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") H5F_addr_decode(f, &p, &(mesg->storage.u.contig.addr)); /* Set the layout operations */ mesg->ops = H5D_LOPS_CONTIG; } /* end if */ else if (mesg->type == H5D_CHUNKED) { + if (H5_IS_BUFFER_OVERFLOW(p, H5F_SIZEOF_ADDR(f), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") H5F_addr_decode(f, &p, &(mesg->storage.u.chunk.idx_addr)); /* Set the layout operations */ @@ -164,29 +180,46 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU * size in the dataset code, where we've got the dataspace * information available also. - QAK 5/26/04 */ - p += ndims * 4; /* Skip over dimension sizes (32-bit quantities) */ - } /* end if */ + if (H5_IS_BUFFER_OVERFLOW(p, (ndims * sizeof(uint32_t)), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") + p += ndims * sizeof(uint32_t); /* Skip over dimension sizes */ + } /* end if */ else { if (ndims < 2) - HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "bad dimensions for chunked storage") + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "bad dimensions for chunked storage") mesg->u.chunk.ndims = ndims; - for (u = 0; u < ndims; u++) + + if (H5_IS_BUFFER_OVERFLOW(p, (ndims * sizeof(uint32_t)), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") + + for (u = 0; u < ndims; u++) { UINT32DECODE(p, mesg->u.chunk.dim[u]); + /* Just in case that something goes very wrong, such as file corruption. */ + if (mesg->u.chunk.dim[u] == 0) + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, + "bad chunk dimension value when parsing layout message - chunk dimension " + "must be positive: mesg->u.chunk.dim[%u] = %u", + u, mesg->u.chunk.dim[u]) + } + /* Compute chunk size */ for (u = 1, mesg->u.chunk.size = mesg->u.chunk.dim[0]; u < ndims; u++) mesg->u.chunk.size *= mesg->u.chunk.dim[u]; } /* end if */ if (mesg->type == H5D_COMPACT) { + if (H5_IS_BUFFER_OVERFLOW(p, sizeof(uint32_t), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") UINT32DECODE(p, mesg->storage.u.compact.size); + if (mesg->storage.u.compact.size > 0) { /* Ensure that size doesn't exceed buffer size, due to possible data corruption */ - if (p + mesg->storage.u.compact.size - 1 > p_end) - HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "storage size exceeds buffer size") + if (H5_IS_BUFFER_OVERFLOW(p, mesg->storage.u.compact.size, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") if (NULL == (mesg->storage.u.compact.buf = H5MM_malloc(mesg->storage.u.compact.size))) - HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "memory allocation failed for compact data buffer") H5MM_memcpy(mesg->storage.u.compact.buf, p, mesg->storage.u.compact.size); p += mesg->storage.u.compact.size; @@ -195,18 +228,23 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU } /* end if */ else { /* Layout & storage class */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") mesg->type = mesg->storage.type = (H5D_layout_t)*p++; /* Interpret the rest of the message according to the layout class */ switch (mesg->type) { case H5D_COMPACT: /* Compact data size */ + if (H5_IS_BUFFER_OVERFLOW(p, sizeof(uint16_t), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") UINT16DECODE(p, mesg->storage.u.compact.size); if (mesg->storage.u.compact.size > 0) { /* Ensure that size doesn't exceed buffer size, due to possible data corruption */ - if (p + mesg->storage.u.compact.size - 1 > p_end) - HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "storage size exceeds buffer size") + if (H5_IS_BUFFER_OVERFLOW(p, mesg->storage.u.compact.size, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") /* Allocate space for compact data */ if (NULL == (mesg->storage.u.compact.buf = H5MM_malloc(mesg->storage.u.compact.size))) @@ -224,9 +262,13 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU case H5D_CONTIGUOUS: /* Contiguous storage address */ + if (H5_IS_BUFFER_OVERFLOW(p, H5F_SIZEOF_ADDR(f), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") H5F_addr_decode(f, &p, &(mesg->storage.u.contig.addr)); /* Contiguous storage size */ + if (H5_IS_BUFFER_OVERFLOW(p, H5F_SIZEOF_SIZE(f), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") H5F_DECODE_LENGTH(f, p, mesg->storage.u.contig.size); /* Set the layout operations */ @@ -239,24 +281,36 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU mesg->u.chunk.flags = (uint8_t)0; /* Dimensionality */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.ndims = *p++; + if (mesg->u.chunk.ndims > H5O_LAYOUT_NDIMS) - HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "dimensionality is too large") + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "dimensionality is too large") if (mesg->u.chunk.ndims < 2) - HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "bad dimensions for chunked storage") + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "bad dimensions for chunked storage") /* B-tree address */ + if (H5_IS_BUFFER_OVERFLOW(p, H5F_SIZEOF_ADDR(f), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") H5F_addr_decode(f, &p, &(mesg->storage.u.chunk.idx_addr)); + if (H5_IS_BUFFER_OVERFLOW(p, (mesg->u.chunk.ndims * sizeof(uint32_t)), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") + /* Chunk dimensions */ for (u = 0; u < mesg->u.chunk.ndims; u++) { UINT32DECODE(p, mesg->u.chunk.dim[u]); /* Just in case that something goes very wrong, such as file corruption. */ if (mesg->u.chunk.dim[u] == 0) - HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, NULL, - "chunk dimension must be positive: mesg->u.chunk.dim[%u] = %u", u, - mesg->u.chunk.dim[u]) + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, + "bad chunk dimension value when parsing layout message - chunk " + "dimension must be positive: mesg->u.chunk.dim[%u] = %u", + u, mesg->u.chunk.dim[u]) } /* end for */ /* Compute chunk size */ @@ -270,6 +324,9 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU } /* end if */ else { /* Get the chunked layout flags */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.flags = *p++; /* Check for valid flags */ @@ -280,25 +337,50 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "bad flag value for message") /* Dimensionality */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.ndims = *p++; + if (mesg->u.chunk.ndims > H5O_LAYOUT_NDIMS) HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "dimensionality is too large") /* Encoded # of bytes for each chunk dimension */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.enc_bytes_per_dim = *p++; + if (mesg->u.chunk.enc_bytes_per_dim == 0 || mesg->u.chunk.enc_bytes_per_dim > 8) HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "encoded chunk dimension size is too large") + if (H5_IS_BUFFER_OVERFLOW(p, (mesg->u.chunk.ndims * mesg->u.chunk.enc_bytes_per_dim), + p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") + /* Chunk dimensions */ - for (u = 0; u < mesg->u.chunk.ndims; u++) + for (u = 0; u < mesg->u.chunk.ndims; u++) { UINT64DECODE_VAR(p, mesg->u.chunk.dim[u], mesg->u.chunk.enc_bytes_per_dim); + /* Just in case that something goes very wrong, such as file corruption. */ + if (mesg->u.chunk.dim[u] == 0) + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, + "bad chunk dimension value when parsing layout message - chunk " + "dimension must be positive: mesg->u.chunk.dim[%u] = %u", + u, mesg->u.chunk.dim[u]) + } + /* Compute chunk size */ for (u = 1, mesg->u.chunk.size = mesg->u.chunk.dim[0]; u < mesg->u.chunk.ndims; u++) mesg->u.chunk.size *= mesg->u.chunk.dim[u]; /* Chunk index type */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.idx_type = (H5D_chunk_index_t)*p++; + if (mesg->u.chunk.idx_type >= H5D_CHUNK_IDX_NTYPES) HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "unknown chunk index type") mesg->storage.u.chunk.idx_type = mesg->u.chunk.idx_type; @@ -315,6 +397,9 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU case H5D_CHUNK_IDX_SINGLE: /* Single Chunk Index */ if (mesg->u.chunk.flags & H5O_LAYOUT_CHUNK_SINGLE_INDEX_WITH_FILTER) { + if (H5_IS_BUFFER_OVERFLOW(p, H5F_SIZEOF_SIZE(f) + sizeof(uint32_t), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") H5F_DECODE_LENGTH(f, p, mesg->storage.u.chunk.u.single.nbytes); UINT32DECODE(p, mesg->storage.u.chunk.u.single.filter_mask); } /* end if */ @@ -325,9 +410,13 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU case H5D_CHUNK_IDX_FARRAY: /* Fixed array creation parameters */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.u.farray.cparam.max_dblk_page_nelmts_bits = *p++; + if (0 == mesg->u.chunk.u.farray.cparam.max_dblk_page_nelmts_bits) - HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "invalid fixed array creation parameter") /* Set the chunk operations */ @@ -336,25 +425,49 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU case H5D_CHUNK_IDX_EARRAY: /* Extensible array creation parameters */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.u.earray.cparam.max_nelmts_bits = *p++; + if (0 == mesg->u.chunk.u.earray.cparam.max_nelmts_bits) - HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "invalid extensible array creation parameter") + + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.u.earray.cparam.idx_blk_elmts = *p++; + if (0 == mesg->u.chunk.u.earray.cparam.idx_blk_elmts) - HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "invalid extensible array creation parameter") + + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.u.earray.cparam.sup_blk_min_data_ptrs = *p++; + if (0 == mesg->u.chunk.u.earray.cparam.sup_blk_min_data_ptrs) - HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "invalid extensible array creation parameter") + + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.u.earray.cparam.data_blk_min_elmts = *p++; + if (0 == mesg->u.chunk.u.earray.cparam.data_blk_min_elmts) - HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "invalid extensible array creation parameter") + + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.u.earray.cparam.max_dblk_page_nelmts_bits = *p++; + if (0 == mesg->u.chunk.u.earray.cparam.max_dblk_page_nelmts_bits) - HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "invalid extensible array creation parameter") /* Set the chunk operations */ @@ -362,10 +475,35 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU break; case H5D_CHUNK_IDX_BT2: /* v2 B-tree index */ + if (H5_IS_BUFFER_OVERFLOW(p, sizeof(uint32_t), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") UINT32DECODE(p, mesg->u.chunk.u.btree2.cparam.node_size); + + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.u.btree2.cparam.split_percent = *p++; + + if (mesg->u.chunk.u.btree2.cparam.split_percent == 0 || + mesg->u.chunk.u.btree2.cparam.split_percent > 100) + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, + "bad value for v2 B-tree split percent value - must be > 0 and " + "<= 100: split percent = %" PRIu8, + mesg->u.chunk.u.btree2.cparam.split_percent) + + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") mesg->u.chunk.u.btree2.cparam.merge_percent = *p++; + if (mesg->u.chunk.u.btree2.cparam.merge_percent == 0 || + mesg->u.chunk.u.btree2.cparam.merge_percent > 100) + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, + "bad value for v2 B-tree merge percent value - must be > 0 and " + "<= 100: merge percent = %" PRIu8, + mesg->u.chunk.u.btree2.cparam.merge_percent) + /* Set the chunk operations */ mesg->storage.u.chunk.ops = H5D_COPS_BT2; break; @@ -376,6 +514,9 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU } /* end switch */ /* Chunk index address */ + if (H5_IS_BUFFER_OVERFLOW(p, H5F_SIZEOF_ADDR(f), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") H5F_addr_decode(f, &p, &(mesg->storage.u.chunk.idx_addr)); } /* end else */ @@ -389,7 +530,13 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU HGOTO_ERROR(H5E_OHDR, H5E_VERSION, NULL, "invalid layout version with virtual layout") /* Heap information */ + if (H5_IS_BUFFER_OVERFLOW(p, H5F_SIZEOF_ADDR(f), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") H5F_addr_decode(f, &p, &(mesg->storage.u.virt.serial_list_hobjid.addr)); + /* NOTE: virtual mapping global heap entry address could be undefined */ + + if (H5_IS_BUFFER_OVERFLOW(p, sizeof(uint32_t), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") UINT32DECODE(p, mesg->storage.u.virt.serial_list_hobjid.idx); /* Initialize other fields */ @@ -405,54 +552,88 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU /* Decode heap block if it exists */ if (mesg->storage.u.virt.serial_list_hobjid.addr != HADDR_UNDEF) { const uint8_t *heap_block_p; + const uint8_t *heap_block_p_end; uint8_t heap_vers; size_t block_size = 0; size_t tmp_size; hsize_t tmp_hsize; uint32_t stored_chksum; uint32_t computed_chksum; - size_t i; /* Read heap */ if (NULL == (heap_block = (uint8_t *)H5HG_read( f, &(mesg->storage.u.virt.serial_list_hobjid), NULL, &block_size))) HGOTO_ERROR(H5E_OHDR, H5E_READERROR, NULL, "Unable to read global heap block") - heap_block_p = (const uint8_t *)heap_block; + heap_block_p = (const uint8_t *)heap_block; + heap_block_p_end = heap_block_p + block_size - 1; /* Decode the version number of the heap block encoding */ + if (H5_IS_BUFFER_OVERFLOW(heap_block_p, 1, heap_block_p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") heap_vers = (uint8_t)*heap_block_p++; + if ((uint8_t)H5O_LAYOUT_VDS_GH_ENC_VERS != heap_vers) HGOTO_ERROR(H5E_OHDR, H5E_VERSION, NULL, "bad version # of encoded VDS heap information, expected %u, got %u", (unsigned)H5O_LAYOUT_VDS_GH_ENC_VERS, (unsigned)heap_vers) /* Number of entries */ + if (H5_IS_BUFFER_OVERFLOW(heap_block_p, H5F_SIZEOF_SIZE(f), heap_block_p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") H5F_DECODE_LENGTH(f, heap_block_p, tmp_hsize) /* Allocate entry list */ if (NULL == (mesg->storage.u.virt.list = (H5O_storage_virtual_ent_t *)H5MM_calloc( (size_t)tmp_hsize * sizeof(H5O_storage_virtual_ent_t)))) - HGOTO_ERROR(H5E_OHDR, H5E_RESOURCE, NULL, "unable to allocate heap block") + HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, NULL, "unable to allocate heap block") mesg->storage.u.virt.list_nalloc = (size_t)tmp_hsize; mesg->storage.u.virt.list_nused = (size_t)tmp_hsize; /* Decode each entry */ - for (i = 0; i < mesg->storage.u.virt.list_nused; i++) { + for (size_t i = 0; i < mesg->storage.u.virt.list_nused; i++) { + ptrdiff_t avail_buffer_space; + + avail_buffer_space = heap_block_p_end - heap_block_p + 1; + if (avail_buffer_space <= 0) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") + /* Source file name */ - tmp_size = HDstrlen((const char *)heap_block_p) + 1; + tmp_size = HDstrnlen((const char *)heap_block_p, (size_t)avail_buffer_space); + if (tmp_size == (size_t)avail_buffer_space) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding - unterminated source " + "file name string") + else + tmp_size += 1; /* Add space for NUL terminator */ + if (NULL == (mesg->storage.u.virt.list[i].source_file_name = (char *)H5MM_malloc(tmp_size))) - HGOTO_ERROR(H5E_OHDR, H5E_RESOURCE, NULL, + HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, NULL, "unable to allocate memory for source file name") H5MM_memcpy(mesg->storage.u.virt.list[i].source_file_name, heap_block_p, tmp_size); heap_block_p += tmp_size; + avail_buffer_space = heap_block_p_end - heap_block_p + 1; + if (avail_buffer_space <= 0) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") + /* Source dataset name */ - tmp_size = HDstrlen((const char *)heap_block_p) + 1; + tmp_size = HDstrnlen((const char *)heap_block_p, (size_t)avail_buffer_space); + if (tmp_size == (size_t)avail_buffer_space) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding - unterminated source " + "dataset name string") + else + tmp_size += 1; /* Add space for NUL terminator */ + if (NULL == (mesg->storage.u.virt.list[i].source_dset_name = (char *)H5MM_malloc(tmp_size))) - HGOTO_ERROR(H5E_OHDR, H5E_RESOURCE, NULL, + HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, NULL, "unable to allocate memory for source dataset name") H5MM_memcpy(mesg->storage.u.virt.list[i].source_dset_name, heap_block_p, tmp_size); heap_block_p += tmp_size; @@ -535,6 +716,9 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU } /* end for */ /* Read stored checksum */ + if (H5_IS_BUFFER_OVERFLOW(heap_block_p, sizeof(uint32_t), heap_block_p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, + "ran off end of input buffer while decoding") UINT32DECODE(heap_block_p, stored_chksum) /* Compute checksum */ @@ -558,7 +742,7 @@ H5O__layout_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU case H5D_LAYOUT_ERROR: case H5D_NLAYOUTS: default: - HGOTO_ERROR(H5E_OHDR, H5E_CANTLOAD, NULL, "Invalid layout class") + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "Invalid layout class") } /* end switch */ } /* end else */ diff --git a/src/H5Osdspace.c b/src/H5Osdspace.c index c4b723f..e9a0dc6 100644 --- a/src/H5Osdspace.c +++ b/src/H5Osdspace.c @@ -107,11 +107,11 @@ static void * H5O__sdspace_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNUSED mesg_flags, unsigned H5_ATTR_UNUSED *ioflags, size_t p_size, const uint8_t *p) { - H5S_extent_t *sdim = NULL; /* New extent dimensionality structure */ + const uint8_t *p_end = p + p_size - 1; /* End of the p buffer */ + H5S_extent_t *sdim = NULL; /* New extent dimensionality structure */ unsigned flags, version; - unsigned i; /* Local counting variable */ - const uint8_t *p_end = p + p_size - 1; /* End of the p buffer */ - void *ret_value = NULL; /* Return value */ + unsigned i; + void *ret_value = NULL; /* Return value */ FUNC_ENTER_PACKAGE @@ -121,25 +121,37 @@ H5O__sdspace_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UN /* decode */ if (NULL == (sdim = H5FL_CALLOC(H5S_extent_t))) - HGOTO_ERROR(H5E_DATASPACE, H5E_NOSPACE, NULL, "dataspace structure allocation failed") + HGOTO_ERROR(H5E_DATASPACE, H5E_CANTALLOC, NULL, "dataspace structure allocation failed") + sdim->type = H5S_NO_CLASS; /* Check version */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") version = *p++; + if (version < H5O_SDSPACE_VERSION_1 || version > H5O_SDSPACE_VERSION_2) - HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, NULL, "wrong version number in dataspace message") + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "wrong version number in dataspace message") sdim->version = version; /* Get rank */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") sdim->rank = *p++; + if (sdim->rank > H5S_MAX_RANK) - HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, NULL, "simple dataspace dimensionality is too large") + HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "simple dataspace dimensionality is too large") /* Get dataspace flags for later */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") flags = *p++; /* Get or determine the type of the extent */ if (version >= H5O_SDSPACE_VERSION_2) { + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") sdim->type = (H5S_class_t)*p++; + if (sdim->type != H5S_SIMPLE && sdim->rank > 0) HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, NULL, "invalid rank for scalar or NULL dataspace") } /* end if */ @@ -151,36 +163,48 @@ H5O__sdspace_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UN sdim->type = H5S_SCALAR; /* Increment past reserved byte */ + if (H5_IS_BUFFER_OVERFLOW(p, 1, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") p++; } /* end else */ HDassert(sdim->type != H5S_NULL || sdim->version >= H5O_SDSPACE_VERSION_2); /* Only Version 1 has these reserved bytes */ - if (version == H5O_SDSPACE_VERSION_1) + if (version == H5O_SDSPACE_VERSION_1) { + if (H5_IS_BUFFER_OVERFLOW(p, 4, p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") p += 4; /*reserved*/ + } /* Decode dimension sizes */ if (sdim->rank > 0) { - /* Ensure that rank doesn't cause reading passed buffer's end, - due to possible data corruption */ uint8_t sizeof_size = H5F_SIZEOF_SIZE(f); - if (p + (sizeof_size * sdim->rank - 1) > p_end) { - HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "rank might cause reading passed buffer's end") - } + + /* + * Ensure that decoding doesn't cause reading past buffer's end, + * due to possible data corruption - check that we have space to + * decode a "sdim->rank" number of hsize_t values + */ + if (H5_IS_BUFFER_OVERFLOW(p, (sizeof_size * sdim->rank), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") if (NULL == (sdim->size = (hsize_t *)H5FL_ARR_MALLOC(hsize_t, (size_t)sdim->rank))) - HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "memory allocation failed") for (i = 0; i < sdim->rank; i++) H5F_DECODE_LENGTH(f, p, sdim->size[i]); if (flags & H5S_VALID_MAX) { if (NULL == (sdim->max = (hsize_t *)H5FL_ARR_MALLOC(hsize_t, (size_t)sdim->rank))) - HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed") - - /* Ensure that rank doesn't cause reading passed buffer's end */ - if (p + (sizeof_size * sdim->rank - 1) > p_end) - HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "rank might cause reading passed buffer's end") + HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "memory allocation failed") + + /* + * Ensure that decoding doesn't cause reading past buffer's end, + * due to possible data corruption - check that we have space to + * decode a "sdim->rank" number of hsize_t values + */ + if (H5_IS_BUFFER_OVERFLOW(p, (sizeof_size * sdim->rank), p_end)) + HGOTO_ERROR(H5E_OHDR, H5E_OVERFLOW, NULL, "ran off end of input buffer while decoding") for (i = 0; i < sdim->rank; i++) H5F_DECODE_LENGTH(f, p, sdim->max[i]); diff --git a/src/H5private.h b/src/H5private.h index ec77bbb..eb7d8e0 100644 --- a/src/H5private.h +++ b/src/H5private.h @@ -325,6 +325,11 @@ /* Raise an integer to a power of 2 */ #define H5_EXP2(n) (1 << (n)) +/* Check if a read of size bytes starting at ptr would overflow past + * the last valid byte, pointed to by buffer_end. + */ +#define H5_IS_BUFFER_OVERFLOW(ptr, size, buffer_end) (((ptr) + (size)-1) > (buffer_end)) + /* * HDF Boolean type. */ -- cgit v0.12