Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for file format security issues #4283

Merged
merged 3 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading