File Pass-compact-chunk-size-info-to-ensure-requested-elements-are-within-bounds.patch of Package hdf5.28370
From: Egbert Eich <eich@suse.com>
Date: Sat Oct 1 15:13:52 2022 +0200
Subject: Pass compact chunk size info to ensure requested elements are within bounds
Patch-mainline: Not yet
Git-repo: https://github.com/HDFGroup/hdf5
Git-commit: 0bce664fd82795520bf706a741a183acb69476c3
References:
To avoid reading/writing elements out of bounds of a compact chunk, pass
size info and check whether all elements are within the size before attempting
to read/write these elements. Such accesses can occur when accessing malformed
hdf5 files.
This fixes CVE-2018-11205
Signed-off-by: Egbert Eich <eich@suse.com>
Signed-off-by: Egbert Eich <eich@suse.de>
---
src/H5Dchunk.c | 36 ++++++++++++++++++++++++++++--------
src/H5Dcompact.c | 6 ++++++
src/H5Dpkg.h | 1 +
3 files changed, 35 insertions(+), 8 deletions(-)
diff --git a/src/H5Dchunk.c b/src/H5Dchunk.c
index a727d88602..6c3c69b28d 100644
--- a/src/H5Dchunk.c
+++ b/src/H5Dchunk.c
@@ -128,6 +128,7 @@ typedef struct H5D_rdcc_ent_t {
H5F_block_t chunk_block; /*offset/length of chunk in file */
hsize_t chunk_idx; /*index of chunk in dataset */
uint8_t * chunk; /*the unfiltered chunk data */
+ size_t size; /*size of chunk */
unsigned idx; /*index in hash table */
struct H5D_rdcc_ent_t *next; /*next item in doubly-linked list */
struct H5D_rdcc_ent_t *prev; /*previous item in doubly-linked list */
@@ -303,7 +304,7 @@ static herr_t H5D__chunk_cache_evict(const H5D_t *dset, H5D_rdcc_ent_t *ent, h
static hbool_t H5D__chunk_is_partial_edge_chunk(unsigned dset_ndims, const uint32_t *chunk_dims,
const hsize_t *chunk_scaled, const hsize_t *dset_dims);
static void * H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t relax,
- hbool_t prev_unfilt_chunk);
+ hbool_t prev_unfilt_chunk, size_t *ret_size);
static herr_t H5D__chunk_unlock(const H5D_io_info_t *io_info, const H5D_chunk_ud_t *udata, hbool_t dirty,
void *chunk, uint32_t naccessed);
static herr_t H5D__chunk_cache_prune(const H5D_t *dset, size_t size);
@@ -2455,6 +2456,7 @@ H5D__chunk_read(H5D_io_info_t *io_info, const H5D_type_info_t *type_info, hsize_
uint32_t src_accessed_bytes = 0; /* Total accessed size in a chunk */
hbool_t skip_missing_chunks = FALSE; /* Whether to skip missing chunks */
herr_t ret_value = SUCCEED; /*return value */
+ size_t chunk_size = 0;
FUNC_ENTER_STATIC
@@ -2540,11 +2542,12 @@ H5D__chunk_read(H5D_io_info_t *io_info, const H5D_type_info_t *type_info, hsize_
src_accessed_bytes = chunk_info->chunk_points * (uint32_t)type_info->src_type_size;
/* Lock the chunk into the cache */
- if (NULL == (chunk = H5D__chunk_lock(io_info, &udata, FALSE, FALSE)))
+ if (NULL == (chunk = H5D__chunk_lock(io_info, &udata, FALSE, FALSE, &chunk_size)))
HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "unable to read raw data chunk")
/* Set up the storage buffer information for this chunk */
cpt_store.compact.buf = chunk;
+ cpt_store.compact.size = chunk_size;
/* Point I/O info at contiguous I/O info for this chunk */
chk_io_info = &cpt_io_info;
@@ -2604,6 +2607,7 @@ H5D__chunk_write(H5D_io_info_t *io_info, const H5D_type_info_t *type_info, hsize
hbool_t cpt_dirty; /* Temporary placeholder for compact storage "dirty" flag */
uint32_t dst_accessed_bytes = 0; /* Total accessed size in a chunk */
herr_t ret_value = SUCCEED; /* Return value */
+ size_t chunk_size;
FUNC_ENTER_STATIC
@@ -2674,11 +2678,12 @@ H5D__chunk_write(H5D_io_info_t *io_info, const H5D_type_info_t *type_info, hsize
entire_chunk = FALSE;
/* Lock the chunk into the cache */
- if (NULL == (chunk = H5D__chunk_lock(io_info, &udata, entire_chunk, FALSE)))
+ if (NULL == (chunk = H5D__chunk_lock(io_info, &udata, entire_chunk, FALSE, &chunk_size)))
HGOTO_ERROR(H5E_IO, H5E_READERROR, FAIL, "unable to read raw data chunk")
/* Set up the storage buffer information for this chunk */
cpt_store.compact.buf = chunk;
+ cpt_store.compact.size = chunk_size;
/* Point I/O info at main I/O info for this chunk */
chk_io_info = &cpt_io_info;
@@ -3674,7 +3679,7 @@ done:
*-------------------------------------------------------------------------
*/
static void *
-H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t relax, hbool_t prev_unfilt_chunk)
+H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t relax, hbool_t prev_unfilt_chunk, size_t *ret_size)
{
const H5D_t * dset = io_info->dset; /* Local pointer to the dataset info */
const H5O_pline_t *pline =
@@ -3691,6 +3696,7 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
hbool_t disable_filters = FALSE; /* Whether to disable filters (when adding to cache) */
void * chunk = NULL; /*the file chunk */
void * ret_value = NULL; /* Return value */
+ size_t chunk_size_ret = 0;
FUNC_ENTER_STATIC
@@ -3756,6 +3762,7 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
ent->chunk = (uint8_t *)H5D__chunk_mem_xfree(ent->chunk, old_pline);
ent->chunk = (uint8_t *)chunk;
chunk = NULL;
+ ent->size = chunk_size;
/* Mark the chunk as having filters disabled as well as "newly
* disabled" so it is inserted on flush */
@@ -3783,6 +3790,7 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
ent->chunk = (uint8_t *)H5D__chunk_mem_xfree(ent->chunk, old_pline);
ent->chunk = (uint8_t *)chunk;
chunk = NULL;
+ ent->size = chunk_size;
/* Mark the chunk as having filters enabled */
ent->edge_chunk_state &= ~(H5D_RDCC_DISABLE_FILTERS | H5D_RDCC_NEWLY_DISABLED_FILTERS);
@@ -3862,6 +3870,7 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
/* In the case that some dataset functions look through this data,
* clear it to all 0s. */
HDmemset(chunk, 0, chunk_size);
+ chunk_size_ret = chunk_size;
} /* end if */
else {
/*
@@ -3883,6 +3892,7 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
if (H5F_block_read(dset->oloc.file, H5FD_MEM_DRAW, chunk_addr, my_chunk_alloc, chunk) < 0)
HGOTO_ERROR(H5E_IO, H5E_READERROR, NULL, "unable to read raw data chunk")
+ chunk_size_ret = my_chunk_alloc;
if (old_pline && old_pline->nused) {
H5Z_EDC_t err_detect; /* Error detection info */
H5Z_cb_t filter_cb; /* I/O filter callback function */
@@ -3896,6 +3906,7 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
if (H5Z_pipeline(old_pline, H5Z_FLAG_REVERSE, &(udata->filter_mask), err_detect,
filter_cb, &my_chunk_alloc, &buf_alloc, &chunk) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTFILTER, NULL, "data pipeline read failed")
+ chunk_size_ret = buf_alloc;
/* Reallocate chunk if necessary */
if (udata->new_unfilt_chunk) {
@@ -3906,6 +3917,8 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL,
"memory allocation failed for raw data chunk")
} /* end if */
+ chunk_size_ret = my_chunk_alloc;
+ /* orly? */
H5MM_memcpy(chunk, tmp_chunk, chunk_size);
(void)H5D__chunk_mem_xfree(tmp_chunk, old_pline);
} /* end if */
@@ -3926,6 +3939,7 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL,
"memory allocation failed for raw data chunk")
+ chunk_size_ret = chunk_size;
if (H5P_is_fill_value_defined(fill, &fill_status) < 0)
HGOTO_ERROR(H5E_PLIST, H5E_CANTGET, NULL, "can't tell if fill value defined")
@@ -3991,6 +4005,7 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
H5_CHECKED_ASSIGN(ent->rd_count, uint32_t, chunk_size, size_t);
H5_CHECKED_ASSIGN(ent->wr_count, uint32_t, chunk_size, size_t);
ent->chunk = (uint8_t *)chunk;
+ ent->size = chunk_size_ret;
/* Add it to the cache */
HDassert(NULL == rdcc->slot[udata->idx_hint]);
@@ -4024,6 +4039,7 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
HDassert(!ent->locked);
ent->locked = TRUE;
chunk = ent->chunk;
+ chunk_size_ret = ent->size;
} /* end if */
else
/*
@@ -4035,6 +4051,8 @@ H5D__chunk_lock(const H5D_io_info_t *io_info, H5D_chunk_ud_t *udata, hbool_t rel
/* Set return value */
ret_value = chunk;
+ if (ret_size != NULL)
+ *ret_size = chunk_size_ret;
done:
/* Release the fill buffer info, if it's been initialized */
@@ -4043,9 +4061,11 @@ done:
/* Release the chunk allocated, on error */
if (!ret_value)
- if (chunk)
+ if (chunk) {
chunk = H5D__chunk_mem_xfree(chunk, pline);
-
+ if (ret_size != NULL)
+ *ret_size = 0;
+ }
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__chunk_lock() */
@@ -4836,7 +4856,7 @@ H5D__chunk_update_old_edge_chunks(H5D_t *dset, hsize_t old_dim[])
if (H5F_addr_defined(chk_udata.chunk_block.offset) || (UINT_MAX != chk_udata.idx_hint)) {
/* Lock the chunk into cache. H5D__chunk_lock will take care of
* updating the chunk to no longer be an edge chunk. */
- if (NULL == (chunk = (void *)H5D__chunk_lock(&chk_io_info, &chk_udata, FALSE, TRUE)))
+ if (NULL == (chunk = (void *)H5D__chunk_lock(&chk_io_info, &chk_udata, FALSE, TRUE, NULL)))
HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "unable to lock raw data chunk")
/* Unlock the chunk */
@@ -5143,7 +5163,7 @@ H5D__chunk_prune_fill(H5D_chunk_it_ud1_t *udata, hbool_t new_unfilt_chunk)
HGOTO_ERROR(H5E_DATASET, H5E_CANTSELECT, FAIL, "unable to select hyperslab")
/* Lock the chunk into the cache, to get a pointer to the chunk buffer */
- if (NULL == (chunk = (void *)H5D__chunk_lock(io_info, &chk_udata, FALSE, FALSE)))
+ if (NULL == (chunk = (void *)H5D__chunk_lock(io_info, &chk_udata, FALSE, FALSE, NULL)))
HGOTO_ERROR(H5E_DATASET, H5E_READERROR, FAIL, "unable to lock raw data chunk")
/* Fill the selection in the memory buffer */
diff --git a/src/H5Dcompact.c b/src/H5Dcompact.c
index 8d823adcea..d0831c019a 100644
--- a/src/H5Dcompact.c
+++ b/src/H5Dcompact.c
@@ -234,6 +234,7 @@ H5D__compact_io_init(const H5D_io_info_t *io_info, const H5D_type_info_t H5_ATTR
FUNC_ENTER_STATIC_NOERR
io_info->store->compact.buf = io_info->dset->shared->layout.storage.u.compact.buf;
+ io_info->store->compact.size = io_info->dset->shared->layout.storage.u.compact.size;
io_info->store->compact.dirty = &io_info->dset->shared->layout.storage.u.compact.dirty;
FUNC_LEAVE_NOAPI(SUCCEED)
@@ -267,6 +268,8 @@ H5D__compact_readvv(const H5D_io_info_t *io_info, size_t dset_max_nseq, size_t *
FUNC_ENTER_STATIC
HDassert(io_info);
+ if (io_info->store->compact.size < *(dset_offset_arr + dset_max_nseq - 1) + *(dset_size_arr + dset_max_nseq - 1))
+ HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "source size less than requested data")
/* Use the vectorized memory copy routine to do actual work */
if ((ret_value = H5VM_memcpyvv(io_info->u.rbuf, mem_max_nseq, mem_curr_seq, mem_size_arr, mem_offset_arr,
@@ -309,6 +312,9 @@ H5D__compact_writevv(const H5D_io_info_t *io_info, size_t dset_max_nseq, size_t
FUNC_ENTER_STATIC
HDassert(io_info);
+ if (io_info->store->compact.size < *(dset_offset_arr + dset_max_nseq - 1) + *(dset_size_arr + dset_max_nseq - 1)) {
+ HGOTO_ERROR(H5E_IO, H5E_WRITEERROR, FAIL, "source size less than requested data")
+ }
/* Use the vectorized memory copy routine to do actual work */
if ((ret_value = H5VM_memcpyvv(io_info->store->compact.buf, dset_max_nseq, dset_curr_seq, dset_size_arr,
diff --git a/src/H5Dpkg.h b/src/H5Dpkg.h
index 16e101c012..ccdd1675a1 100644
--- a/src/H5Dpkg.h
+++ b/src/H5Dpkg.h
@@ -194,6 +194,7 @@ typedef struct {
typedef struct {
void * buf; /* Buffer for compact dataset */
hbool_t *dirty; /* Pointer to dirty flag to mark */
+ size_t size; /* Buffer size for compact dataset */
} H5D_compact_storage_t;
typedef union H5D_storage_t {