Skip to content

Commit

Permalink
Fix for github issue #3790: infinite loop closing library (#4445)
Browse files Browse the repository at this point in the history
* Fix for github issue #3790: infinite loop closing library
Cause of the problem:
When h5dump tries to open the user provided test file, the metadata cache will
call the "get_final_load_size" callback to find out the actual size of the
the root object header.  The callback function will call
H5O__prefix_deserialize() to allocate space for the object header
data structure (via H5FL_CALLOC) and to deserialize the object header prefix
in order to find the actual size of the object header.
The metadata cache will then check whether the actual size obtained
will exceed the file's EOA.
Since the actual size obtained from the test file exceeds the EOA,
the metadata cache throws an error and return.
However, the oh structure that was allocated in H5O__prefix_deserialize()
was not freed and hence causing the problem described in this issue.
Fix:
1) Deallocate the oh structure after obtaining and saving the needed
information in udata which will be used later on in the "verify_chksum" callback.
2) Deserialize the object header prefix in the "object header's
"deserialize" callback regardless.  The original coding intends to keep the
deserialized prefix so that the object header's "deserialize" callback
does not need to deserialize the prefix again if the object header is coming
through the "get_final_load_size" callback.
  • Loading branch information
vchoi-hdfgroup authored Apr 30, 2024
1 parent 667b607 commit b23affc
Show file tree
Hide file tree
Showing 9 changed files with 48 additions and 47 deletions.
14 changes: 14 additions & 0 deletions release_docs/RELEASE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,20 @@ Bug Fixes since HDF5-1.14.0 release

Library
-------
- Fixed infinite loop closing library issue when h5dump with a user provided test file

The library's metadata cache calls the "get_final_load_size" client callback
to find out the actual size of the object header. As the size obtained
exceeds the file's EOA, it throws an error but the object header structure
allocated through the client callback is not freed hence causing the
issue described.

(1) Free the structure allocated in the object header client callback after
saving the needed information in udata. (2) Deserialize the object header
prefix in the object header's "deserialize" callback regardless.

Fixes GitHub #3790

- Fixed many (future) CVE issues

