Fix High/Medium issues from the filter_config_string comprehensive review

Addresses 11 findings from a 12-agent review of the string-based filter
configuration feature:

- H5Opline.c: fail H5Dcreate/H5Ocopy with H5E_BADRANGE when a filter's
  config string cannot be persisted because the file's high libver bound
  doesn't support pipeline v3, instead of silently dropping the string
  with a success return.
- H5Pocpl.c: H5Pget_filter_params_by_idx() now raises H5E_OVERFLOW on
  truncation in all three source branches, matching its own sibling
  H5Zconfig_get_str(); also stops discarding a genuine H5Z_filter_avail()
  failure.
- H5Zdevelop.h / H5Z.c: fix two independently-wrong comments about
  H5Z_class3_t's field layout, one of which gave plugin authors unsafe
  migration advice for the filter callback's incompatible type change.
- h5zImp.c: fix an out-of-bounds heap write in the JNI H5Zconfig_get_param
  long/double/boolean overloads when called with a zero-length array.
- h5tools_dump.c: h5dump -p no longer silently omits an entire filter's
  FILTERS{} entry when a decoration buffer fails to allocate.
- tomlc17: add h5_toml_prefix.h, an HDF5-authored (not vendored) header
  that force-includes symbol-renaming macros ahead of tomlc17.c, so a
  statically-linked libhdf5.a cannot collide with an application's own
  copy of tomlc17. Does not touch the pristine vendored files.
- H5Zconfig.c: H5Z_CONFIG_MAX_PARAMS is now actually enforced.
- Six built-in filters: use H5Z_CLASS3_T_VERS instead of a hardcoded
  literal for the class version field.

test/tfilter2.c updated to match the new libver-bound failure contract.
Verified via a full rebuild: tfilter2, t_filters_parallel, h5repack
plugin_test_cfg*, and h5dump filter tests all pass.

