Hide built-in filter class symbols from the dynamic symbol table

H5Z_DEFLATE/H5Z_SHUFFLE/H5Z_FLETCHER32/H5Z_NBIT/H5Z_SCALEOFFSET/H5Z_SZIP
were declared with H5_DLLVAR, which expands to
`extern __attribute__((visibility("default")))` -- exporting these
package-private structs into libhdf5's dynamic symbol table even
though they're only ever referenced from H5Z.c within this library.

Converting these to H5Z_class3_t changed their runtime .version field
(now H5Z_CLASS3_T_VERS_INTERNAL == 2) and struct size. Any process that
both links libhdf5 directly and dynamically loads a third-party H5Z
filter plugin whose own H5Z_class2_t global happens to share the same
symbol name (e.g. netCDF-c's own H5Zdeflate.c test plugin, which
declares its own `const H5Z_class2_t H5Z_DEFLATE[1]`) is subject to
ELF symbol interposition: the dynamic linker's default global/exported
symbol resolution silently rebinds the plugin's own internal reference
to *our* already-loaded H5Z_DEFLATE instead, so plugin code reading
"its own" struct actually reads ours -- version 2 where it expected 1,
tripping the plugin's version-mismatch validation and failing with a
generic filter error.

This was silently harmless upstream (H5Z_DEFLATE there is
H5Z_class2_t/version 1, structurally identical to what third-party
code expects), so it never surfaced as a bug until our own struct
conversion made the two same-named, cross-library symbols actually
disagree.

