mirror of
https://github.com/HDFGroup/hdf5.git
synced 2026-09-25 04:09:44 +03:00
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:
@@ -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");
|
||||
|
||||
@@ -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
@@ -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
@@ -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) */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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) */
|
||||
|
||||
@@ -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
@@ -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
@@ -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) */
|
||||
|
||||
@@ -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
@@ -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 --- */
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
Reference in New Issue
Block a user