Deferred by explicit decision (documented in
COMPREHENSIVE_REVIEW_filter_config_string.md): bumping H5P_ENCODE_VERS
and hardening the H5Pdecode() buffer-length gap, both of which need
architecture beyond this fix pass; JNI test coverage for the new API;
and the remaining ~13 Medium/Low findings.
This commit is contained in:
Scot Breitenfeld
2026-09-11 23:29:08 -05:00
parent caf35f3c09
commit dc2584eb2c
17 changed files with 264 additions and 57 deletions
+6
View File
@@ -187,6 +187,8 @@ Java_hdf_hdf5lib_H5_H5Zconfig_1get_1param__Ljava_lang_String_2Ljava_lang_String_
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Zconfig_get_param: key string is NULL");
if (NULL == out)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Zconfig_get_param: out array is NULL");
if (ENVPTR->GetArrayLength(ENVONLY, out) < 1)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Zconfig_get_param: out array must have at least one element");
PIN_JAVA_STRING(ENVONLY, params, c_params, &isCopy1, "H5Zconfig_get_param: params not pinned");
PIN_JAVA_STRING(ENVONLY, key, c_key, &isCopy2, "H5Zconfig_get_param: key not pinned");
@@ -238,6 +240,8 @@ Java_hdf_hdf5lib_H5_H5Zconfig_1get_1param__Ljava_lang_String_2Ljava_lang_String_
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Zconfig_get_param: key string is NULL");
if (NULL == out)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Zconfig_get_param: out array is NULL");
if (ENVPTR->GetArrayLength(ENVONLY, out) < 1)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Zconfig_get_param: out array must have at least one element");
PIN_JAVA_STRING(ENVONLY, params, c_params, &isCopy1, "H5Zconfig_get_param: params not pinned");
PIN_JAVA_STRING(ENVONLY, key, c_key, &isCopy2, "H5Zconfig_get_param: key not pinned");
@@ -289,6 +293,8 @@ Java_hdf_hdf5lib_H5_H5Zconfig_1get_1param__Ljava_lang_String_2Ljava_lang_String_
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Zconfig_get_param: key string is NULL");
if (NULL == out)
H5_NULL_ARGUMENT_ERROR(ENVONLY, "H5Zconfig_get_param: out array is NULL");
if (ENVPTR->GetArrayLength(ENVONLY, out) < 1)
H5_BAD_ARGUMENT_ERROR(ENVONLY, "H5Zconfig_get_param: out array must have at least one element");
PIN_JAVA_STRING(ENVONLY, params, c_params, &isCopy1, "H5Zconfig_get_param: params not pinned");
PIN_JAVA_STRING(ENVONLY, key, c_key, &isCopy2, "H5Zconfig_get_param: key not pinned");
+29
View File
@@ -1016,6 +1016,35 @@ if (HDF5_C_HAS_FVISIBILITY_HIDDEN)
)
endif ()
# -fvisibility=hidden above only protects a *shared* libhdf5 -- it has no
# effect on a static libhdf5.a, whose object files still export tomlc17's
# ordinary global symbols (toml_parse, toml_free, ...), so an application
# statically linking libhdf5.a alongside its own copy of tomlc17 can hit a
# duplicate-symbol link error or silent symbol interposition. Force-include
# h5_toml_prefix.h (an HDF5-authored file, not part of the pristine vendored
# copy) ahead of tomlc17.c's own content so every tomlc17_xxx symbol it
# defines is renamed to H5Z__toml_c17_xxx via plain #define -- this needs no
# edit to the pristine tomlc17.c/.h files, so their checked-in SHA-256 hashes
# in src/tomlc17/README.md remain valid. H5Zconfig.c, the sole HDF5 caller,
# picks up the same renaming via its own explicit #include of the same file.
if (MSVC)
set_property (SOURCE ${HDF5_SRC_DIR}/tomlc17/tomlc17.c APPEND PROPERTY COMPILE_OPTIONS
"/FI${HDF5_SRC_DIR}/tomlc17/h5_toml_prefix.h"
)
else ()
check_c_compiler_flag ("-include ${HDF5_SRC_DIR}/tomlc17/h5_toml_prefix.h" HDF5_C_HAS_INCLUDE_FLAG)
if (HDF5_C_HAS_INCLUDE_FLAG)
set_property (SOURCE ${HDF5_SRC_DIR}/tomlc17/tomlc17.c APPEND PROPERTY COMPILE_OPTIONS
"-include" "${HDF5_SRC_DIR}/tomlc17/h5_toml_prefix.h"
)
else ()
message (WARNING
"Compiler does not support -include; the vendored tomlc17 symbols will "
"NOT be prefixed and may collide with an application's own copy of "
"tomlc17 when statically linked. See src/CMakeLists.txt.")
endif ()
endif ()
# Vendored third-party code should not be held to HDF5_ENABLE_WARNINGS_AS_ERRORS'
# own strict internal warning set: it legitimately trips several of them
# (e.g. -Werror=cast-align in its pointer-arithmetic tokenizer), which would
+16 -4
View File
@@ -763,8 +763,12 @@ H5O_pline_set_version(H5F_t *f, H5O_pline_t *pline)
/* A filter carrying a verbatim configuration string needs the version-3
* encoding to persist it. Request v3 only when the file's high bound
* admits it; otherwise fall back silently to the current version -- the
* strings are simply not written and introspection relies on get_config. */
* admits it; if it doesn't, fail outright instead of silently falling
* back to an older version that drops the string on encode (see the
* `pline->version >= H5O_PLINE_VERSION_3` guards in H5O__pline_encode())
* -- a caller who set a config string is entitled to know it was not
* persisted, rather than getting a success return and a file that
* silently no longer round-trips the exact string they set. */
if (version < H5O_PLINE_VERSION_3) {
bool have_config = false;
@@ -774,8 +778,16 @@ H5O_pline_set_version(H5F_t *f, H5O_pline_t *pline)
break;
}
if (have_config && H5O_pline_ver_bounds[H5F_HIGH_BOUND(f)] >= H5O_PLINE_VERSION_3)
version = H5O_PLINE_VERSION_3;
if (have_config) {
if (H5O_pline_ver_bounds[H5F_HIGH_BOUND(f)] >= H5O_PLINE_VERSION_3)
version = H5O_PLINE_VERSION_3;
else
HGOTO_ERROR(H5E_PLINE, H5E_BADRANGE, FAIL,
"filter configuration string cannot be persisted: file's high library "
"version bound does not support the version-3 filter pipeline message "
"required to store it (raise the bound with H5Pset_libver_bounds(), or "
"append the filter without a configuration string)");
}
}
/* Version bounds check */
+34 -11
View File
@@ -1447,14 +1447,23 @@ H5P__ocrt_pipeline_dec(const void **_pp, void *_value)
assert(enc_size < 256);
UINT64DECODE_VAR(*pp, config_len, enc_size);
/* This decode callback has no end-of-buffer pointer to bound
* the memcpy below against (unlike H5O__pline_decode, which
* validates against p_end for the on-disk equivalent of this
* same field) -- capping config_len here is what keeps a
* corrupted/malicious H5Pdecode() buffer from driving an
* unbounded allocation and an unbounded read past the real
* buffer, and also prevents (size_t)config_len + 1 from
* wrapping on a maximal config_len. */
/* H5P_prp_decode_func_t (the public typedef every property
* decode callback, including this one, must match) is
* herr_t (*)(const void **buf, void *value) -- there is no
* end-of-buffer pointer anywhere in this call chain, down to
* H5Pdecode() itself, whose contract is "the user is
* responsible for passing in the correct buffer" (see its
* doxygen). That is a pre-existing, systemic property of every
* field this function decodes (cd_nelmts/cd_values above are
* equally unbounded against the real buffer length) -- adding
* an end pointer here alone would not close that, and doing so
* for real would mean changing a public callback typedef used
* by every third-party property, not a fix scoped to this
* field. The H5Z_CONFIG_STRING_MAX cap below is therefore only
* a bound on how FAR a malformed/corrupted config_len can walk
* past the real buffer on a bad H5Pdecode() input -- not a
* guarantee that it can't happen at all -- and it also keeps
* (size_t)config_len + 1 from wrapping on a maximal value. */
if (config_len > H5Z_CONFIG_STRING_MAX) {
filter.cd_values = (unsigned *)H5MM_xfree(filter.cd_values);
HGOTO_ERROR(H5E_PLIST, H5E_BADVALUE, FAIL, "filter config string exceeds maximum length");
@@ -2345,6 +2354,8 @@ H5Pget_filter_params_by_idx(hid_t plist_id, unsigned idx, char *params_buf, size
size_t copy_len = (needed < params_buf_size - 1) ? needed : params_buf_size - 1;
H5MM_memcpy(params_buf, filter->config, copy_len);
params_buf[copy_len] = '\0';
if (copy_len < needed)
HGOTO_ERROR(H5E_PLIST, H5E_OVERFLOW, FAIL, "params_buf too small for stored config string");
}
HGOTO_DONE(SUCCEED);
@@ -2352,9 +2363,15 @@ H5Pget_filter_params_by_idx(hid_t plist_id, unsigned idx, char *params_buf, size
/* Trigger a plugin load if the filter isn't registered yet, so get_config
* is available on the first query rather than only subsequent ones
* (mirrors H5Zget_filter_class_info()). Availability itself is ignored
* here; the fallback path below handles filters that remain unavailable. */
(void)H5Z_filter_avail(filter->id);
* (mirrors H5Zget_filter_class_info()). A "not available" result is
* ignored here; the fallback path below handles filters that remain
* unavailable. A genuine failure (e.g. H5PL_load() found a plugin but
* H5Z_register() rejected it) pushes a real error onto the stack, which
* we must not leave for an unrelated later H5Eprint()/H5Ewalk() to
* surface confusingly -- clear it explicitly instead of discarding the
* return value outright. */
if (H5Z_filter_avail(filter->id) < 0)
H5E_clear_stack();
/* Attempt to find the entry (try plugin load if not yet registered) */
(void)H5Z_find_entry(true, filter->id, &entry);
@@ -2396,6 +2413,9 @@ H5Pget_filter_params_by_idx(hid_t plist_id, unsigned idx, char *params_buf, size
size_t copy_len = (out_size < params_buf_size - 1) ? out_size : params_buf_size - 1;
H5MM_memcpy(params_buf, tmp_buf, copy_len);
params_buf[copy_len] = '\0';
if (copy_len < out_size)
HGOTO_ERROR(H5E_PLIST, H5E_OVERFLOW, FAIL,
"params_buf too small for get_config-reconstructed string");
}
} /* end if params_buf */
} /* end if get_config */
@@ -2442,6 +2462,9 @@ H5Pget_filter_params_by_idx(hid_t plist_id, unsigned idx, char *params_buf, size
size_t copy_len = (true_len < params_buf_size - 1) ? true_len : params_buf_size - 1;
H5MM_memcpy(params_buf, tmp_buf, copy_len);
params_buf[copy_len] = '\0';
if (copy_len < true_len)
HGOTO_ERROR(H5E_PLIST, H5E_OVERFLOW, FAIL,
"params_buf too small for synthesized cd_values string");
}
}
} /* end else (fallback) */
+9
View File
@@ -2973,6 +2973,15 @@ H5_DLL herr_t H5Pmodify_filter_by_idx(hid_t plist_id, unsigned filter_idx, unsig
* (excluding NUL) in \p params_len, then allocate \p params_len + 1
* bytes and call again with \p params_buf_size = \p params_len + 1.
*
* \note If \p params_buf is non-NULL but \p params_buf_size is smaller
* than the required size, \p params_buf is still filled with a
* truncated, NUL-terminated string (in case the caller wants the
* partial content), \p params_len (if non-NULL) is still set to the
* true, untruncated required length, and the function fails with a
* minor error code of #H5E_OVERFLOW. Check the return value, not
* just whether \p params_buf came back non-empty, to detect
* truncation.
*
* \since 3.0.0
*/
H5_DLL herr_t H5Pget_filter_params_by_idx(hid_t plist_id, unsigned idx, char *params_buf,
+8 -4
View File
@@ -442,10 +442,14 @@ H5Z_register(const H5Z_class2_t *cls)
/* H5Z_register() is typed as const H5Z_class2_t *, but external callers
* (H5Zregister, H5PL_load) may pass a H5Z_class3_t * cast to that type.
* Re-sniff the version here so that the description field
* (at the same offset as can_apply in H5Z_class2_t) is never misread.
* Do NOT "simplify" away this check - the v3 dispatch must happen inside
* H5Z_register, not only at the H5Zregister API boundary. */
* version is the first field in both structs, so it can be read safely
* through either type; re-sniff it here before touching anything past
* set_local, since H5Z_class3_t's filter field is a wider-signature
* H5Z_func2_t (not H5Z_func_t) and the struct carries three more fields
* (set_config, get_config, description) that H5Z_class2_t does not have
* at all -- treating a v3-cast struct as v2 past that point would misread
* those fields. Do NOT "simplify" away this check - the v3 dispatch must
* happen inside H5Z_register, not only at the H5Zregister API boundary. */
if (cls->version == H5Z_CLASS3_T_VERS_INTERNAL) {
if (H5Z_register3((const H5Z_class3_t *)cls) < 0)
HGOTO_ERROR(H5E_PLINE, H5E_CANTINIT, FAIL, "unable to register filter");
+38
View File
@@ -40,6 +40,12 @@
#include "H5MMprivate.h" /* Memory management */
#include "H5Zpkg.h" /* Filter internals */
/* Renames every public tomlc17 symbol to an H5Z__toml_c17_-prefixed name so
* a statically-linked libhdf5.a cannot collide with an application's own
* copy of tomlc17 (see h5_toml_prefix.h for the full rationale). Must be
* included before tomlc17.h so every call site below picks up the renamed
* declarations. */
#include "tomlc17/h5_toml_prefix.h"
#include "tomlc17/tomlc17.h"
/* Append one source character to the output buffer, or skip it if full. */
@@ -337,6 +343,30 @@ done:
FUNC_LEAVE_NOAPI(ret_value)
} /* end H5Z_canonicalize_params() */
/*
* H5Z__count_table_keys - recursively count leaf key=value assignments in a
* parsed TOML table, including keys nested inside inline tables / dotted-key
* groups (a nested table itself is not counted, only its own leaves are).
* Used to enforce H5Z_CONFIG_MAX_PARAMS.
*/
static size_t
H5Z__count_table_keys(toml_datum_t tab)
{
size_t count = 0;
int32_t i;
for (i = 0; i < tab.u.tab.size; i++) {
toml_datum_t v = tab.u.tab.value[i];
if (v.type == TOML_TABLE)
count += H5Z__count_table_keys(v);
else
count++;
}
return count;
}
/*
* H5Z__toml_parse_params - wrap params as a TOML document and parse it.
*
@@ -399,6 +429,14 @@ H5Z__toml_parse_params(const char *params, toml_result_t *tr_out, toml_datum_t *
"malformed filter parameter string (not a valid TOML inline table)");
}
if (H5Z__count_table_keys(*ptab_out) > H5Z_CONFIG_MAX_PARAMS) {
toml_free(*tr_out);
memset(tr_out, 0, sizeof(*tr_out));
HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL,
"filter parameter string exceeds H5Z_CONFIG_MAX_PARAMS (%d key-value pairs)",
H5Z_CONFIG_MAX_PARAMS);
}
done:
H5MM_xfree(wrapped);
H5MM_xfree(expanded);
+1 -1
View File
@@ -37,7 +37,7 @@ static herr_t H5Z__deflate_get_config(unsigned flags, size_t cd_nelmts, const un
/* This message derives from H5Z */
H5_ATTR_VISIBILITY_HIDDEN const H5Z_class3_t H5Z_DEFLATE[1] = {{
2, /* H5Z_class3_t version */
H5Z_CLASS3_T_VERS, /* H5Z_class3_t version */
H5Z_FILTER_DEFLATE, /* Filter id number */
1, /* encoder_present flag (set to true) */
1, /* decoder_present flag (set to true) */
+9 -4
View File
@@ -305,11 +305,16 @@ typedef struct H5Z_class3_t {
H5Z_get_config_func_t get_config; /**< Parameter string reconstruction; may be NULL */
const char *description; /**< Human-readable description of the filter (e.g., "Deflate (zlib)
general-purpose compression"); may be NULL. Appended last (not
inserted after \c name) so that a caller positionally initializing
this struct from an H5Z_class2_t literal -- version, id,
inserted after \c name) so the first eight fields -- version, id,
encoder_present, decoder_present, name, can_apply, set_local, filter --
and simply appending the new v3 fields keeps every original field in
its original slot. */
share the same slots an H5Z_class2_t literal would occupy positionally.
\warning That slot correspondence does NOT make \c filter itself
reusable: it is typed \c H5Z_func2_t here (9 parameters, adding
\p dxpl_id, \p scaled, \p ndims) versus \c H5Z_func_t (7 parameters)
in H5Z_class2_t. A callback written against the old signature must be
rewritten to accept and, if unneeded, ignore the three new parameters
before it can be assigned to this field -- reusing the old function
pointer as-is is undefined behavior. */
} H5Z_class3_t;
//! <!-- [H5Z_class3_t_snip] -->
+1 -1
View File
@@ -24,7 +24,7 @@ static size_t H5Z__filter_fletcher32(unsigned flags, size_t cd_nelmts, const uns
/* This message derives from H5Z */
H5_ATTR_VISIBILITY_HIDDEN const H5Z_class3_t H5Z_FLETCHER32[1] = {{
2, /* H5Z_class3_t version */
H5Z_CLASS3_T_VERS, /* H5Z_class3_t version */
H5Z_FILTER_FLETCHER32, /* Filter id number */
1, /* encoder_present flag (set to true) */
1, /* decoder_present flag (set to true) */
+1 -1
View File
@@ -85,7 +85,7 @@ static void H5Z__nbit_compress(unsigned char *data, unsigned d_nelmts, unsigne
/* This message derives from H5Z */
H5_ATTR_VISIBILITY_HIDDEN H5Z_class3_t H5Z_NBIT[1] = {{
2, /* H5Z_class3_t version */
H5Z_CLASS3_T_VERS, /* H5Z_class3_t version */
H5Z_FILTER_NBIT, /* Filter id number */
1, /* Assume encoder present: check before registering */
1, /* decoder_present flag (set to true) */
+1 -1
View File
@@ -95,7 +95,7 @@ static herr_t H5Z__scaleoffset_get_config(unsigned flags, size_t cd_nelmts, cons
/* This message derives from H5Z */
H5_ATTR_VISIBILITY_HIDDEN H5Z_class3_t H5Z_SCALEOFFSET[1] = {{
2, /* H5Z_class3_t version */
H5Z_CLASS3_T_VERS, /* H5Z_class3_t version */
H5Z_FILTER_SCALEOFFSET, /* Filter id number */
1, /* Assume encoder present: check before registering */
1, /* decoder_present flag (set to true) */
+1 -1
View File
@@ -28,7 +28,7 @@ static size_t H5Z__filter_shuffle(unsigned flags, size_t cd_nelmts, const unsign
/* This message derives from H5Z */
H5_ATTR_VISIBILITY_HIDDEN const H5Z_class3_t H5Z_SHUFFLE[1] = {{
2, /* H5Z_class3_t version */
H5Z_CLASS3_T_VERS, /* H5Z_class3_t version */
H5Z_FILTER_SHUFFLE, /* Filter id number */
1, /* encoder_present flag (set to true) */
1, /* decoder_present flag (set to true) */
+1 -1
View File
@@ -41,7 +41,7 @@ static herr_t H5Z__szip_get_config(unsigned flags, size_t cd_nelmts, const unsig
/* This message derives from H5Z */
H5_ATTR_VISIBILITY_HIDDEN H5Z_class3_t H5Z_SZIP[1] = {{
2, /* H5Z_class3_t version */
H5Z_CLASS3_T_VERS, /* H5Z_class3_t version */
H5Z_FILTER_SZIP, /* Filter id number */
1, /* Assume encoder present: check before registering */
1, /* decoder_present flag (set to true) */
+60
View File
@@ -0,0 +1,60 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Copyright by The HDF Group. *
* All rights reserved. *
* *
* This file is part of HDF5. The full HDF5 copyright notice, including *
* terms governing use, modification, and redistribution, is contained in *
* the LICENSE file, which can be found at the root of the source code *
* distribution tree, or in https://www.hdfgroup.org/licenses. *
* If you do not have access to either file, you may request a copy from *
* help@hdfgroup.org. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* h5_toml_prefix.h - HDF5-authored symbol-prefixing shim for the vendored
* tomlc17 library (src/tomlc17/tomlc17.c, tomlc17.h -- pristine, unmodified
* copies; see src/tomlc17/README.md for provenance and the update procedure).
*
* -fvisibility=hidden (applied to tomlc17.c in src/CMakeLists.txt) only
* strips these symbols from the dynamic symbol table of a *shared* libhdf5;
* it does nothing for a static libhdf5.a, whose object files still export
* ordinary global symbols. An application that statically links libhdf5.a
* alongside its own copy of tomlc17 (same version or not) would then hit a
* duplicate-symbol link error, or worse, silently link against whichever
* copy the linker picks first.
*
* This header renames every public tomlc17 symbol to an H5Z__toml_-prefixed
* name via plain object-like macros, force-included ahead of both tomlc17.c
* itself (via a compiler flag in src/CMakeLists.txt, since editing an
* #include into the pristine tomlc17.c would defeat the point) and every
* HDF5 file that calls into it (H5Zconfig.c, via an explicit #include
* immediately before "tomlc17/tomlc17.h"). Because a #define is a textual
* substitution, this renames both tomlc17.c's function *definitions* and
* every call site consistently, without editing a single byte of the
* pristine vendored files -- their checked-in SHA-256 hashes in
* src/tomlc17/README.md remain valid.
*
* When updating the vendored copy (see README.md's "Updating the vendored
* copy" section), diff the new tomlc17.h's public API against the list
* below and add/remove entries to match -- an unprefixed new public
* function would silently reintroduce the exact collision this header
* exists to prevent.
*/
#ifndef H5_TOML_PREFIX_H
#define H5_TOML_PREFIX_H
#define toml_parse H5Z__toml_c17_parse
#define toml_parse_named H5Z__toml_c17_parse_named
#define toml_parse_file H5Z__toml_c17_parse_file
#define toml_parse_file_named H5Z__toml_c17_parse_file_named
#define toml_parse_file_ex H5Z__toml_c17_parse_file_ex
#define toml_free H5Z__toml_c17_free
#define toml_get H5Z__toml_c17_get
#define toml_seek H5Z__toml_c17_seek
#define toml_merge H5Z__toml_c17_merge
#define toml_equiv H5Z__toml_c17_equiv
#define toml_default_option H5Z__toml_c17_default_option
#define toml_set_option H5Z__toml_c17_set_option
#endif /* H5_TOML_PREFIX_H */
+35 -19
View File
@@ -2640,8 +2640,14 @@ test_config_string_ondisk(hid_t fapl)
TEST_ERROR;
PASSED();
/* --- fmt-05: libver high bound below V300 silently omits the string --- */
TESTING("config string: silent v2 downgrade when libver bound too low");
/* --- fmt-05: libver high bound below V300 rejects the create, rather
* than silently persisting the dataset without the config string ---
* (H5O_pline_set_version() now fails outright instead of downgrading
* the pipeline message version and dropping the string: a caller who
* set a config string is entitled to know it will not round-trip,
* rather than getting a success return and a file that silently no
* longer carries the exact string they set.) */
TESTING("config string: create fails when libver bound too low to persist it");
if ((fapl_dg = H5Pcopy(fapl)) < 0)
TEST_ERROR;
if (H5Pset_libver_bounds(fapl_dg, H5F_LIBVER_EARLIEST, H5F_LIBVER_V200) < 0)
@@ -2650,26 +2656,36 @@ test_config_string_ondisk(hid_t fapl)
TEST_ERROR;
if ((file = H5Fcreate(filename, H5F_ACC_TRUNC, H5P_DEFAULT, fapl_dg)) < 0)
TEST_ERROR;
H5E_BEGIN_TRY
{
dset = H5Dcreate2(file, "dset", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT);
}
H5E_END_TRY
if (dset >= 0)
TEST_ERROR; /* must fail: string cannot be persisted at this libver bound */
dset = H5I_INVALID_HID;
if (H5Pclose(dcpl) < 0)
TEST_ERROR;
dcpl = H5I_INVALID_HID;
/* The same filter with no config string (a fresh DCPL, raw cd_values
* form) is unaffected by the bound and still succeeds. */
{
hsize_t chunk[2] = {4, 4};
unsigned cd[1] = {5};
if ((dcpl = H5Pcreate(H5P_DATASET_CREATE)) < 0)
TEST_ERROR;
if (H5Pset_chunk(dcpl, 2, chunk) < 0)
TEST_ERROR;
if (H5Pset_filter(dcpl, CFG_ONDISK_FILTER_ID, 0, 1, cd) < 0)
TEST_ERROR;
}
if ((dset = H5Dcreate2(file, "dset", H5T_NATIVE_INT, sid, H5P_DEFAULT, dcpl, H5P_DEFAULT)) < 0)
TEST_ERROR;
if (H5Dclose(dset) < 0 || H5Pclose(dcpl) < 0 || H5Fclose(file) < 0)
if (H5Dclose(dset) < 0 || H5Pclose(dcpl) < 0 || H5Fclose(file) < 0 || H5Pclose(fapl_dg) < 0)
TEST_ERROR;
dset = dcpl = file = H5I_INVALID_HID;
/* Plugin still registered: getter falls back to get_config ("level = 5"),
* proving the verbatim string was not persisted at v2. */
if ((file = H5Fopen(filename, H5F_ACC_RDONLY, fapl)) < 0)
TEST_ERROR;
if ((dset = H5Dopen2(file, "dset", H5P_DEFAULT)) < 0)
TEST_ERROR;
if ((dcpl_out = H5Dget_create_plist(dset)) < 0)
TEST_ERROR;
if (cfg_ondisk_get_params(dcpl_out, pbuf, sizeof(pbuf)) < 0)
TEST_ERROR;
if (strcmp(pbuf, "level = 5") != 0) /* get_config form, not the stored "level=5" */
TEST_ERROR;
if (H5Pclose(dcpl_out) < 0 || H5Dclose(dset) < 0 || H5Fclose(file) < 0 || H5Pclose(fapl_dg) < 0)
TEST_ERROR;
dcpl_out = dset = file = fapl_dg = H5I_INVALID_HID;
dset = dcpl = file = fapl_dg = H5I_INVALID_HID;
PASSED();
/* --- fmt-07: H5Pmodify_filter clears the stored string --- */
+14 -9
View File
@@ -3893,13 +3893,16 @@ h5tools_dump_dcpl(FILE *stream, const h5tool_format_t *info, h5tools_context_t *
const char *filter_descr = NULL; /* library-owned, no free needed */
bool have_extra; /* true if this filter has a PARAMS_STRING and/or DESCRIPTION */
if (!params_str_buf || !params_annot) {
free(params_str_buf);
free(params_annot);
continue;
}
params_annot[0] = '\0';
/* On allocation failure, do NOT skip the whole filter: fall through
* with both buffers left NULL, so the guard below simply omits the
* PARAMS_STRING/DESCRIPTION decoration (have_extra stays false) while
* the filter's own FILTERS{} entry is still rendered by the switch
* below. Silently dropping an entire filter from -p output would make
* the DDL look complete while actually under-reporting the pipeline.
* The unconditional free() calls at the end of this loop iteration
* handle cleanup either way (free(NULL) is a no-op). */
if (params_annot)
params_annot[0] = '\0';
cd_nelmts = NELMTS(cd_values);
filtn = H5Pget_filter2(dcpl_id, (unsigned)i, &filt_flags, &cd_nelmts, cd_values,
@@ -3912,8 +3915,10 @@ h5tools_dump_dcpl(FILE *stream, const h5tool_format_t *info, h5tools_context_t *
}
/* -p prints PARAMS_STRING and DESCRIPTION nested inside this
* filter's own FILTERS{} entry. */
if (dcpl_id >= 0 && ctx->show_filter_params) {
* filter's own FILTERS{} entry. Both buffers must have allocated
* successfully above; if either failed, skip decoration for this
* filter (have_extra stays false below) rather than the whole entry. */
if (dcpl_id >= 0 && ctx->show_filter_params && params_str_buf && params_annot) {
size_t plen = 0;
if (H5Pget_filter_params_by_idx(dcpl_id, (unsigned)i, params_str_buf, params_buf_size,
&plen) >= 0 &&