From 9268b803b742f99c1f8793cae74f19e74976b065 Mon Sep 17 00:00:00 2001 From: bmribler <39579120+bmribler@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:07:23 -0500 Subject: [PATCH] Fixes buffer underflow (#6143) Fixes security issue by treating non-NULL buffer with size 0 as length-only query in get_name API functions. Behavior: Modify get_name API functions to treat (buffer != NULL, size == 0) as length-only queries, preventing undefined behavior. Fix applied to H5Aget_name, H5Aget_name_by_idx, H5Fget_name, H5Gget_objname_by_idx, H5Iget_name, H5Lget_name_by_idx, H5Rget_file_name, H5Rget_obj_name, H5Rget_attr_name, and 8 other functions. Tests: Update test/links.c, test/tattr.c, test/tfile.c, test/titerate.c, and test/trefer.c to verify new behavior with non-null buffer and size 0. Documentation: Update comments in H5A.c, H5F.c, H5Gdeprec.c, H5I.c, H5L.c, H5R.c, and H5Rdeprec.c to reflect new behavior.t]@users.noreply.github.com> --- release_docs/CHANGELOG.md | 5 ++++ src/H5A.c | 11 +++++++- src/H5F.c | 32 ++++++++++++++------- src/H5Gdeprec.c | 34 ++++++++++++++-------- src/H5I.c | 32 ++++++++++++++------- src/H5L.c | 4 +++ src/H5R.c | 59 ++++++++++++++++++++++++++++++++++++++- src/H5Rdeprec.c | 22 +++++++++++++-- test/links.c | 21 ++++++++++++-- test/tattr.c | 25 +++++++++++++++-- test/tfile.c | 16 ++++++++++- test/titerate.c | 16 ++++++++++- test/trefer.c | 43 +++++++++++++++++++++++----- 13 files changed, 271 insertions(+), 49 deletions(-) diff --git a/release_docs/CHANGELOG.md b/release_docs/CHANGELOG.md index 74ad9b50f2b..c7a8fffb638 100644 --- a/release_docs/CHANGELOG.md +++ b/release_docs/CHANGELOG.md @@ -112,6 +112,11 @@ We would like to thank the many HDF5 community members who contributed to this r ## Library +### Fixes potential security issues + + The get_name API functions allow passing NULL when querying the object name length. However, passing a non-NULL buffer with size == 0 will result in security vulnerability of invalid write. That was because the library wrote a null terminator to the buffer regardless of what the size of the buffer was as long as the buffer was non-NULL. + These functions are now fixed to treat (buffer != NULL, size == 0) as a length-only query to eliminate Valgrind error of invalid write. + ### Fixed a performance issue with chunked dataset I/O When dataset chunks are unable to be placed in the dataset chunk cache (for example, if a chunk diff --git a/src/H5A.c b/src/H5A.c index 0725489bc24..9d4862d327b 100644 --- a/src/H5A.c +++ b/src/H5A.c @@ -1221,7 +1221,8 @@ done: Up to 'buf_size'-1 characters are stored in 'buf' followed by a '\0' string terminator. If the name of the attribute is longer than 'buf_size'-1, the string terminator is stored in the last position of the buffer to - properly terminate the string. + properly terminate the string. If 'buf' is non-NULL but 'buf_size' is 0, + treat the call as length being queried. --------------------------------------------------------------------------*/ ssize_t H5Aget_name(hid_t attr_id, size_t buf_size, char *buf /*out*/) @@ -1233,6 +1234,10 @@ H5Aget_name(hid_t attr_id, size_t buf_size, char *buf /*out*/) FUNC_ENTER_API((-1)) + /* If buffer size is zero, treat as length query and do not write, even a '\0' */ + if (buf && buf_size == 0) + buf = NULL; + /* check arguments */ if (NULL == (vol_obj = H5VL_vol_object_verify(attr_id, H5I_ATTR))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, (-1), "not an attribute"); @@ -1295,6 +1300,10 @@ H5Aget_name_by_idx(hid_t loc_id, const char *obj_name, H5_index_t idx_type, H5_i FUNC_ENTER_API(FAIL) + /* If buffer size is zero, treat as length query and do not write, even a '\0' */ + if (name && size == 0) + name = NULL; + /* Check args */ if (H5I_ATTR == H5I_get_type(loc_id)) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, FAIL, "location is not valid for an attribute"); diff --git a/src/H5F.c b/src/H5F.c index a0a57d2947b..2450098b757 100644 --- a/src/H5F.c +++ b/src/H5F.c @@ -1972,19 +1972,27 @@ done: * Function: H5Fget_name * * Purpose: Gets the name of the file to which object OBJ_ID belongs. - * If 'name' is non-NULL then write up to 'size' bytes into that - * buffer and always return the length of the entry name. - * Otherwise `size' is ignored and the function does not store - * the name, just returning the number of characters required to - * store the name. If an error occurs then the buffer pointed to - * by 'name' (NULL or non-NULL) is unchanged and the function - * returns a negative value. + * + * Description: + * When 'name' is non-NULL: + * - if 'size' > 0: writes up to 'size' bytes into the buffer + * (including null terminator) and returns the actual length + * of the name (excluding null terminator). + * - if 'size' == 0: treats the call as length query, does not + * write anything to the buffer (not even a null terminator), and + * returns the actual length of the name (excluding null terminator). + * + * When 'name' is NULL: does not write anything regardless of 'size' + * and returns the actual length of the name (excluding null terminator). + * + * On error, the buffer is unchanged and the function returns + * a negative value. + * + * Return: Success: Length of the name (excluding null terminator) + * Failure: Negative * * Note: This routine returns the name that was used to open the file, * not the actual name after resolving symlinks, etc. - * - * Return: Success: The length of the file name - * Failure: -1 *------------------------------------------------------------------------- */ ssize_t @@ -1998,6 +2006,10 @@ H5Fget_name(hid_t obj_id, char *name /*out*/, size_t size) FUNC_ENTER_API((-1)) + /* If name size is zero, treat as length query and do not write, even a '\0' */ + if (name && size == 0) + name = NULL; + /* Check the type */ type = H5I_get_type(obj_id); if (H5I_FILE != type && H5I_GROUP != type && H5I_DATATYPE != type && H5I_DATASET != type && diff --git a/src/H5Gdeprec.c b/src/H5Gdeprec.c index b6364db46e4..b610ad473ce 100644 --- a/src/H5Gdeprec.c +++ b/src/H5Gdeprec.c @@ -1145,20 +1145,26 @@ done: * Function: H5Gget_objname_by_idx * * Purpose: Returns the name of objects in the group by giving index. - * If `name' is non-NULL then write up to `size' bytes into that - * buffer and always return the length of the entry name. - * Otherwise `size' is ignored and the function does not store the name, - * just returning the number of characters required to store the name. - * If an error occurs then the buffer pointed to by `name' (NULL or non-NULL) - * is unchanged and the function returns a negative value. - * If a zero is returned for the name's length, then there is no name - * associated with the ID. + * + * Description: + * When 'name' is non-NULL: + * - if 'size' > 0: writes up to 'size' bytes into the buffer + * (including null terminator) and returns the actual length + * of the name (excluding null terminator). + * - if 'size' == 0: treats the call as length query, does not + * write anything to the buffer (not even a null terminator), and + * returns the actual length of the name (excluding null terminator). + * + * When 'name' is NULL: does not write anything regardless of 'size' + * and returns the actual length of the name (excluding null terminator). + * + * On error, the buffer is unchanged and the function returns + * a negative value. + * + * Return: Success: Length of the name (excluding null terminator) + * Failure: Negative * * Note: Deprecated in favor of H5Lget_name_by_idx - * - * Return: Success: Non-negative - * Failure: Negative - * *------------------------------------------------------------------------- */ ssize_t @@ -1172,6 +1178,10 @@ H5Gget_objname_by_idx(hid_t loc_id, hsize_t idx, char *name /*out*/, size_t size FUNC_ENTER_API(-1) + /* If name size is zero, treat as length query and do not write, even a '\0' */ + if (name && size == 0) + name = NULL; + /* Set up collective metadata if appropriate */ if (H5CX_set_loc(loc_id) < 0) HGOTO_ERROR(H5E_SYM, H5E_CANTSET, (-1), "can't set collective metadata read info"); diff --git a/src/H5I.c b/src/H5I.c index b71a9772b57..4b8fc4f0231 100644 --- a/src/H5I.c +++ b/src/H5I.c @@ -824,19 +824,27 @@ done: * * Purpose: Gets a name of an object from its ID. * - * Return: Success: The length of the name + * Description: + * When 'name' is non-NULL: + * - if 'size' > 0: writes up to 'size' bytes into the buffer + * (including null terminator) and returns the actual length + * of the name (excluding null terminator). + * - if 'size' == 0: treats the call as length query, does not + * write anything to the buffer (not even a null terminator), and + * returns the actual length of the name (excluding null terminator). * - * Failure: -1 + * When 'name' is NULL: does not write anything regardless of 'size' + * and returns the actual length of the name (excluding null terminator). + * + * On error, the buffer is unchanged and the function returns + * a negative value. + * + * Return: Success: The length of the name (excluding null terminator) + * Failure: Negative * * Notes: - * If 'name' is non-NULL then write up to 'size' bytes into that - * buffer and always return the length of the entry name. - * Otherwise 'size' is ignored and the function does not store the name, - * just returning the number of characters required to store the name. - * If an error occurs then the buffer pointed to by 'name' (NULL or non-NULL) - * is unchanged and the function returns a negative value. - * If a zero is returned for the name's length, then there is no name - * associated with the ID. + * If a zero is returned for the name's length, then there is no name + * associated with the ID. * *------------------------------------------------------------------------- */ @@ -851,6 +859,10 @@ H5Iget_name(hid_t id, char *name /*out*/, size_t size) FUNC_ENTER_API((-1)) + /* If name size is zero, treat as length query and do not write, even a '\0' */ + if (name && size == 0) + name = NULL; + /* Get the object pointer */ if (NULL == (vol_obj = H5VL_vol_object(id))) HGOTO_ERROR(H5E_ID, H5E_BADTYPE, (-1), "invalid identifier"); diff --git a/src/H5L.c b/src/H5L.c index a818445c52a..ee603bfa801 100644 --- a/src/H5L.c +++ b/src/H5L.c @@ -1478,6 +1478,10 @@ H5Lget_name_by_idx(hid_t loc_id, const char *group_name, H5_index_t idx_type, H5 FUNC_ENTER_API((-1)) + /* If name size is zero, treat as length query and do not write, even a '\0' */ + if (name && size == 0) + name = NULL; + /* Check arguments */ if (!group_name || !*group_name) HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, (-1), "no name specified"); diff --git a/src/H5R.c b/src/H5R.c index 2b8dcdbf2f3..8851cb33f8e 100644 --- a/src/H5R.c +++ b/src/H5R.c @@ -1013,8 +1013,23 @@ done: * Purpose: Given a reference to some object, determine a file name of the * object located into. * - * Return: Non-negative length of the path on success / -1 on failure + * Description: + * When 'buf' is non-NULL: + * - if 'size' > 0: writes up to 'size' bytes into the buffer + * (including null terminator) and returns the actual length + * of the name (excluding null terminator). + * - if 'size' == 0: treats the call as length query, does not + * write anything to the buffer (not even a null terminator), and + * returns the actual length of the name (excluding null terminator). * + * When 'buf' is NULL: does not write anything regardless of 'size' + * and returns the actual length of the name (excluding null terminator). + * + * On error, the buffer is unchanged and the function returns + * a negative value. + * + * Return: Success: The length of the name (excluding null terminator) + * Failure: Negative *------------------------------------------------------------------------- */ ssize_t @@ -1032,6 +1047,10 @@ H5Rget_file_name(const H5R_ref_t *ref_ptr, char *buf /*out*/, size_t size) H5R__get_type((const H5R_ref_priv_t *)ref_ptr) >= H5R_MAXTYPE) HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, (-1), "invalid reference type"); + /* If buffer size is zero, treat as length query and do not write, even a '\0' */ + if (buf && size == 0) + buf = NULL; + /* Get name */ if (H5I_INVALID_HID == (loc_id = H5R__get_loc_id((const H5R_ref_priv_t *)ref_ptr))) { /* Un-opened external references do not have loc_id set but hold a @@ -1073,6 +1092,21 @@ done: * Purpose: Given a reference to some object, determine a path to the * object referenced in the file. * + * Description: + * When 'buf' is non-NULL: + * - if 'size' > 0: writes up to 'size' bytes into the buffer + * (including null terminator) and returns the actual length + * of the name (excluding null terminator). + * - if 'size' == 0: treats the call as length query, does not + * write anything to the buffer (not even a null terminator), and + * returns the actual length of the name (excluding null terminator). + * + * When 'buf' is NULL: does not write anything regardless of 'size' + * and returns the actual length of the name (excluding null terminator). + * + * On error, the buffer is unchanged and the function returns + * a negative value. + * * Return: Non-negative length of the path on success / -1 on failure * *------------------------------------------------------------------------- @@ -1099,6 +1133,10 @@ H5Rget_obj_name(H5R_ref_t *ref_ptr, hid_t rapl_id, char *buf /*out*/, size_t siz if (rapl_id < 0) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, (-1), "not a property list"); + /* If buffer size is zero, treat as length query and do not write, even a '\0' */ + if (buf && size == 0) + buf = NULL; + /* Retrieve loc_id from reference */ if (H5I_INVALID_HID == (loc_id = H5R__get_loc_id((const H5R_ref_priv_t *)ref_ptr))) /* Attempt to re-open file and pass rapl_id as a fapl_id */ @@ -1140,6 +1178,21 @@ done: * * Purpose: Given a reference to some attribute, determine its name. * + * Description: + * When 'buf' is non-NULL: + * - if 'size' > 0: writes up to 'size' bytes into the buffer + * (including null terminator) and returns the actual length + * of the name (excluding null terminator). + * - if 'size' == 0: treats the call as length query, does not + * write anything to the buffer (not even a null terminator), and + * returns the actual length of the name (excluding null terminator). + * + * When 'buf' is NULL: does not write anything regardless of 'size' + * and returns the actual length of the name (excluding null terminator). + * + * On error, the buffer is unchanged and the function returns + * a negative value. + * * Return: Non-negative length of the path on success / -1 on failure * *------------------------------------------------------------------------- @@ -1157,6 +1210,10 @@ H5Rget_attr_name(const H5R_ref_t *ref_ptr, char *buf /*out*/, size_t size) if (H5R__get_type((const H5R_ref_priv_t *)ref_ptr) != H5R_ATTR) HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, (-1), "invalid reference type"); + /* If buffer size is zero, treat as length query and do not write, even a '\0' */ + if (buf && size == 0) + buf = NULL; + /* Get attribute name */ if ((ret_value = H5R__get_attr_name((const H5R_ref_priv_t *)ref_ptr, buf, size)) < 0) HGOTO_ERROR(H5E_REFERENCE, H5E_CANTGET, (-1), "unable to determine attribute name"); diff --git a/src/H5Rdeprec.c b/src/H5Rdeprec.c index 6ca014e16a7..4b5437d4f5f 100644 --- a/src/H5Rdeprec.c +++ b/src/H5Rdeprec.c @@ -741,9 +741,23 @@ done: * Purpose: Given a reference to some object, determine a path to the * object referenced in the file. * - * Return: Success: Non-negative length of the path - * Failure: -1 + * Description: + * When 'name' is non-NULL: + * - if 'size' > 0: writes up to 'size' bytes into the buffer + * (including null terminator) and returns the actual length + * of the name (excluding null terminator). + * - if 'size' == 0: treats the call as length query, does not + * write anything to the buffer (not even a null terminator), and + * returns the actual length of the name (excluding null terminator). * + * When 'name' is NULL: does not write anything regardless of 'size' + * and returns the actual length of the path (excluding null terminator). + * + * On error, the buffer is unchanged and the function returns + * a negative value. + * + * Return: Success: The length of the path (excluding null terminator) + * Failure: Negative *------------------------------------------------------------------------- */ ssize_t @@ -766,6 +780,10 @@ H5Rget_name(hid_t id, H5R_type_t ref_type, const void *ref, char *name /*out*/, if (ref_type != H5R_OBJECT1 && ref_type != H5R_DATASET_REGION1) HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, (-1), "invalid reference type"); + /* If buffer size is zero, treat as length query and do not write, even a '\0' */ + if (name && size == 0) + name = NULL; + /* Get the VOL object */ if (NULL == (vol_obj = H5VL_vol_object(id))) HGOTO_ERROR(H5E_ARGS, H5E_BADTYPE, (-1), "invalid file identifier"); diff --git a/test/links.c b/test/links.c index 262cd0f2e50..091cdaf84ae 100644 --- a/test/links.c +++ b/test/links.c @@ -154,6 +154,9 @@ static const char *FILENAME[] = {"links0", #define TIMESTAMP_GROUP_1 "timestamp1" #define TIMESTAMP_GROUP_2 "timestamp2" +/* Used by test_deprec() */ +#define NON_NULL_BUF "NON_NULL_BUF" + /* Link iteration struct */ typedef struct { H5_iter_order_t order; /* Direction of iteration */ @@ -1990,8 +1993,11 @@ test_deprec(hid_t fapl, bool new_format) hsize_t num_objs; /* Number of objects in a group */ char filename[1024]; char tmpstr[1024]; - int len = 0; /* Length of comment */ - herr_t status; /* Generic return value */ + int len = 0; /* Length of comment */ + char non_null_buf[80]; /* Buffer to test non-null buffer calls */ + char *buf_ptr; /* To pass mid-string */ + ssize_t name_len; /* Length of name */ + herr_t status; /* Generic return value */ if (new_format) TESTING("backwards compatibility (w/new group format)"); @@ -2052,6 +2058,17 @@ test_deprec(hid_t fapl, bool new_format) if (len >= 0) TEST_ERROR; + /* Verify that passing a non-null buffer with size 0 still returns the correct name + size and the buffer is not modified */ + strcpy(non_null_buf, NON_NULL_BUF); + buf_ptr = &non_null_buf[4]; + if ((name_len = H5Gget_objname_by_idx(group1_id, (hsize_t)0, buf_ptr, 0)) < 0) + FAIL_STACK_ERROR; + if ((size_t)name_len != strlen(tmpstr)) + TEST_ERROR; + if ((strcmp(non_null_buf, NON_NULL_BUF) != 0)) + TEST_ERROR; + /* Test getting the type for objects */ if ((obj_type = H5Gget_objtype_by_idx(group1_id, (hsize_t)0)) < 0) FAIL_STACK_ERROR; diff --git a/test/tattr.c b/test/tattr.c index 780f11591cf..af08d2db0a2 100644 --- a/test/tattr.c +++ b/test/tattr.c @@ -148,6 +148,7 @@ static float attr_data5 = -5.123F; /* Test data for 5th attribute */ /* Used by test_attr_info_null_info_pointer() */ #define GET_INFO_NULL_POINTER_ATTR_NAME "NullInfoPointerAttr" +#define NON_NULL_BUF "NON_NULL_BUF" /* Used by test_attr_rename_invalid_name() */ #define INVALID_RENAME_TEST_ATTR_NAME "InvalidRenameTestAttr" @@ -6526,10 +6527,13 @@ test_attr_rename_invalid_name(hid_t fcpl, hid_t fapl) /*************************************************************** ** -** test_attr_get_name_invalid_buf(): A test to ensure that -** passing a NULL buffer to H5Aget_name(_by_idx) when +** test_attr_get_name_invalid_buf(): A test to ensure that: +** - passing a NULL buffer to H5Aget_name(_by_idx) when ** the 'size' parameter is non-zero doesn't cause bad ** behavior. +** - passing a non-NULL buffer to H5Aget_name(_by_idx) +** when the 'size' parameter is zero treats as a length +** query call. ** ****************************************************************/ static void @@ -6539,6 +6543,9 @@ test_attr_get_name_invalid_buf(hid_t fcpl, hid_t fapl) hid_t fid; hid_t attr; hid_t sid; + char non_null_buf[80]; /* Buffer to test non-null buffer calls */ + char *buf_ptr; /* To pass mid-string */ + ssize_t namelen; /* Length of attribute name */ /* Create dataspace for attribute */ sid = H5Screate(H5S_SCALAR); @@ -6569,6 +6576,20 @@ test_attr_get_name_invalid_buf(hid_t fcpl, hid_t fapl) VERIFY(err_ret, FAIL, "H5Aget_name_by_idx"); + /* Verify that passing a non-null buffer with size 0 still returns the correct name + size and the buffer is not modified */ + strcpy(non_null_buf, NON_NULL_BUF); + buf_ptr = &non_null_buf[4]; + namelen = H5Aget_name(attr, (size_t)0, buf_ptr); + CHECK(namelen, FAIL, "H5Aget_name"); + VERIFY(namelen, (ssize_t)strlen(GET_NAME_INVALID_BUF_TEST_ATTR_NAME), "H5Aget_name"); + VERIFY(strcmp(non_null_buf, NON_NULL_BUF), 0, "H5Aget_name"); + + namelen = H5Aget_name_by_idx(fid, ".", H5_INDEX_CRT_ORDER, H5_ITER_INC, 0, buf_ptr, 0, H5P_DEFAULT); + CHECK(namelen, FAIL, "H5Aget_name_by_idx"); + VERIFY(namelen, (ssize_t)strlen(GET_NAME_INVALID_BUF_TEST_ATTR_NAME), "H5Aget_name_by_idx"); + VERIFY(strcmp(non_null_buf, NON_NULL_BUF), 0, "H5Aget_name_by_idx"); + /* Close dataspace */ err_ret = H5Sclose(sid); CHECK(err_ret, FAIL, "H5Sclose"); diff --git a/test/tfile.c b/test/tfile.c index 7f7e293b921..32ea586cd96 100644 --- a/test/tfile.c +++ b/test/tfile.c @@ -147,6 +147,9 @@ /* Declaration for test_incr_filesize() */ #define FILE_INCR_FILESIZE "tfile_incr_filesize" +/* Used by test_file_getname() */ +#define NON_NULL_BUF "NON_NULL_BUF" + /* Files created under 1.6 branch and 1.8 branch--used in test_filespace_compatible() */ static const char *OLD_FILENAME[] = { "filespace_1_6.h5", /* 1.6 HDF5 file */ @@ -2411,7 +2414,9 @@ test_file_getname(void) hsize_t dims[TESTA_RANK] = {TESTA_NX, TESTA_NY}; char name[TESTA_NAME_BUF_SIZE]; ssize_t name_len; - herr_t ret; /* Generic return value */ + char non_null_buf[80]; /* Buffer to test non-null buffer calls */ + char *buf_ptr; /* To pass mid-string */ + herr_t ret; /* Generic return value */ /* Output message about test being performed */ MESSAGE(5, ("Testing H5Fget_name() functionality\n")); @@ -2426,6 +2431,15 @@ test_file_getname(void) VERIFY_STR(name, FILE1, "H5Fget_name"); VERIFY(name_len, strlen(FILE1), "H5Fget_name"); + /* Verify that passing a non-null buffer with size 0 still returns the correct name + size and the buffer is not modified */ + strcpy(non_null_buf, NON_NULL_BUF); + buf_ptr = &non_null_buf[4]; + name_len = H5Fget_name(file_id, buf_ptr, 0); + CHECK(name_len, FAIL, "H5Fget_name"); + VERIFY(name_len, strlen(FILE1), "H5Fget_name"); + VERIFY(strcmp(non_null_buf, NON_NULL_BUF), 0, "H5Fget_name"); + /* Create a group in the root group */ group_id = H5Gcreate2(file_id, TESTA_GROUPNAME, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); CHECK(group_id, FAIL, "H5Gcreate2"); diff --git a/test/titerate.c b/test/titerate.c index 7efae347247..595a577d8f6 100644 --- a/test/titerate.c +++ b/test/titerate.c @@ -39,6 +39,9 @@ #define SPACE1_RANK 1 #define SPACE1_DIM1 4 +/* Used by test_reference_obj() and test_reference_attr() */ +#define NON_NULL_BUF "NON_NULL_BUF" + typedef enum { RET_ZERO, RET_TWO, RET_CHANGE, RET_CHANGE2 } iter_enum; /* Custom group iteration callback data */ @@ -894,7 +897,9 @@ test_grp_memb_funcs(hid_t fapl) VERIFY(ginfo.nlinks, (NDATASETS + 2), "H5Gget_info"); for (i = 0; i < (int)ginfo.nlinks; i++) { - H5O_info2_t oinfo; /* Object info */ + H5O_info2_t oinfo; /* Object info */ + char non_null_buf[80]; /* Buffer to test non-null buffer calls */ + char *buf_ptr; /* To pass mid-string */ /* Test with NULL for name, to query length */ name_len = H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, NULL, @@ -908,6 +913,15 @@ test_grp_memb_funcs(hid_t fapl) /* Double-check that the length is the same */ VERIFY(ret, name_len, "H5Lget_name_by_idx"); + /* Test with non-null buffer for name and 0 for size */ + strcpy(non_null_buf, NON_NULL_BUF); + buf_ptr = &non_null_buf[4]; + ret = (herr_t)H5Lget_name_by_idx(root_group, ".", H5_INDEX_NAME, H5_ITER_INC, (hsize_t)i, buf_ptr, 0, + H5P_DEFAULT); + CHECK(ret, FAIL, "H5Lget_name_by_idx"); + VERIFY(ret, name_len, "H5Lget_name_by_idx"); + VERIFY(strcmp(non_null_buf, NON_NULL_BUF), 0, "H5Lget_name_by_idx"); + /* Keep a copy of the dataset names around for later */ obj_names[i] = strdup(dataset_name); CHECK_PTR(obj_names[i], "strdup"); diff --git a/test/trefer.c b/test/trefer.c index cd6bf2ca1b9..4df923fd8a6 100644 --- a/test/trefer.c +++ b/test/trefer.c @@ -88,6 +88,9 @@ typedef struct s2_t { #define MAX_ITER_WRITE MAX_ITER_CREATE #define MAX_ITER_READ MAX_ITER_CREATE +/* Used by test_reference_obj() and test_reference_attr() */ +#define NON_NULL_BUF "NON_NULL_BUF" + /**************************************************************** ** ** test_reference_params(): Test basic H5R (reference) parameters @@ -458,11 +461,13 @@ test_reference_obj(void) *rbuf; /* buffer read from disk */ H5R_ref_t *wbuf_cp; /* copy buffer */ unsigned *ibuf, *obuf; - unsigned i, j; /* Counters */ - ssize_t namelen; /* String buffer size return value */ - char *namebuf; /* Buffer for attribute's or dataset's name */ - H5O_type_t obj_type; /* Object type */ - herr_t ret; /* Generic return value */ + unsigned i, j; /* Counters */ + ssize_t namelen; /* String buffer size return value */ + char *namebuf; /* Buffer for attribute's or dataset's name */ + H5O_type_t obj_type; /* Object type */ + char non_null_buf[80]; /* Buffer to test non-null buffer calls */ + char *buf_ptr; /* To pass mid-string */ + herr_t ret; /* Generic return value */ /* Output message about test being performed */ MESSAGE(5, ("Testing Object Reference Functions\n")); @@ -626,17 +631,31 @@ test_reference_obj(void) CHECK(namelen, FAIL, "H5Rget_file_name"); VERIFY(namelen, strlen(FILE_REF_OBJ), "H5Rget_file_name"); + /* Test passing in non-null buffer with buffer size is zero */ + strcpy(non_null_buf, NON_NULL_BUF); + buf_ptr = &non_null_buf[4]; + namelen = H5Rget_file_name(&rbuf[0], buf_ptr, 0); + CHECK(namelen, FAIL, "H5Rget_file_name"); + VERIFY(namelen, strlen(FILE_REF_OBJ), "H5Rget_file_name"); + VERIFY(strcmp(non_null_buf, NON_NULL_BUF), 0, "H5Rget_file_name"); + /* Get the file name for the reference */ namebuf = (char *)malloc((size_t)namelen + 1); namelen = H5Rget_file_name(&rbuf[0], namebuf, (size_t)namelen + 1); CHECK(namelen, FAIL, "H5Rget_file_name"); VERIFY(strcmp(namebuf, FILE_REF_OBJ), 0, "namebuf vs FILE_REF_OBJ"); VERIFY(namelen, strlen(FILE_REF_OBJ), "H5Rget_file_name"); - free(namebuf); /* Testing Dataset1 */ + /* Test passing in non-null buffer with buffer size is zero */ + buf_ptr = &non_null_buf[8]; + namelen = H5Rget_obj_name(&rbuf[0], H5P_DEFAULT, buf_ptr, 0); + CHECK(namelen, FAIL, "H5Rget_obj_name"); + VERIFY(namelen, strlen(DS1_REF_OBJ), "H5Rget_obj_name"); + VERIFY(strcmp(non_null_buf, NON_NULL_BUF), 0, "H5Rget_obj_name"); + /* Getting the name of the referenced object and verify it */ namelen = H5Rget_obj_name(&rbuf[0], H5P_DEFAULT, NULL, 0); CHECK(namelen, FAIL, "H5Rget_obj_name"); @@ -2479,6 +2498,8 @@ test_reference_attr(void) char *attr_name = NULL; /* name of attribute, from H5A */ ssize_t attr_name_size; /* size of attribute name */ H5O_type_t obj_type; /* Object type */ + char non_null_buf[80]; /* Buffer to test non-null buffer calls */ + char *buf_ptr; /* To pass mid-string */ herr_t ret; /* Generic return value */ /* Output message about test being performed */ @@ -2649,10 +2670,18 @@ test_reference_attr(void) /* Testing "Attr1" */ + /* Test passing in non-null buffer with buffer size is zero */ + strcpy(non_null_buf, NON_NULL_BUF); + buf_ptr = &non_null_buf[4]; + namelen = H5Rget_attr_name(&ref_rbuf[0], buf_ptr, 0); + CHECK(namelen, FAIL, "H5Rget_attr_name"); + VERIFY(namelen, strlen(ATTR1_REF_OBJ), "H5Rget_attr_name"); + VERIFY(strcmp(non_null_buf, NON_NULL_BUF), 0, "H5Rget_attr_name"); + /* Getting the name of the referenced attribute and verify it */ namelen = H5Rget_attr_name(&ref_rbuf[0], NULL, 0); CHECK(namelen, FAIL, "H5Rget_attr_name"); - VERIFY(namelen, strlen(ATTR1_REF_OBJ), "H5Rget_obj_name"); + VERIFY(namelen, strlen(ATTR1_REF_OBJ), "H5Rget_attr_name"); namebuf = (char *)malloc((size_t)namelen + 1); namelen = H5Rget_attr_name(&ref_rbuf[0], namebuf, (size_t)namelen + 1);