Skip to content

Commit

Permalink
Fixes a bad memory read and unfreed memory in fsinfo code (HDFGroup#893)
Browse files Browse the repository at this point in the history
* Fixes a bad memory read and unfreed memory in fsinfo code

The segfaul from CVE-2020-10810 was fixed some time ago, but the
illegal memory read and unfreed memory were not.

This fix tracks some buffer sizes and errors out gracefully on errors,
ensuring buffers are cleaned up and avoiding the H5FL infinite loop +
abort on library close.

* Committing clang-format changes

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
derobins and github-actions[bot] committed Aug 12, 2021
1 parent 7c918e6 commit b5c6652
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 14 deletions.
1 change: 1 addition & 0 deletions MANIFEST
Expand Up @@ -1168,6 +1168,7 @@
./test/cork.c
./test/corrupt_stab_msg.h5
./test/cross_read.c
./test/cve_2020_10810.h5
./test/dangle.c
./test/deflate.h5
./test/del_many_dense_attrs.c
Expand Down
18 changes: 18 additions & 0 deletions release_docs/RELEASE.txt
Expand Up @@ -924,6 +924,24 @@ Bug Fixes since HDF5-1.12.0 release
===================================
Library
-------
- Fixed an invalid read and memory leak when parsing corrupt file space
info messages

When the corrupt file from CVE-2020-10810 was parsed by the library,
the code that imports the version 0 file space info object header
message to the version 1 struct could read past the buffer read from
the disk, causing an invalid memory read. Not catching this error would
cause downstream errors that eventually resulted in a previously
allocated buffer to be unfreed when the library shut down. In builds
where the free lists are in use, this could result in an infinite loop
and SIGABRT when the library shuts down.

We now track the buffer size and raise an error on attempts to read
past the end of it.

(DER - 2021/08/12, HDFFV-11053)


- Fixed CVE-2018-14460

The tool h5repack produced a segfault when the rank in dataspace
Expand Down
21 changes: 12 additions & 9 deletions src/H5Ocache.c
Expand Up @@ -78,8 +78,8 @@ static herr_t H5O__cache_chk_free_icr(void *thing);
static herr_t H5O__prefix_deserialize(const uint8_t *image, H5O_cache_ud_t *udata);

/* Chunk routines */
static herr_t H5O__chunk_deserialize(H5O_t *oh, haddr_t addr, size_t len, const uint8_t *image,
H5O_common_cache_ud_t *udata, hbool_t *dirty);
static herr_t H5O__chunk_deserialize(H5O_t *oh, haddr_t addr, size_t chunk_size, const uint8_t *image,
size_t len, H5O_common_cache_ud_t *udata, hbool_t *dirty);
static herr_t H5O__chunk_serialize(const H5F_t *f, H5O_t *oh, unsigned chunkno);

/* Misc. routines */
Expand Down Expand Up @@ -287,7 +287,7 @@ H5O__cache_verify_chksum(const void *_image, size_t len, void *_udata)
*-------------------------------------------------------------------------
*/
static void *
H5O__cache_deserialize(const void *image, size_t H5_ATTR_NDEBUG_UNUSED len, void *_udata, hbool_t *dirty)
H5O__cache_deserialize(const void *image, size_t len, void *_udata, hbool_t *dirty)
{
H5O_t * oh = NULL; /* Object header read in */
H5O_cache_ud_t *udata = (H5O_cache_ud_t *)_udata; /* User data for callback */
Expand Down Expand Up @@ -333,7 +333,7 @@ H5O__cache_deserialize(const void *image, size_t H5_ATTR_NDEBUG_UNUSED len, void
oh->proxy = NULL;

/* Parse the first chunk */
if (H5O__chunk_deserialize(oh, udata->common.addr, udata->chunk0_size, (const uint8_t *)image,
if (H5O__chunk_deserialize(oh, udata->common.addr, udata->chunk0_size, (const uint8_t *)image, len,
&(udata->common), dirty) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, NULL, "can't deserialize first object header chunk")

Expand Down Expand Up @@ -736,7 +736,7 @@ H5O__cache_chk_verify_chksum(const void *_image, size_t len, void *_udata)
*-------------------------------------------------------------------------
*/
static void *
H5O__cache_chk_deserialize(const void *image, size_t H5_ATTR_NDEBUG_UNUSED len, void *_udata, hbool_t *dirty)
H5O__cache_chk_deserialize(const void *image, size_t len, void *_udata, hbool_t *dirty)
{
H5O_chunk_proxy_t * chk_proxy = NULL; /* Chunk proxy object */
H5O_chk_cache_ud_t *udata = (H5O_chk_cache_ud_t *)_udata; /* User data for callback */
Expand All @@ -763,7 +763,7 @@ H5O__cache_chk_deserialize(const void *image, size_t H5_ATTR_NDEBUG_UNUSED len,
HDassert(udata->common.cont_msg_info);

/* Parse the chunk */
if (H5O__chunk_deserialize(udata->oh, udata->common.addr, udata->size, (const uint8_t *)image,
if (H5O__chunk_deserialize(udata->oh, udata->common.addr, udata->size, (const uint8_t *)image, len,
&(udata->common), dirty) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTINIT, NULL, "can't deserialize object header chunk")

Expand Down Expand Up @@ -1275,7 +1275,7 @@ H5O__prefix_deserialize(const uint8_t *_image, H5O_cache_ud_t *udata)
*-------------------------------------------------------------------------
*/
static herr_t
H5O__chunk_deserialize(H5O_t *oh, haddr_t addr, size_t len, const uint8_t *image,
H5O__chunk_deserialize(H5O_t *oh, haddr_t addr, size_t chunk_size, const uint8_t *image, size_t len,
H5O_common_cache_ud_t *udata, hbool_t *dirty)
{
const uint8_t *chunk_image; /* Pointer into buffer to decode */
Expand All @@ -1295,6 +1295,7 @@ H5O__chunk_deserialize(H5O_t *oh, haddr_t addr, size_t len, const uint8_t *image
HDassert(oh);
HDassert(H5F_addr_defined(addr));
HDassert(image);
HDassert(len);
HDassert(udata->f);
HDassert(udata->cont_msg_info);

Expand All @@ -1315,14 +1316,16 @@ H5O__chunk_deserialize(H5O_t *oh, haddr_t addr, size_t len, const uint8_t *image
oh->chunk[chunkno].addr = addr;
if (chunkno == 0)
/* First chunk's 'image' includes room for the object header prefix */
oh->chunk[0].size = len + (size_t)H5O_SIZEOF_HDR(oh);
oh->chunk[0].size = chunk_size + (size_t)H5O_SIZEOF_HDR(oh);
else
oh->chunk[chunkno].size = len;
oh->chunk[chunkno].size = chunk_size;
if (NULL == (oh->chunk[chunkno].image = H5FL_BLK_MALLOC(chunk_image, oh->chunk[chunkno].size)))
HGOTO_ERROR(H5E_OHDR, H5E_CANTALLOC, FAIL, "memory allocation failed")
oh->chunk[chunkno].chunk_proxy = NULL;

/* Copy disk image into chunk's image */
if (len < oh->chunk[chunkno].size)
HGOTO_ERROR(H5E_OHDR, H5E_CANTCOPY, FAIL, "attempted to copy too many disk image bytes into buffer")
H5MM_memcpy(oh->chunk[chunkno].image, image, oh->chunk[chunkno].size);

/* Point into chunk image to decode */
Expand Down
15 changes: 10 additions & 5 deletions src/H5Ofsinfo.c
Expand Up @@ -91,11 +91,12 @@ H5FL_DEFINE_STATIC(H5O_fsinfo_t);
*/
static void *
H5O__fsinfo_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNUSED mesg_flags,
unsigned H5_ATTR_UNUSED *ioflags, size_t H5_ATTR_UNUSED p_size, const uint8_t *p)
unsigned H5_ATTR_UNUSED *ioflags, size_t p_size, const uint8_t *p)
{
H5O_fsinfo_t * fsinfo = NULL; /* File space info message */
H5F_mem_page_t ptype; /* Memory type for iteration */
unsigned vers; /* message version */
H5O_fsinfo_t * fsinfo = NULL; /* File space info message */
H5F_mem_page_t ptype; /* Memory type for iteration */
unsigned vers; /* message version */
const uint8_t *p_end = p + p_size;
void * ret_value = NULL; /* Return value */

FUNC_ENTER_STATIC
Expand Down Expand Up @@ -136,8 +137,12 @@ H5O__fsinfo_decode(H5F_t *f, H5O_t H5_ATTR_UNUSED *open_oh, unsigned H5_ATTR_UNU
fsinfo->threshold = threshold;
if (HADDR_UNDEF == (fsinfo->eoa_pre_fsm_fsalloc = H5F_get_eoa(f, H5FD_MEM_DEFAULT)))
HGOTO_ERROR(H5E_FILE, H5E_CANTGET, NULL, "unable to get file size")
for (type = H5FD_MEM_SUPER; type < H5FD_MEM_NTYPES; type++)
for (type = H5FD_MEM_SUPER; type < H5FD_MEM_NTYPES; type++) {
if (p + H5_SIZEOF_HADDR_T > p_end)
HGOTO_ERROR(H5E_FILE, H5E_CANTDECODE, NULL,
"ran off end of input buffer while decoding")
H5F_addr_decode(f, &p, &(fsinfo->fs_addr[type - 1]));
}
break;

case H5F_FILE_SPACE_ALL:
Expand Down
Binary file added test/cve_2020_10810.h5
Binary file not shown.
56 changes: 56 additions & 0 deletions test/ohdr.c
Expand Up @@ -456,6 +456,59 @@ test_ohdr_swmr(hbool_t new_format)
return FAIL;
} /* test_ohdr_swmr() */

/*
* Tests bad object header messages.
*
* Currently tests for CVE-2020-10810 fixes but can be expanded to handle
* other CVE badness.
*/

/* This is a generated file that can be obtained from:
*
* https://nvd.nist.gov/vuln/detail/CVE-2020-10810
*
* It was formerly named H5AC_unpin_entry_POC
*/
#define CVE_2020_10810_FILENAME "cve_2020_10810.h5"

static herr_t
test_ohdr_badness(hid_t fapl)
{
hid_t fid = H5I_INVALID_HID;

/* CVE-2020-10810 involved a malformed fsinfo message
* This test ensures the fundamental problem is fixed. Running it under
* valgrind et al. will ensure that the memory leaks and invalid access
* are fixed.
*/
TESTING("Fix for CVE-2020-10810");

H5E_BEGIN_TRY
{
/* This should fail due to the malformed fsinfo message. It should
* fail gracefully and not segfault.
*/
fid = H5Fopen(CVE_2020_10810_FILENAME, H5F_ACC_RDWR, fapl);
}
H5E_END_TRY;

if (fid >= 0)
FAIL_PUTS_ERROR("should not have been able to open malformed file");

PASSED();

return SUCCEED;

error:
H5E_BEGIN_TRY
{
H5Fclose(fid);
}
H5E_END_TRY;

return FAIL;
}

/*
* To test objects with unknown messages in a file with:
* a) H5O_BOGUS_VALID_ID:
Expand Down Expand Up @@ -2047,6 +2100,9 @@ main(void)
} /* high */
} /* low */

/* Verify bad ohdr message fixes work */
test_ohdr_badness(fapl);

/* Verify symbol table messages are cached */
if (h5_verify_cached_stabs(FILENAME, fapl) < 0)
TEST_ERROR
Expand Down

0 comments on commit b5c6652

Please sign in to comment.