Root-caused by reproducing netCDF-c's nczarr test suite locally
against this branch's HDF5 build (matching CI's exact netCDF-c commit
and build config) -- 22/33 nczarr tests failed with "Filter error: bad
id or parameters or duplicate filter" prior to this fix, all passing
identically to upstream develop's clean run afterward. Confirmed via
direct instrumentation that netCDF-c's own H5Zdeflate.c plugin was
reading H5Z_CLASS3_T_VERS_INTERNAL (2) out of our H5Z_DEFLATE instead
of its own H5Z_CLASS_T_VERS (1). After marking these six symbols
H5_ATTR_VISIBILITY_HIDDEN (an existing, previously-unused macro
already in H5private.h for exactly this purpose) and removing the
inappropriate H5_DLLVAR export, netCDF-c's full nczarr_test suite
passes 32/32 with 0 failures.
This commit is contained in:
Scot Breitenfeld
2026-07-16 11:53:40 -05:00
parent cf898f31e1
commit 498ebe3948
7 changed files with 21 additions and 12 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ static herr_t H5Z__deflate_get_config(unsigned flags, size_t cd_nelmts, const un
size_t *buf_size);
/* This message derives from H5Z */
const H5Z_class3_t H5Z_DEFLATE[1] = {{
H5_ATTR_VISIBILITY_HIDDEN const H5Z_class3_t H5Z_DEFLATE[1] = {{
2, /* H5Z_class3_t version (literal 2) */
H5Z_FILTER_DEFLATE, /* Filter id number */
1, /* encoder_present flag (set to true) */
+1 -1
View File
@@ -23,7 +23,7 @@ static size_t H5Z__filter_fletcher32(unsigned flags, size_t cd_nelmts, const uns
size_t *buf_size, void **buf);
/* This message derives from H5Z */
const H5Z_class3_t H5Z_FLETCHER32[1] = {{
H5_ATTR_VISIBILITY_HIDDEN const H5Z_class3_t H5Z_FLETCHER32[1] = {{
2, /* H5Z_class3_t version (literal 2) */
H5Z_FILTER_FLETCHER32, /* Filter id number */
1, /* encoder_present flag (set to true) */
+1 -1
View File
@@ -86,7 +86,7 @@ static herr_t H5Z__nbit_set_config(const char *params, unsigned *flags, size_t *
unsigned cd_values[], size_t cd_values_size);
/* This message derives from H5Z */
H5Z_class3_t H5Z_NBIT[1] = {{
H5_ATTR_VISIBILITY_HIDDEN H5Z_class3_t H5Z_NBIT[1] = {{
2, /* H5Z_class3_t version (literal 2) */
H5Z_FILTER_NBIT, /* Filter id number */
1, /* Assume encoder present: check before registering */
+15 -6
View File
@@ -29,17 +29,26 @@
/* Internal filters */
/********************/
/* These built-in filter class structs are package-private: referenced only
* from within libhdf5 (H5Z.c), never part of the public API. H5_DLLVAR's
* default-visibility export is therefore wrong for them -- it previously
* caused these symbols to appear in libhdf5's dynamic symbol table, where
* they can collide with identically-named globals in dynamically loaded
* third-party filter plugins that happen to share the same symbol name.
* Plain "extern" plus hidden visibility keeps them linkable across this
* library's own translation units without exposing them externally. */
/* Shuffle filter */
H5_DLLVAR const H5Z_class3_t H5Z_SHUFFLE[1];
H5_ATTR_VISIBILITY_HIDDEN extern const H5Z_class3_t H5Z_SHUFFLE[1];
/* Fletcher32 filter */
H5_DLLVAR const H5Z_class3_t H5Z_FLETCHER32[1];
H5_ATTR_VISIBILITY_HIDDEN extern const H5Z_class3_t H5Z_FLETCHER32[1];
/* n-bit filter */
H5_DLLVAR H5Z_class3_t H5Z_NBIT[1];
H5_ATTR_VISIBILITY_HIDDEN extern H5Z_class3_t H5Z_NBIT[1];
/* Scale/offset filter */
H5_DLLVAR H5Z_class3_t H5Z_SCALEOFFSET[1];
H5_ATTR_VISIBILITY_HIDDEN extern H5Z_class3_t H5Z_SCALEOFFSET[1];
/********************/
/* External filters */
@@ -47,12 +56,12 @@ H5_DLLVAR H5Z_class3_t H5Z_SCALEOFFSET[1];
/* Deflate filter */
#ifdef H5_HAVE_FILTER_DEFLATE
H5_DLLVAR const H5Z_class3_t H5Z_DEFLATE[1];
H5_ATTR_VISIBILITY_HIDDEN extern const H5Z_class3_t H5Z_DEFLATE[1];
#endif /* H5_HAVE_FILTER_DEFLATE */
/* szip filter */
#ifdef H5_HAVE_FILTER_SZIP
H5_DLLVAR H5Z_class3_t H5Z_SZIP[1];
H5_ATTR_VISIBILITY_HIDDEN extern H5Z_class3_t H5Z_SZIP[1];
#endif /* H5_HAVE_FILTER_SZIP */
/* Package internal routines */
+1 -1
View File
@@ -94,7 +94,7 @@ static herr_t H5Z__scaleoffset_get_config(unsigned flags, size_t cd_nelmts, cons
char *buf, size_t *buf_size);
/* This message derives from H5Z */
H5Z_class3_t H5Z_SCALEOFFSET[1] = {{
H5_ATTR_VISIBILITY_HIDDEN H5Z_class3_t H5Z_SCALEOFFSET[1] = {{
2, /* H5Z_class3_t version (literal 2) */
H5Z_FILTER_SCALEOFFSET, /* Filter id number */
1, /* Assume encoder present: check before registering */
+1 -1
View File
@@ -27,7 +27,7 @@ static size_t H5Z__filter_shuffle(unsigned flags, size_t cd_nelmts, const unsign
void **buf);
/* This message derives from H5Z */
const H5Z_class3_t H5Z_SHUFFLE[1] = {{
H5_ATTR_VISIBILITY_HIDDEN const H5Z_class3_t H5Z_SHUFFLE[1] = {{
2, /* H5Z_class3_t version (literal 2) */
H5Z_FILTER_SHUFFLE, /* Filter id number */
1, /* encoder_present flag (set to true) */
+1 -1
View File
@@ -40,7 +40,7 @@ static herr_t H5Z__szip_get_config(unsigned flags, size_t cd_nelmts, const unsig
size_t *buf_size);
/* This message derives from H5Z */
H5Z_class3_t H5Z_SZIP[1] = {{
H5_ATTR_VISIBILITY_HIDDEN H5Z_class3_t H5Z_SZIP[1] = {{
2, /* H5Z_class3_t version (literal 2) */
H5Z_FILTER_SZIP, /* Filter id number */
1, /* Assume encoder present: check before registering */