diff --git a/hl/src/H5DS.c b/hl/src/H5DS.c index f40042607fb..c42c7544d44 100644 --- a/hl/src/H5DS.c +++ b/hl/src/H5DS.c @@ -2292,6 +2292,7 @@ H5DSis_scale(hid_t did) is_ds = 1; free(buf); + buf = NULL; if (H5Tclose(tid) < 0) goto out; diff --git a/hl/src/H5LT.c b/hl/src/H5LT.c index 9f525e8e982..f9676a776f9 100644 --- a/hl/src/H5LT.c +++ b/hl/src/H5LT.c @@ -1889,13 +1889,17 @@ H5LTtext_to_dtype(const char *text, H5LT_lang_t lang_type) input_len = strlen(text); myinput = strdup(text); + if (!myinput) + goto out; if ((type_id = H5LTyyparse()) < 0) { free(myinput); + myinput = NULL; goto out; } free(myinput); + myinput = NULL; input_len = 0; return type_id; @@ -1909,7 +1913,20 @@ H5LTtext_to_dtype(const char *text, H5LT_lang_t lang_type) * * Purpose: Expand the buffer and append a string to it. * - * Return: void + * Parameters: + * _no_user_buf: Operating mode flag + * - true: Library-managed buffer (can reallocate) + * - false: User-provided buffer (fixed size, no realloc) + * len: Pointer to current buffer size (updated if reallocated) + * buf: Buffer to append to (must not be NULL) + * str_to_add: String to append (NULL = no append, just ensure space) + * + * Preconditions: + * - buf must be a valid, allocated buffer (never NULL) + * - In library-managed mode: buf was allocated via calloc/malloc + * - In user-provided mode: buf points to user's pre-allocated buffer + * + * Return: Updated buffer pointer on success, NULL on failure * *------------------------------------------------------------------------- */ @@ -1918,12 +1935,17 @@ realloc_and_append(bool _no_user_buf, size_t *len, char *buf, const char *str_to { size_t size_str_to_add, size_str; + /* Precondition: buf must be allocated (verified in debug builds) */ + assert(buf != NULL && "Buffer must not be NULL"); + + /* Defensive runtime check for production safety (assertions disabled in release builds) */ + if (!buf) + goto out; + + /* Mode 1: Library-managed buffer - can reallocate as needed */ if (_no_user_buf) { char *tmp_realloc; - if (!buf) - goto out; - /* If the buffer isn't big enough, reallocate it. Otherwise, go to do strcat. */ if (str_to_add && ((ssize_t)(*len - (strlen(buf) + strlen(str_to_add) + 1)) < LIMIT)) { *len += ((strlen(buf) + strlen(str_to_add) + 1) / INCREMENT + 1) * INCREMENT; @@ -1941,6 +1963,8 @@ realloc_and_append(bool _no_user_buf, size_t *len, char *buf, const char *str_to else buf = tmp_realloc; } + /* Mode 2: User-provided buffer - fixed size, no reallocation + * (else case is implicit - no action needed) */ if (str_to_add) { /* find the size of the buffer to add */ @@ -2171,6 +2195,50 @@ H5LTdtype_to_text(hid_t dtype, char *str, H5LT_lang_t lang_type, size_t *len) return FAIL; } +/*------------------------------------------------------------------------- + * Function: H5LT_append_dtype_super_text + * + * Purpose: Helper function to get super type text and append it to dt_str. + * This encapsulates the common pattern of: allocate buffer, + * convert dtype to text, append to string, free buffer. + * + * Return: Success: updated dt_str pointer, Failure: NULL + * + *------------------------------------------------------------------------- + */ +static char * +H5LT_append_dtype_super_text(hid_t super, char *dt_str, H5LT_lang_t lang, size_t *slen, bool no_user_buf) +{ + size_t super_len; + char *stmp = NULL; + + /* Get required buffer size for super type text */ + if (H5LTdtype_to_text(super, NULL, lang, &super_len) < 0) + return NULL; + + /* Allocate buffer for super type text */ + stmp = (char *)calloc(super_len, sizeof(char)); + if (!stmp) + return NULL; + + /* Convert super type to text */ + if (H5LTdtype_to_text(super, stmp, lang, &super_len) < 0) { + free(stmp); + return NULL; + } + + /* Append super type text to dt_str */ + if (!(dt_str = realloc_and_append(no_user_buf, slen, dt_str, stmp))) { + free(stmp); + return NULL; + } + + /* Clean up */ + free(stmp); + + return dt_str; +} + /*------------------------------------------------------------------------- * Function: H5LT_dtype_to_text * @@ -2536,8 +2604,7 @@ H5LT_dtype_to_text(hid_t dtype, char *dt_str, H5LT_lang_t lang, size_t *slen, bo tag = H5Tget_tag(dtype); if (tag) { snprintf(tmp_str, TMP_LEN, "OPQ_TAG \"%s\";\n", tag); - if (tag) - H5free_memory(tag); + H5free_memory(tag); tag = NULL; } else @@ -2556,9 +2623,7 @@ H5LT_dtype_to_text(hid_t dtype, char *dt_str, H5LT_lang_t lang, size_t *slen, bo break; } case H5T_ENUM: { - hid_t super; - size_t super_len; - char *stmp = NULL; + hid_t super; /* Print lead-in */ snprintf(dt_str, *slen, "H5T_ENUM {\n"); @@ -2566,23 +2631,11 @@ H5LT_dtype_to_text(hid_t dtype, char *dt_str, H5LT_lang_t lang, size_t *slen, bo if (!(dt_str = indentation(indent + COL, dt_str, no_user_buf, slen))) goto out; + /* Get super type and append its text representation */ if ((super = H5Tget_super(dtype)) < 0) goto out; - if (H5LTdtype_to_text(super, NULL, lang, &super_len) < 0) - goto out; - stmp = (char *)calloc(super_len, sizeof(char)); - if (H5LTdtype_to_text(super, stmp, lang, &super_len) < 0) { - free(stmp); + if (!(dt_str = H5LT_append_dtype_super_text(super, dt_str, lang, slen, no_user_buf))) goto out; - } - if (!(dt_str = realloc_and_append(no_user_buf, slen, dt_str, stmp))) { - free(stmp); - goto out; - } - - if (stmp) - free(stmp); - stmp = NULL; snprintf(tmp_str, TMP_LEN, ";\n"); if (!(dt_str = realloc_and_append(no_user_buf, slen, dt_str, tmp_str))) @@ -2603,9 +2656,7 @@ H5LT_dtype_to_text(hid_t dtype, char *dt_str, H5LT_lang_t lang, size_t *slen, bo break; } case H5T_VLEN: { - hid_t super; - size_t super_len; - char *stmp = NULL; + hid_t super; /* Print lead-in */ snprintf(dt_str, *slen, "H5T_VLEN {\n"); @@ -2613,23 +2664,12 @@ H5LT_dtype_to_text(hid_t dtype, char *dt_str, H5LT_lang_t lang, size_t *slen, bo if (!(dt_str = indentation(indent + COL, dt_str, no_user_buf, slen))) goto out; + /* Get super type and append its text representation */ if ((super = H5Tget_super(dtype)) < 0) goto out; - if (H5LTdtype_to_text(super, NULL, lang, &super_len) < 0) - goto out; - stmp = (char *)calloc(super_len, sizeof(char)); - if (H5LTdtype_to_text(super, stmp, lang, &super_len) < 0) { - free(stmp); + if (!(dt_str = H5LT_append_dtype_super_text(super, dt_str, lang, slen, no_user_buf))) goto out; - } - if (!(dt_str = realloc_and_append(no_user_buf, slen, dt_str, stmp))) { - free(stmp); - goto out; - } - if (stmp) - free(stmp); - stmp = NULL; snprintf(tmp_str, TMP_LEN, "\n"); if (!(dt_str = realloc_and_append(no_user_buf, slen, dt_str, tmp_str))) goto out; @@ -2647,8 +2687,6 @@ H5LT_dtype_to_text(hid_t dtype, char *dt_str, H5LT_lang_t lang, size_t *slen, bo } case H5T_ARRAY: { hid_t super; - size_t super_len; - char *stmp = NULL; hsize_t dims[H5S_MAX_RANK]; int ndims; @@ -2674,22 +2712,12 @@ H5LT_dtype_to_text(hid_t dtype, char *dt_str, H5LT_lang_t lang, size_t *slen, bo if (!(dt_str = realloc_and_append(no_user_buf, slen, dt_str, tmp_str))) goto out; + /* Get super type and append its text representation */ if ((super = H5Tget_super(dtype)) < 0) goto out; - if (H5LTdtype_to_text(super, NULL, lang, &super_len) < 0) - goto out; - stmp = (char *)calloc(super_len, sizeof(char)); - if (H5LTdtype_to_text(super, stmp, lang, &super_len) < 0) { - free(stmp); + if (!(dt_str = H5LT_append_dtype_super_text(super, dt_str, lang, slen, no_user_buf))) goto out; - } - if (!(dt_str = realloc_and_append(no_user_buf, slen, dt_str, stmp))) { - free(stmp); - goto out; - } - if (stmp) - free(stmp); - stmp = NULL; + snprintf(tmp_str, TMP_LEN, "\n"); if (!(dt_str = realloc_and_append(no_user_buf, slen, dt_str, tmp_str))) goto out; @@ -2775,9 +2803,7 @@ H5LT_dtype_to_text(hid_t dtype, char *dt_str, H5LT_lang_t lang, size_t *slen, bo break; } case H5T_COMPLEX: { - hid_t super; - size_t super_len; - char *stmp = NULL; + hid_t super; /* Print lead-in */ snprintf(dt_str, *slen, "H5T_COMPLEX {\n"); @@ -2785,23 +2811,12 @@ H5LT_dtype_to_text(hid_t dtype, char *dt_str, H5LT_lang_t lang, size_t *slen, bo if (!(dt_str = indentation(indent + COL, dt_str, no_user_buf, slen))) goto out; + /* Get super type and append its text representation */ if ((super = H5Tget_super(dtype)) < 0) goto out; - if (H5LTdtype_to_text(super, NULL, lang, &super_len) < 0) - goto out; - stmp = (char *)calloc(super_len, sizeof(char)); - if (H5LTdtype_to_text(super, stmp, lang, &super_len) < 0) { - free(stmp); + if (!(dt_str = H5LT_append_dtype_super_text(super, dt_str, lang, slen, no_user_buf))) goto out; - } - if (!(dt_str = realloc_and_append(no_user_buf, slen, dt_str, stmp))) { - free(stmp); - goto out; - } - if (stmp) - free(stmp); - stmp = NULL; snprintf(tmp_str, TMP_LEN, "\n"); if (!(dt_str = realloc_and_append(no_user_buf, slen, dt_str, tmp_str))) goto out; diff --git a/hl/src/H5TB.c b/hl/src/H5TB.c index 0941946cc24..360338cd2ef 100644 --- a/hl/src/H5TB.c +++ b/hl/src/H5TB.c @@ -3021,11 +3021,29 @@ H5TBget_field_info(hid_t loc_id, const char *dset_name, char *field_names[], siz for (i = 0; i < nfields; i++) { /* get the member name */ if (field_names) { - char *member_name; + char *member_name; + size_t name_len; if (NULL == (member_name = H5Tget_member_name(tid, (unsigned)i))) goto out; - strcpy(field_names[i], member_name); + + name_len = strlen(member_name); + + /* Only enforce HLTB_MAX_FIELD_LEN limit when truncation is necessary. + * This preserves backward compatibility for typical field names while + * preventing unbounded writes for exceptionally long names. + * For names < HLTB_MAX_FIELD_LEN: copies exact length (backward compatible) + * For names >= HLTB_MAX_FIELD_LEN: truncates to HLTB_MAX_FIELD_LEN - 1 chars */ + if (name_len >= HLTB_MAX_FIELD_LEN) { + /* Name too long - truncate to fit HLTB_MAX_FIELD_LEN buffer */ + memcpy(field_names[i], member_name, HLTB_MAX_FIELD_LEN - 1); + field_names[i][HLTB_MAX_FIELD_LEN - 1] = '\0'; + } + else { + /* Name fits - copy entire string including null terminator */ + memcpy(field_names[i], member_name, name_len + 1); + } + H5free_memory(member_name); } /* end if */ diff --git a/hl/src/H5TBprivate.h b/hl/src/H5TBprivate.h index 11346f6d862..bedb936d638 100644 --- a/hl/src/H5TBprivate.h +++ b/hl/src/H5TBprivate.h @@ -19,9 +19,8 @@ /* public TB prototypes */ #include "H5TBpublic.h" -#define TABLE_CLASS "TABLE" -#define TABLE_VERSION "3.0" -#define HLTB_MAX_FIELD_LEN 255 +#define TABLE_CLASS "TABLE" +#define TABLE_VERSION "3.0" /*------------------------------------------------------------------------- * diff --git a/hl/src/H5TBpublic.h b/hl/src/H5TBpublic.h index 7879712b361..05973f44bc3 100644 --- a/hl/src/H5TBpublic.h +++ b/hl/src/H5TBpublic.h @@ -13,6 +13,20 @@ #ifndef H5TBpublic_H #define H5TBpublic_H +/** + * \brief Maximum length for table field names + * + * \details This constant defines the maximum field name length supported by + * H5TBget_field_info(). Field names exceeding #HLTB_MAX_FIELD_LEN - 1 + * characters will be truncated. Callers should allocate buffers sized + * appropriately for their expected field names; only names at or exceeding + * this length require buffers of #HLTB_MAX_FIELD_LEN bytes. Shorter names + * are copied exactly without padding to this length. + * + * \since 2.1.0 + */ +#define HLTB_MAX_FIELD_LEN 255 + #ifdef __cplusplus extern "C" { #endif @@ -457,7 +471,13 @@ H5HL_DLL herr_t H5TBget_table_info(hid_t loc_id, const char *dset_name, hsize_t * * \fg_loc_id * \param[in] dset_name The name of the dataset to read - * \param[out] field_names An array containing the names of the fields + * \param[out] field_names An array containing the names of the fields. + * Each element must be allocated by the caller with sufficient + * space to hold the field name. Field names are copied as-is if + * shorter than #HLTB_MAX_FIELD_LEN. Names longer than + * #HLTB_MAX_FIELD_LEN - 1 characters will be truncated and + * require buffers of at least #HLTB_MAX_FIELD_LEN bytes. + * Pass NULL to skip retrieving field names. * \param[out] field_sizes An array containing the size of the fields * \param[out] field_offsets An array containing the offsets of the fields * \param[out] type_size The size of the HDF5 datatype associated @@ -472,6 +492,13 @@ H5HL_DLL herr_t H5TBget_table_info(hid_t loc_id, const char *dset_name, hsize_t * named \p dset_name attached to the object specified * by the identifier \p loc_id. * + * \note When retrieving field names, callers should allocate each field_names[i] + * buffer with sufficient space for the expected field name length. + * For typical field names (< 255 characters), only the actual name length + * plus one byte for null termination is written. Field names exceeding + * #HLTB_MAX_FIELD_LEN - 1 characters will be silently truncated and + * require buffers of at least #HLTB_MAX_FIELD_LEN bytes. + * */ H5HL_DLL herr_t H5TBget_field_info(hid_t loc_id, const char *dset_name, char *field_names[], size_t *field_sizes, size_t *field_offsets, size_t *type_size); diff --git a/hl/test/test_table.c b/hl/test/test_table.c index 8545aac708a..724895cb64c 100644 --- a/hl/test/test_table.c +++ b/hl/test/test_table.c @@ -1488,6 +1488,201 @@ test_table(hid_t fid, int do_write) PASSED(); + /*------------------------------------------------------------------------- + * + * Test backward compatibility: smaller buffers for short field names + * + *------------------------------------------------------------------------- + */ + + HL_TESTING2("field info with small buffers (backward compatibility)"); + + /* Allocate smaller buffers (32 bytes) which is sufficient for the actual + * field names in this test ("Name", "Longitude", "Pressure", "Temperature", "Latitude") + * This tests that the library doesn't unconditionally write to byte 254, + * which would overflow these smaller buffers. */ + names_out = (char **)malloc(sizeof(char *) * (size_t)NFIELDS); + for (i = 0; i < NFIELDS; i++) { + names_out[i] = (char *)malloc(sizeof(char) * 32); + } + + /* Get field info - should work with smaller buffers for short names */ + if (H5TBget_field_info(fid, "table1", names_out, sizes_out, offset_out, &size_out) < 0) + goto out; + + /* Verify field names are correctly retrieved */ + for (i = 0; i < NFIELDS; i++) { + if ((strcmp(field_names[i], names_out[i]) != 0)) { + goto out; + } + } + + /* release */ + for (i = 0; i < NFIELDS; i++) { + free(names_out[i]); + } + free(names_out); + + PASSED(); + + /*------------------------------------------------------------------------- + * + * Test field name length boundaries (HLTB_MAX_FIELD_LEN = 255) + * Tests exact boundary conditions for field name truncation + * + *------------------------------------------------------------------------- + */ + + HL_TESTING2("field name length boundaries"); + + { + hid_t fid_boundary = H5I_INVALID_HID; + hid_t boundary_field_types[5]; + size_t boundary_field_offsets[5]; + char **boundary_names_out = NULL; + size_t boundary_sizes_out[5]; + size_t boundary_offset_out[5]; + size_t boundary_size_out; + hsize_t boundary_nfields; + hsize_t boundary_nrecords; + + /* Create field names at various boundary lengths: + * - field_253: 253 chars (fits: 253 + 1 null = 254 < 255, no truncation) + * - field_254: 254 chars (at boundary: 254 + 1 null = 255, no truncation) + * - field_255: 255 chars (over: 255 + 1 null = 256 > 255, truncates to 254) + * - field_1000: 1000 chars (way over: truncates to 254) + * - ShortName: 10 chars (control, no truncation) */ + + char *field_253 = (char *)malloc(254); /* 253 chars + null */ + char *field_254 = (char *)malloc(255); /* 254 chars + null */ + char *field_255 = (char *)malloc(256); /* 255 chars + null */ + char *field_1000 = (char *)malloc(1001); /* 1000 chars + null */ + + /* Fill with 'A' characters */ + memset(field_253, 'A', 253); + field_253[253] = '\0'; + memset(field_254, 'B', 254); + field_254[254] = '\0'; + memset(field_255, 'C', 255); + field_255[255] = '\0'; + memset(field_1000, 'D', 1000); + field_1000[1000] = '\0'; + + const char *boundary_field_names[5] = {field_253, field_254, field_255, field_1000, "ShortName"}; + + /* Create temporary file for boundary test */ + if ((fid_boundary = H5Fcreate("test_boundary.h5", H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT)) < 0) + goto boundary_out; + + /* Setup field types (all int for simplicity) */ + for (i = 0; i < 5; i++) { + boundary_field_types[i] = H5T_NATIVE_INT; + boundary_field_offsets[i] = i * sizeof(int); + } + + /* Create table with boundary-length field names */ + int boundary_data[2][5] = {{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}}; + + if (H5TBmake_table("Boundary Test", fid_boundary, "boundary_table", 5, 2, 5 * sizeof(int), + boundary_field_names, boundary_field_offsets, boundary_field_types, 10, NULL, 0, + boundary_data) < 0) + goto boundary_out; + + /* Allocate output buffers (HLTB_MAX_FIELD_LEN bytes each) */ + boundary_names_out = (char **)malloc(sizeof(char *) * 5); + for (i = 0; i < 5; i++) { + boundary_names_out[i] = (char *)malloc(HLTB_MAX_FIELD_LEN); + } + + /* Retrieve field info */ + if (H5TBget_field_info(fid_boundary, "boundary_table", boundary_names_out, boundary_sizes_out, + boundary_offset_out, &boundary_size_out) < 0) + goto boundary_out; + + /* Verify results (HLTB_MAX_FIELD_LEN = 255): + * - field_253 (253 chars): should NOT be truncated (253 + 1 null = 254 < 255) + * - field_254 (254 chars): should NOT be truncated (254 + 1 null = 255 = 255) + * - field_255 (255 chars): should be truncated to 254 chars (255 + 1 null = 256 > 255) + * - field_1000 (1000 chars): should be truncated to 254 chars + * - ShortName: should NOT be truncated */ + + /* Test 1: 253-char name should fit exactly (no truncation) */ + if (strlen(boundary_names_out[0]) != 253) + goto boundary_out; + if (strncmp(boundary_names_out[0], field_253, 253) != 0) + goto boundary_out; + + /* Test 2: 254-char name should fit exactly at boundary (no truncation) */ + if (strlen(boundary_names_out[1]) != 254) + goto boundary_out; + if (strncmp(boundary_names_out[1], field_254, 254) != 0) + goto boundary_out; + + /* Test 3: 255-char name should be truncated to 254 chars */ + if (strlen(boundary_names_out[2]) != 254) + goto boundary_out; + if (strncmp(boundary_names_out[2], field_255, 254) != 0) + goto boundary_out; + + /* Test 4: 1000-char name should be truncated to 254 chars */ + if (strlen(boundary_names_out[3]) != 254) + goto boundary_out; + if (strncmp(boundary_names_out[3], field_1000, 254) != 0) + goto boundary_out; + + /* Test 5: Short name should remain unchanged */ + if (strcmp(boundary_names_out[4], "ShortName") != 0) + goto boundary_out; + + /* Verify table info */ + if (H5TBget_table_info(fid_boundary, "boundary_table", &boundary_nfields, &boundary_nrecords) < 0) + goto boundary_out; + if (boundary_nfields != 5 || boundary_nrecords != 2) + goto boundary_out; + + /* Cleanup */ + for (i = 0; i < 5; i++) { + free(boundary_names_out[i]); + } + free(boundary_names_out); + free(field_253); + free(field_254); + free(field_255); + free(field_1000); + + if (H5Fclose(fid_boundary) < 0) + goto out; + + PASSED(); + goto boundary_cleanup; + +boundary_out: + H5_FAILED(); + if (boundary_names_out) { + for (i = 0; i < 5; i++) { + if (boundary_names_out[i]) + free(boundary_names_out[i]); + } + free(boundary_names_out); + } + if (field_253) + free(field_253); + if (field_254) + free(field_254); + if (field_255) + free(field_255); + if (field_1000) + free(field_1000); + H5E_BEGIN_TRY + { + H5Fclose(fid_boundary); + } + H5E_END_TRY + return -1; + +boundary_cleanup:; + } + /*------------------------------------------------------------------------- * end *------------------------------------------------------------------------- diff --git a/release_docs/CHANGELOG.md b/release_docs/CHANGELOG.md index 44fda58147f..427852b4570 100644 --- a/release_docs/CHANGELOG.md +++ b/release_docs/CHANGELOG.md @@ -84,6 +84,14 @@ We would like to thank the many HDF5 community members who contributed to this r ## Library +### Fixed file descriptor leaks in stdio VFD error paths + + Fixed multiple resource leaks in the H5FDstdio driver where file descriptors were not properly closed on error paths. The error handling code was incorrectly attempting to close a local variable instead of the file pointer stored in the file structure, leading to file descriptor leaks. This issue affected 5 error paths in `H5FD_stdio_open()` and could cause file descriptor exhaustion in long-running applications. + +### Added defensive NULL pointer checks in native VOL connector + + Added assertion checks for NULL pointer parameters in `H5VL_native_get_file_struct()` to catch programming errors earlier and improve code robustness. + ## Java Library ## Configuration @@ -98,6 +106,28 @@ We would like to thank the many HDF5 community members who contributed to this r ## High-Level Library +### Fixed critical buffer overflow vulnerability in H5TBget_field_info() + + **SECURITY FIX**: Fixed a buffer overflow vulnerability (CWE-120) in `H5TBget_field_info()` where field names were copied using unbounded `strcpy()`, allowing malicious HDF5 files with overly long field names to overflow caller-provided buffers. The function now uses bounds-checked `memcpy()` with the newly public `HLTB_MAX_FIELD_LEN` constant (255 bytes). Field names exceeding this limit are safely truncated. Backward compatibility is preserved - buffers smaller than 255 bytes still work correctly for short field names. + +### Made HLTB_MAX_FIELD_LEN constant public + + The `HLTB_MAX_FIELD_LEN` constant (255) has been moved from the private header `H5TBprivate.h` to the public header `H5TBpublic.h` with full documentation. This allows applications to correctly size their field name buffers when calling `H5TBget_field_info()`. Previously, this value was internal-only, forcing users to guess the appropriate buffer size. + +### Fixed memory leaks and improved safety in H5LT functions + + - Fixed memory leak in `H5LTtext_to_dtype()` by adding NULL check after `strdup()` call + - Added defensive NULL checks and pointer nullification after `free()` calls to prevent use-after-free bugs + - Improved documentation for `realloc_and_append()` internal function with detailed parameter contracts and preconditions + +### Eliminated code duplication in H5LT datatype conversion + + Refactored `H5LT_dtype_to_text()` by extracting common super-type handling logic into a new helper function `H5LT_append_dtype_super_text()`. This eliminates approximately 80 lines of duplicated code that was previously repeated across 4 datatype cases (ENUM, VLEN, ARRAY, COMPLEX), improving maintainability and reducing the risk of inconsistent behavior. + +### Fixed use-after-free risk in H5DSis_scale() + + Added `buf = NULL` assignment after freeing buffer to prevent potential use-after-free bugs. + ## Fortran High-Level APIs ## Documentation diff --git a/src/H5FDstdio.c b/src/H5FDstdio.c index ecc62ce190d..63881842bc6 100644 --- a/src/H5FDstdio.c +++ b/src/H5FDstdio.c @@ -393,8 +393,8 @@ H5FD_stdio_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr /* Use the value in the property list */ if (H5Pget_file_locking(fapl_id, &unused, &file->ignore_disabled_file_locks) < 0) { + fclose(file->fp); free(file); - fclose(f); H5Epush_ret(__func__, H5E_ERR_CLS, H5E_FILE, H5E_CANTGET, "unable to get use disabled file locks property", NULL); } @@ -407,23 +407,23 @@ H5FD_stdio_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr file->fd = fileno(file->fp); #endif /* H5_HAVE_WIN32_API */ if (file->fd < 0) { + fclose(file->fp); free(file); - fclose(f); H5Epush_ret(__func__, H5E_ERR_CLS, H5E_FILE, H5E_CANTOPENFILE, "unable to get file descriptor", NULL); } /* end if */ #ifdef H5_HAVE_WIN32_API file->hFile = (HANDLE)_get_osfhandle(file->fd); if (INVALID_HANDLE_VALUE == file->hFile) { + fclose(file->fp); free(file); - fclose(f); H5Epush_ret(__func__, H5E_ERR_CLS, H5E_FILE, H5E_CANTOPENFILE, "unable to get Windows file handle", NULL); } /* end if */ if (!GetFileInformationByHandle((HANDLE)file->hFile, &fileinfo)) { + fclose(file->fp); free(file); - fclose(f); H5Epush_ret(__func__, H5E_ERR_CLS, H5E_FILE, H5E_CANTOPENFILE, "unable to get Windows file descriptor information", NULL); } /* end if */ @@ -433,8 +433,8 @@ H5FD_stdio_open(const char *name, unsigned flags, hid_t fapl_id, haddr_t maxaddr file->dwVolumeSerialNumber = fileinfo.dwVolumeSerialNumber; #else /* H5_HAVE_WIN32_API */ if (fstat(file->fd, &sb) < 0) { + fclose(file->fp); free(file); - fclose(f); H5Epush_ret(__func__, H5E_ERR_CLS, H5E_FILE, H5E_BADFILE, "unable to fstat file", NULL); } /* end if */ file->device = sb.st_dev; diff --git a/src/H5VLnative.c b/src/H5VLnative.c index d4bb31d9aea..b6309f2ee62 100644 --- a/src/H5VLnative.c +++ b/src/H5VLnative.c @@ -561,6 +561,10 @@ H5VL_native_get_file_struct(void *obj, H5I_type_t type, H5F_t **file) FUNC_ENTER_NOAPI(FAIL) + /* Check arguments */ + assert(obj); + assert(file); + *file = NULL; switch (type) {