summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjhendersonHDF <jhenderson@hdfgroup.org>2023-04-15 00:28:02 (GMT)
committerGitHub <noreply@github.com>2023-04-15 00:28:02 (GMT)
commitae202d3d62195dde72b15fb9cff90ed5ea3f9553 (patch)
treedee008c7577eeeaaea9949de3c3dcd4d4e04d2a3
parent69c720bffa4980779dc06d3cf9cd9f271bc8588b (diff)
downloadhdf5-ae202d3d62195dde72b15fb9cff90ed5ea3f9553.zip
hdf5-ae202d3d62195dde72b15fb9cff90ed5ea3f9553.tar.gz
hdf5-ae202d3d62195dde72b15fb9cff90ed5ea3f9553.tar.bz2
Add buffer overrun checks to H5O__layout_decode and H5O__sdspace_decode (#2679) (#2728)
-rw-r--r--release_docs/RELEASE.txt28
-rw-r--r--src/H5Olayout.c256
-rw-r--r--src/H5Osdspace.c62
-rw-r--r--src/H5private.h5
4 files changed, 296 insertions, 55 deletions
diff --git a/release_docs/RELEASE.txt b/release_docs/RELEASE.txt
index 735ac42..520fcc7 100644
--- a/release_docs/RELEASE.txt
+++ b/release_docs/RELEASE.txt
@@ -156,6 +156,34 @@ Bug Fixes since HDF5-1.14.0 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 issues in the Subfiling VFD when using the SELECT_IOC_EVERY_NTH_RANK
+ or SELECT_IOC_TOTAL I/O concentrator selection strategies
+
+ Multiple bugs involving these I/O concentrator selection strategies
+ were fixed, including:
+
+ * A bug that caused the selection strategy to be altered when
+ criteria for the strategy was specified in the
+ H5FD_SUBFILING_IOC_SELECTION_CRITERIA environment variable as
+ a single value, rather than in the old and undocumented
+ 'integer:integer' format
+ * Two bugs which caused a request for 'N' I/O concentrators to
+ result in 'N - 1' I/O concentrators being assigned, which also
+ lead to issues if only 1 I/O concentrator was requested
+
+ Also added a regression test for these two I/O concentrator selection
+ strategies to prevent future issues.
+
+ (JTH - 2023/03/15)
+
- Memory leak
Memory leak was detected when running h5dump with "pov". The memory was allocated
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 0b68305..45cf9ae 100644
--- a/src/H5private.h
+++ b/src/H5private.h
@@ -324,6 +324,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.
*/