Skip to content

Commit

Permalink
Fixes for file format security issues (HDFGroup#4283)
Browse files Browse the repository at this point in the history
  • Loading branch information
jhendersonHDF authored and qkoziol committed Apr 2, 2024
1 parent 86504eb commit f2eaaa7
Show file tree
Hide file tree
Showing 39 changed files with 906 additions and 492 deletions.
3 changes: 0 additions & 3 deletions src/H5Dchunk.c
Original file line number Diff line number Diff line change
Expand Up @@ -7028,9 +7028,6 @@ H5D__chunk_copy(H5F_t *f_src, H5O_storage_chunk_t *storage_src, H5O_layout_chunk
bkg = udata.bkg;

done:
/* Caller expects that source datatype will be freed */
if (dt_src && (H5T_close(dt_src) < 0))
HDONE_ERROR(H5E_DATASET, H5E_CANTCLOSEOBJ, FAIL, "can't close temporary datatype");
if (dt_dst && (H5T_close(dt_dst) < 0))
HDONE_ERROR(H5E_DATASET, H5E_CANTCLOSEOBJ, FAIL, "can't close temporary datatype");
if (dt_mem && (H5T_close(dt_mem) < 0))
Expand Down
63 changes: 59 additions & 4 deletions src/H5Dcompact.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ typedef struct H5D_compact_iovv_memmanage_ud_t {

/* Layout operation callbacks */
static herr_t H5D__compact_construct(H5F_t *f, H5D_t *dset);
static herr_t H5D__compact_init(H5F_t *f, const H5D_t *dset, hid_t dapl_id);
static bool H5D__compact_is_space_alloc(const H5O_storage_t *storage);
static herr_t H5D__compact_io_init(H5D_io_info_t *io_info, H5D_dset_io_info_t *dinfo);
static herr_t H5D__compact_iovv_memmanage_cb(hsize_t dst_off, hsize_t src_off, size_t len, void *_udata);
Expand All @@ -79,7 +80,7 @@ static herr_t H5D__compact_dest(H5D_t *dset);
/* Compact storage layout I/O ops */
const H5D_layout_ops_t H5D_LOPS_COMPACT[1] = {{
H5D__compact_construct, /* construct */
NULL, /* init */
H5D__compact_init, /* init */
H5D__compact_is_space_alloc, /* is_space_alloc */
NULL, /* is_data_cached */
H5D__compact_io_init, /* io_init */
Expand Down Expand Up @@ -198,6 +199,63 @@ H5D__compact_construct(H5F_t *f, H5D_t *dset)
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__compact_construct() */

/*-------------------------------------------------------------------------
* Function: H5D__compact_init
*
* Purpose: Initialize the info for a compact dataset. This is
* called when the dataset is initialized.
*
* Return: Non-negative on success/Negative on failure
*
*-------------------------------------------------------------------------
*/
static herr_t
H5D__compact_init(H5F_t H5_ATTR_UNUSED *f, const H5D_t *dset, hid_t H5_ATTR_UNUSED dapl_id)
{
hssize_t snelmts; /* Temporary holder for number of elements in dataspace */
hsize_t nelmts; /* Number of elements in dataspace */
size_t dt_size; /* Size of datatype */
hsize_t data_size; /* Dataset size, in bytes */
herr_t ret_value = SUCCEED; /* Return value */

FUNC_ENTER_PACKAGE

/* Sanity check */
assert(dset);
assert(H5D_COMPACT == dset->shared->layout.storage.type);

/*
* Now that we've read the dataset's datatype, dataspace and
* layout information, perform a quick check for compact datasets
* to ensure that the size of the internal buffer that was
* allocated for the dataset's raw data matches the size of
* the data. A corrupted file can cause a mismatch between the
* two, which might result in buffer overflows during future
* I/O to the dataset.
*/
if (0 == (dt_size = H5T_GET_SIZE(dset->shared->type)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get datatype size");
if ((snelmts = H5S_GET_EXTENT_NPOINTS(dset->shared->space)) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "can't get number of elements in dataset's dataspace");
nelmts = (hsize_t)snelmts;

/* Compute the size of the dataset's contiguous storage */
data_size = nelmts * dt_size;

/* Check for overflow during multiplication */
if (nelmts != (data_size / dt_size))
HGOTO_ERROR(H5E_DATASET, H5E_OVERFLOW, FAIL, "size of dataset's storage overflowed");

/* Check for mismatch */
if (dset->shared->layout.storage.u.compact.size != data_size)
HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL,
"bad value from dataset header - size of compact dataset's data buffer doesn't match "
"size of dataset data");

done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__compact_init() */

/*-------------------------------------------------------------------------
* Function: H5D__compact_is_space_alloc
*
Expand Down Expand Up @@ -606,9 +664,6 @@ H5D__compact_copy(H5F_t *f_src, H5O_storage_compact_t *_storage_src, H5F_t *f_ds
storage_dst->dirty = true;

done:
/* Caller expects that source datatype will be freed */
if (dt_src && (H5T_close(dt_src) < 0))
HDONE_ERROR(H5E_DATASET, H5E_CANTCLOSEOBJ, FAIL, "can't close temporary datatype");
if (dt_dst && (H5T_close(dt_dst) < 0))
HDONE_ERROR(H5E_DATASET, H5E_CANTCLOSEOBJ, FAIL, "can't close temporary datatype");
if (dt_mem && (H5T_close(dt_mem) < 0))
Expand Down
91 changes: 71 additions & 20 deletions src/H5Dcontig.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,65 @@ H5D__contig_delete(H5F_t *f, const H5O_storage_t *storage)
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__contig_delete */

/*-------------------------------------------------------------------------
* Function: H5D__contig_check
*
* Purpose: Sanity check the contiguous info for a dataset.
*
* Return: Non-negative on success/Negative on failure
*
*-------------------------------------------------------------------------
*/
herr_t
H5D__contig_check(const H5F_t *f, const H5O_layout_t *layout, const H5S_extent_t *extent, const H5T_t *dt)
{
hsize_t nelmts; /* Number of elements in dataspace */
size_t dt_size; /* Size of datatype */
hsize_t data_size; /* Raw data size */
herr_t ret_value = SUCCEED; /* Return value */

FUNC_ENTER_PACKAGE

/* Sanity check */
assert(f);
assert(layout);
assert(extent);
assert(dt);

/* Retrieve the number of elements in the dataspace */
nelmts = H5S_extent_nelem(extent);

/* Get the datatype's size */
if (0 == (dt_size = H5T_GET_SIZE(dt)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to retrieve size of datatype");

/* Compute the size of the dataset's contiguous storage */
data_size = nelmts * dt_size;

/* Check for overflow during multiplication */
if (nelmts != (data_size / dt_size))
HGOTO_ERROR(H5E_DATASET, H5E_OVERFLOW, FAIL, "size of dataset's storage overflowed");

/* Check for invalid (corrupted in the file, probably) dimensions */
if (H5_addr_defined(layout->storage.u.contig.addr)) {
haddr_t rel_eoa; /* Relative end of file address */

if (HADDR_UNDEF == (rel_eoa = H5F_get_eoa(f, H5FD_MEM_DRAW)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to determine file size");

/* Check for invalid dataset size (from bad dimensions) putting the
* dataset elements off the end of the file
*/
if (H5_addr_le((layout->storage.u.contig.addr + data_size), layout->storage.u.contig.addr))
HGOTO_ERROR(H5E_DATASET, H5E_OVERFLOW, FAIL, "invalid dataset size, likely file corruption");
if (H5_addr_gt((layout->storage.u.contig.addr + data_size), rel_eoa))
HGOTO_ERROR(H5E_DATASET, H5E_OVERFLOW, FAIL, "invalid dataset size, likely file corruption");
}

done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__contig_check() */

/*-------------------------------------------------------------------------
* Function: H5D__contig_construct
*
Expand Down Expand Up @@ -438,18 +497,22 @@ H5D__contig_construct(H5F_t *f, H5D_t *dset)
*-------------------------------------------------------------------------
*/
static herr_t
H5D__contig_init(H5F_t H5_ATTR_UNUSED *f, const H5D_t *dset, hid_t H5_ATTR_UNUSED dapl_id)
H5D__contig_init(H5F_t *f, const H5D_t *dset, hid_t H5_ATTR_UNUSED dapl_id)
{
hsize_t tmp_size; /* Temporary holder for raw data size */
size_t tmp_sieve_buf_size; /* Temporary holder for sieve buffer size */
herr_t ret_value = SUCCEED; /* Return value */
size_t tmp_sieve_buf_size; /* Temporary holder for sieve buffer size */
herr_t ret_value = SUCCEED; /* Return value */

FUNC_ENTER_PACKAGE

/* Sanity check */
assert(f);
assert(dset);

/* Sanity check the dataset's info */
if (H5D__contig_check(f, &dset->shared->layout, H5S_GET_EXTENT(dset->shared->space), dset->shared->type) <
0)
HGOTO_ERROR(H5E_DATASET, H5E_BADVALUE, FAIL, "invalid dataset info");

/* Compute the size of the contiguous storage for versions of the
* layout message less than version 3 because versions 1 & 2 would
* truncate the dimension sizes to 32-bits of information. - QAK 5/26/04
Expand All @@ -469,25 +532,16 @@ H5D__contig_init(H5F_t H5_ATTR_UNUSED *f, const H5D_t *dset, hid_t H5_ATTR_UNUSE
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to retrieve size of datatype");

/* Compute the size of the dataset's contiguous storage */
tmp_size = nelmts * dt_size;

/* Check for overflow during multiplication */
if (nelmts != (tmp_size / dt_size))
HGOTO_ERROR(H5E_DATASET, H5E_OVERFLOW, FAIL, "size of dataset's storage overflowed");

/* Assign the dataset's contiguous storage size */
dset->shared->layout.storage.u.contig.size = tmp_size;
} /* end if */
else
tmp_size = dset->shared->layout.storage.u.contig.size;
dset->shared->layout.storage.u.contig.size = nelmts * dt_size;
}

/* Get the sieve buffer size for the file */
tmp_sieve_buf_size = H5F_SIEVE_BUF_SIZE(dset->oloc.file);

/* Adjust the sieve buffer size to the smaller one between the dataset size and the buffer size
* from the file access property. (SLU - 2012/3/30) */
if (tmp_size < tmp_sieve_buf_size)
dset->shared->cache.contig.sieve_buf_size = tmp_size;
if (dset->shared->layout.storage.u.contig.size < tmp_sieve_buf_size)
dset->shared->cache.contig.sieve_buf_size = dset->shared->layout.storage.u.contig.size;
else
dset->shared->cache.contig.sieve_buf_size = tmp_sieve_buf_size;

Expand Down Expand Up @@ -1810,9 +1864,6 @@ H5D__contig_copy(H5F_t *f_src, const H5O_storage_contig_t *storage_src, H5F_t *f
} /* end while */

done:
/* Caller expects that source datatype will be freed */
if (dt_src && (H5T_close(dt_src) < 0))
HDONE_ERROR(H5E_DATASET, H5E_CANTCLOSEOBJ, FAIL, "can't close temporary datatype");
if (dt_dst && (H5T_close(dt_dst) < 0))
HDONE_ERROR(H5E_DATASET, H5E_CANTCLOSEOBJ, FAIL, "can't close temporary datatype");
if (dt_mem && (H5T_close(dt_mem) < 0))
Expand Down
63 changes: 58 additions & 5 deletions src/H5Defl.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ typedef struct H5D_efl_writevv_ud_t {

/* Layout operation callbacks */
static herr_t H5D__efl_construct(H5F_t *f, H5D_t *dset);
static herr_t H5D__efl_init(H5F_t *f, const H5D_t *dset, hid_t dapl_id);
static herr_t H5D__efl_io_init(H5D_io_info_t *io_info, H5D_dset_io_info_t *dinfo);
static ssize_t H5D__efl_readvv(const H5D_io_info_t *io_info, const H5D_dset_io_info_t *dset_info,
size_t dset_max_nseq, size_t *dset_curr_seq, size_t dset_len_arr[],
Expand All @@ -77,7 +78,7 @@ static herr_t H5D__efl_write(const H5O_efl_t *efl, const H5D_t *dset, haddr_t ad
/* External File List (EFL) storage layout I/O ops */
const H5D_layout_ops_t H5D_LOPS_EFL[1] = {{
H5D__efl_construct, /* construct */
NULL, /* init */
H5D__efl_init, /* init */
H5D__efl_is_space_alloc, /* is_space_alloc */
NULL, /* is_data_cached */
H5D__efl_io_init, /* io_init */
Expand Down Expand Up @@ -137,8 +138,9 @@ H5D__efl_construct(H5F_t *f, H5D_t *dset)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to determine datatype size");

/* Check for storage overflows */
max_points = H5S_get_npoints_max(dset->shared->space);
max_storage = H5O_efl_total_size(&dset->shared->dcpl_cache.efl);
max_points = H5S_get_npoints_max(dset->shared->space);
if (H5O_efl_total_size(&dset->shared->dcpl_cache.efl, &max_storage) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to retrieve size of external file");
if (H5S_UNLIMITED == max_points) {
if (H5O_EFL_UNLIMITED != max_storage)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unlimited dataspace but finite storage");
Expand All @@ -149,8 +151,8 @@ H5D__efl_construct(H5F_t *f, H5D_t *dset)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "dataspace size exceeds external storage size");

/* Compute the total size of dataset */
stmp_size = H5S_GET_EXTENT_NPOINTS(dset->shared->space);
assert(stmp_size >= 0);
if ((stmp_size = H5S_GET_EXTENT_NPOINTS(dset->shared->space)) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to retrieve number of elements in dataspace");
tmp_size = (hsize_t)stmp_size * dt_size;
H5_CHECKED_ASSIGN(dset->shared->layout.storage.u.contig.size, hsize_t, tmp_size, hssize_t);

Expand All @@ -161,6 +163,57 @@ H5D__efl_construct(H5F_t *f, H5D_t *dset)
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__efl_construct() */

/*-------------------------------------------------------------------------
* Function: H5D__efl_init
*
* Purpose: Initialize the info for a EFL dataset. This is
* called when the dataset is initialized.
*
* Return: Non-negative on success/Negative on failure
*
*-------------------------------------------------------------------------
*/
static herr_t
H5D__efl_init(H5F_t H5_ATTR_UNUSED *f, const H5D_t *dset, hid_t H5_ATTR_UNUSED dapl_id)
{
size_t dt_size; /* Size of datatype */
hssize_t snelmts; /* Temporary holder for number of elements in dataspace */
hsize_t nelmts; /* Number of elements in dataspace */
hsize_t data_size; /* Raw data size */
hsize_t max_storage; /* Maximum storage size */
herr_t ret_value = SUCCEED; /* Return value */

FUNC_ENTER_PACKAGE

/* Sanity check */
assert(dset);

/* Retrieve the size of the dataset's datatype */
if (0 == (dt_size = H5T_get_size(dset->shared->type)))
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "unable to determine datatype size");

/* Retrieve the number of elements in the dataspace */
if ((snelmts = H5S_GET_EXTENT_NPOINTS(dset->shared->space)) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to retrieve number of elements in dataspace");
nelmts = (hsize_t)snelmts;

/* Compute the size of the dataset's contiguous storage */
data_size = nelmts * dt_size;

/* Check for overflow during multiplication */
if (nelmts != (data_size / dt_size))
HGOTO_ERROR(H5E_DATASET, H5E_OVERFLOW, FAIL, "size of dataset's storage overflowed");

/* Check for storage overflows */
if (H5O_efl_total_size(&dset->shared->dcpl_cache.efl, &max_storage) < 0)
HGOTO_ERROR(H5E_DATASET, H5E_CANTGET, FAIL, "unable to retrieve size of external file");
if (H5O_EFL_UNLIMITED != max_storage && data_size > max_storage)
HGOTO_ERROR(H5E_DATASET, H5E_CANTINIT, FAIL, "dataspace size exceeds external storage size");

done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5D__efl_init() */

/*-------------------------------------------------------------------------
* Function: H5D__efl_is_space_alloc
*
Expand Down
Loading

0 comments on commit f2eaaa7

Please sign in to comment.