A partner organization corrected many potential security issues, which
Expand Down
64 changes: 19 additions & 45 deletions src/H5Ocache.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ H5O__cache_get_final_load_size(const void *image, size_t image_len, void *_udata
/* Set the final size for the cache image */
*actual_len = udata->chunk0_size + (size_t)H5O_SIZEOF_HDR(udata->oh);

/* Save the oh version to be used later in verify_chksum callback
because oh will be freed before leaving this routine */
udata->oh_version = udata->oh->version;

/* Free allocated memory: fix github issue #3970 */
if (H5O__free(udata->oh, false) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTRELEASE, FAIL, "can't destroy object header");
udata->oh = NULL;

done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5O__cache_get_final_load_size() */
Expand All @@ -215,27 +224,17 @@ H5O__cache_verify_chksum(const void *_image, size_t len, void *_udata)

assert(image);
assert(udata);
assert(udata->oh);

/* There is no checksum for version 1 */
if (udata->oh->version != H5O_VERSION_1) {
if (udata->oh_version != H5O_VERSION_1) {
uint32_t stored_chksum; /* Stored metadata checksum value */
uint32_t computed_chksum; /* Computed metadata checksum value */

/* Get stored and computed checksums */
H5F_get_checksums(image, len, &stored_chksum, &computed_chksum);

if (stored_chksum != computed_chksum) {
/* These fields are not deserialized yet in H5O__prefix_deserialize() */
assert(udata->oh->chunk == NULL);
assert(udata->oh->mesg == NULL);
assert(udata->oh->proxy == NULL);

/* Indicate that udata->oh is to be freed later
in H5O__prefix_deserialize() */
udata->free_oh = true;
ret_value = false;
}
if (stored_chksum != computed_chksum)
ret_value = false;
}
else
assert(!(udata->common.file_intent & H5F_ACC_SWMR_WRITE));
Expand Down Expand Up @@ -275,21 +274,12 @@ H5O__cache_deserialize(const void *image, size_t len, void *_udata, bool *dirty)
assert(udata->common.f);
assert(udata->common.cont_msg_info);
assert(dirty);
assert(udata->oh == NULL);

/* Check for partially deserialized object header
*
* The Object header prefix will be deserialized if the object header came
* through the 'get_final_load_size' callback and not deserialized if
* the object header is coming from a cache image.
*/
if (NULL == udata->oh) {
/* Deserialize the object header prefix */
if (H5O__prefix_deserialize((const uint8_t *)image, len, udata) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTDECODE, NULL, "can't deserialize object header prefix");
assert(udata->oh);
}
if (H5O__prefix_deserialize((const uint8_t *)image, len, udata) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTDECODE, NULL, "can't deserialize object header prefix");
assert(udata->oh);

/* Retrieve partially deserialized object header from user data */
oh = udata->oh;

/* Set SWMR flag, if appropriate */
Expand Down Expand Up @@ -1144,25 +1134,9 @@ H5O__prefix_deserialize(const uint8_t *_image, size_t len, H5O_cache_ud_t *udata
if ((size_t)(image - _image) != (size_t)(H5O_SIZEOF_HDR(oh) - H5O_SIZEOF_CHKSUM_OH(oh)))
HGOTO_ERROR(H5E_OHDR, H5E_BADVALUE, FAIL, "bad object header prefix length");

/* If udata->oh is to be freed (see H5O__cache_verify_chksum),
* save the pointer to udata->oh and free it later after setting
* udata->oh with the new object header
*/
if (udata->free_oh) {
H5O_t *saved_oh = udata->oh;
assert(udata->oh);

/* Save the object header for later use in 'deserialize' callback */
udata->oh = oh;
if (H5O__free(saved_oh, false) < 0)
HGOTO_ERROR(H5E_OHDR, H5E_CANTRELEASE, FAIL, "can't destroy object header");
udata->free_oh = false;
}
else
/* Save the object header for later use in 'deserialize' callback */
udata->oh = oh;

oh = NULL;
/* Save the object header for later use in 'deserialize' callback */
udata->oh = oh;
oh = NULL;

done:
/* Release the [possibly partially initialized] object header on errors */
Expand Down
2 changes: 1 addition & 1 deletion src/H5Oint.c
Original file line number Diff line number Diff line change
Expand Up @@ -976,7 +976,7 @@ H5O_protect(const H5O_loc_t *loc, unsigned prot_flags, bool pin_all_chunks)
udata.v1_pfx_nmesgs = 0;
udata.chunk0_size = 0;
udata.oh = NULL;
udata.free_oh = false;
udata.oh_version = 0;
udata.common.f = loc->file;
udata.common.file_intent = file_intent;
udata.common.merged_null_msgs = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/H5Opkg.h
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ typedef struct H5O_cache_ud_t {
unsigned v1_pfx_nmesgs; /* Number of messages from v1 prefix header */
size_t chunk0_size; /* Size of serialized first chunk */
H5O_t *oh; /* Partially deserialized object header, for later use */
bool free_oh; /* Whether to free the object header or not */
uint8_t oh_version; /* Oh version number */
H5O_common_cache_ud_t common; /* Common object header cache callback info */
} H5O_cache_ud_t;

Expand Down
6 changes: 6 additions & 0 deletions tools/test/h5dump/CMakeTests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
file_space.ddl
filter_fail.ddl
non_existing.ddl
infinite_loop.ddl
packedbits.ddl
tall-1.ddl
tall-2.ddl
Expand Down Expand Up @@ -298,6 +299,7 @@
tfpformat.h5
tfvalues.h5
tgroup.h5
3790_infinite_loop.h5
tgrp_comments.h5
tgrpnullspace.h5
thlink.h5
Expand Down Expand Up @@ -371,6 +373,7 @@
set (HDF5_ERROR_REFERENCE_TEST_FILES
filter_fail.err
non_existing.err
infinite_loop.err
tall-1.err
tall-2A.err
tall-2A0.err
Expand Down Expand Up @@ -1423,6 +1426,9 @@
# test for non-existing file
ADD_H5_TEST (non_existing 1 --enable-error-stack tgroup.h5 non_existing.h5)

# test to verify github issue#3790: infinite loop closing library
ADD_H5_TEST (infinite_loop 1 3790_infinite_loop.h5)

# test to verify HDFFV-10333: error similar to H5O_attr_decode in the jira issue
ADD_H5_TEST (err_attr_dspace 1 err_attr_dspace.h5)

Expand Down
1 change: 1 addition & 0 deletions tools/test/h5dump/errfiles/infinite_loop.err
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
h5dump error: unable to open file "3790_infinite_loop.h5"
Empty file.
Binary file added tools/test/h5dump/testfiles/3790_infinite_loop.h5
Binary file not shown.
6 changes: 6 additions & 0 deletions tools/test/h5dump/testh5dump.sh.in
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ $SRC_H5DUMP_TESTFILES/tfloat16_be.h5
$SRC_H5DUMP_TESTFILES/tfpformat.h5
$SRC_H5DUMP_TESTFILES/tfvalues.h5
$SRC_H5DUMP_TESTFILES/tgroup.h5
$SRC_H5DUMP_TESTFILES/3790_infinite_loop.h5
$SRC_H5DUMP_TESTFILES/tgrp_comments.h5
$SRC_H5DUMP_TESTFILES/tgrpnullspace.h5
$SRC_H5DUMP_TESTFILES/thlink.h5
Expand Down Expand Up @@ -200,6 +201,7 @@ $SRC_H5DUMP_OUTFILES/charsets.ddl
$SRC_H5DUMP_OUTFILES/file_space.ddl
$SRC_H5DUMP_OUTFILES/filter_fail.ddl
$SRC_H5DUMP_OUTFILES/non_existing.ddl
$SRC_H5DUMP_OUTFILES/infinite_loop.ddl
$SRC_H5DUMP_OUTFILES/packedbits.ddl
$SRC_H5DUMP_OUTFILES/tall-1.ddl
$SRC_H5DUMP_OUTFILES/tall-2.ddl
Expand Down Expand Up @@ -395,6 +397,7 @@ $SRC_H5DUMP_EXPFILES/twithddlfile.exp
LIST_ERROR_TEST_FILES="
${SRC_H5DUMP_ERRORFILES}/filter_fail.err
${SRC_H5DUMP_ERRORFILES}/non_existing.err
${SRC_H5DUMP_ERRORFILES}/infinite_loop.err
${SRC_H5DUMP_ERRORFILES}/tall-1.err
${SRC_H5DUMP_ERRORFILES}/tall-2A.err
${SRC_H5DUMP_ERRORFILES}/tall-2A0.err
Expand Down Expand Up @@ -1561,6 +1564,9 @@ TOOLTEST2 tall-6.exp --enable-error-stack -y -o tall-6.txt -d /g1/g1.1/dset1.1.1
# test for non-existing file
TOOLTEST3 non_existing.ddl --enable-error-stack tgroup.h5 non_existing.h5

# test to verify github issue #3790: infinite loop closing library
TOOLTEST4 infinite_loop.ddl 3790_infinite_loop.h5

# test to verify HDFFV-10333: error similar to H5O_attr_decode in the jira issue
TOOLTEST err_attr_dspace.ddl err_attr_dspace.h5

Expand Down

0 comments on commit b23affc

Please sign in to comment.