From b211304fb8cd21a6911686b03562b5d55f7027a2 Mon Sep 17 00:00:00 2001 From: Stuart Geipel Date: Thu, 30 Jul 2026 14:34:21 -0400 Subject: [PATCH 1/4] Add error checking to zstd hdf5 plugin This PR adds error checking to the zstd hdf5 plugin. Previously it would return uninitialized memory from malloc when any zstd error occurred. It also prevents proceeding with the result of malloc(0) in the abnormal case where decompSize == 0. --- ZSTD/src/H5Zzstd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ZSTD/src/H5Zzstd.c b/ZSTD/src/H5Zzstd.c index b96fa84c6..4f5270425 100644 --- a/ZSTD/src/H5Zzstd.c +++ b/ZSTD/src/H5Zzstd.c @@ -66,10 +66,16 @@ H5Z_filter_zstd(unsigned int flags, size_t cd_nelmts, const unsigned int cd_valu if (flags & H5Z_FLAG_REVERSE) { /* We're decompressing */ size_t decompSize = ZSTD_getFrameContentSize(*buf, origSize); + if (decompSize == 0 || decompSize == ZSTD_CONTENTSIZE_UNKNOWN || + decompSize == ZSTD_CONTENTSIZE_ERROR) + goto error; + if (NULL == (outbuf = malloc(decompSize))) goto error; decompSize = ZSTD_decompress(outbuf, decompSize, inbuf, origSize); + if (ZSTD_isError(decompSize)) + goto error; #ifdef ZSTD_DEBUG fprintf(stderr, " decompressing nbytes: %ld\n", decompSize); @@ -104,6 +110,8 @@ H5Z_filter_zstd(unsigned int flags, size_t cd_nelmts, const unsigned int cd_valu goto error; compSize = ZSTD_compress(outbuf, compSize, inbuf, origSize, aggression); + if (ZSTD_isError(compSize)) + goto error; #ifdef ZSTD_DEBUG fprintf(stderr, " compressing nbytes: %ld\n", compSize); From aad7cdda1e95da8d6d61fd7c5f1f6893ac509628 Mon Sep 17 00:00:00 2001 From: Stuart Geipel Date: Mon, 17 Aug 2026 14:04:37 -0400 Subject: [PATCH 2/4] Add error reporting with H5Epush This also changes the return type of ZSTD_getFrameContentSize to unsigned long long. --- ZSTD/src/H5Zzstd.c | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/ZSTD/src/H5Zzstd.c b/ZSTD/src/H5Zzstd.c index 4f5270425..02f35f88d 100644 --- a/ZSTD/src/H5Zzstd.c +++ b/ZSTD/src/H5Zzstd.c @@ -22,8 +22,8 @@ static size_t H5Z_filter_zstd(unsigned int flags, size_t cd_nelmts, const unsign #define H5Z_FILTER_ZSTD 32015 -#define PUSH_ERR(func, minor, str) \ - H5Epush(H5E_DEFAULT, __FILE__, func, __LINE__, H5E_ERR_CLS, H5E_PLINE, minor, str) +#define PUSH_ERR(func, minor, ...) \ + H5Epush(H5E_DEFAULT, __FILE__, func, __LINE__, H5E_ERR_CLS, H5E_PLINE, minor, __VA_ARGS__) const H5Z_class2_t H5Z_ZSTD[1] = {{ H5Z_CLASS_T_VERS, /* H5Z_class_t version */ @@ -65,17 +65,33 @@ H5Z_filter_zstd(unsigned int flags, size_t cd_nelmts, const unsigned int cd_valu if (flags & H5Z_FLAG_REVERSE) { /* We're decompressing */ - size_t decompSize = ZSTD_getFrameContentSize(*buf, origSize); - if (decompSize == 0 || decompSize == ZSTD_CONTENTSIZE_UNKNOWN || - decompSize == ZSTD_CONTENTSIZE_ERROR) + unsigned long long contentSize = ZSTD_getFrameContentSize(*buf, origSize); + if (contentSize == ZSTD_CONTENTSIZE_ERROR) { + PUSH_ERR("H5Z_filter_zstd", H5E_CALLBACK, "Input is not a valid zstd frame"); goto error; + } + if (contentSize == ZSTD_CONTENTSIZE_UNKNOWN) { + PUSH_ERR("H5Z_filter_zstd", H5E_CALLBACK, + "zstd frame does not record its decompressed size; the data was likely compressed " + "with the zstd streaming API, which this filter does not support"); + goto error; + } + if (contentSize == 0) { + PUSH_ERR("H5Z_filter_zstd", H5E_CALLBACK, "zstd frame has zero decompressed size"); + goto error; + } - if (NULL == (outbuf = malloc(decompSize))) + if (NULL == (outbuf = malloc((size_t)contentSize))) { + PUSH_ERR("H5Z_filter_zstd", H5E_CALLBACK, "Can't allocate zstd decompression buffer"); goto error; + } - decompSize = ZSTD_decompress(outbuf, decompSize, inbuf, origSize); - if (ZSTD_isError(decompSize)) + size_t decompSize = ZSTD_decompress(outbuf, (size_t)contentSize, inbuf, origSize); + if (ZSTD_isError(decompSize)) { + PUSH_ERR("H5Z_filter_zstd", H5E_CALLBACK, "zstd decompression failed: %s", + ZSTD_getErrorName(decompSize)); goto error; + } #ifdef ZSTD_DEBUG fprintf(stderr, " decompressing nbytes: %ld\n", decompSize); @@ -106,12 +122,17 @@ H5Z_filter_zstd(unsigned int flags, size_t cd_nelmts, const unsigned int cd_valu aggression = ZSTD_maxCLevel(); size_t compSize = ZSTD_compressBound(origSize); - if (NULL == (outbuf = malloc(compSize))) + if (NULL == (outbuf = malloc(compSize))) { + PUSH_ERR("H5Z_filter_zstd", H5E_CALLBACK, "Can't allocate zstd compression buffer"); goto error; + } compSize = ZSTD_compress(outbuf, compSize, inbuf, origSize, aggression); - if (ZSTD_isError(compSize)) + if (ZSTD_isError(compSize)) { + PUSH_ERR("H5Z_filter_zstd", H5E_CALLBACK, "zstd compression failed: %s", + ZSTD_getErrorName(compSize)); goto error; + } #ifdef ZSTD_DEBUG fprintf(stderr, " compressing nbytes: %ld\n", compSize); From 1e7c055dbef50db2de89fa8ae31c72ce5f0775bc Mon Sep 17 00:00:00 2001 From: Stuart Geipel Date: Mon, 17 Aug 2026 14:14:41 -0400 Subject: [PATCH 3/4] Remove variadic macros The rest of the repo shies away from it --- ZSTD/src/H5Zzstd.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ZSTD/src/H5Zzstd.c b/ZSTD/src/H5Zzstd.c index 02f35f88d..401a22e25 100644 --- a/ZSTD/src/H5Zzstd.c +++ b/ZSTD/src/H5Zzstd.c @@ -22,8 +22,10 @@ static size_t H5Z_filter_zstd(unsigned int flags, size_t cd_nelmts, const unsign #define H5Z_FILTER_ZSTD 32015 -#define PUSH_ERR(func, minor, ...) \ - H5Epush(H5E_DEFAULT, __FILE__, func, __LINE__, H5E_ERR_CLS, H5E_PLINE, minor, __VA_ARGS__) +#define PUSH_ERR(func, minor, str) \ + H5Epush(H5E_DEFAULT, __FILE__, func, __LINE__, H5E_ERR_CLS, H5E_PLINE, minor, str) +#define PUSH_ERR2(func, minor, str, arg) \ + H5Epush(H5E_DEFAULT, __FILE__, func, __LINE__, H5E_ERR_CLS, H5E_PLINE, minor, str, arg) const H5Z_class2_t H5Z_ZSTD[1] = {{ H5Z_CLASS_T_VERS, /* H5Z_class_t version */ @@ -88,8 +90,8 @@ H5Z_filter_zstd(unsigned int flags, size_t cd_nelmts, const unsigned int cd_valu size_t decompSize = ZSTD_decompress(outbuf, (size_t)contentSize, inbuf, origSize); if (ZSTD_isError(decompSize)) { - PUSH_ERR("H5Z_filter_zstd", H5E_CALLBACK, "zstd decompression failed: %s", - ZSTD_getErrorName(decompSize)); + PUSH_ERR2("H5Z_filter_zstd", H5E_CALLBACK, "zstd decompression failed: %s", + ZSTD_getErrorName(decompSize)); goto error; } @@ -129,8 +131,8 @@ H5Z_filter_zstd(unsigned int flags, size_t cd_nelmts, const unsigned int cd_valu compSize = ZSTD_compress(outbuf, compSize, inbuf, origSize, aggression); if (ZSTD_isError(compSize)) { - PUSH_ERR("H5Z_filter_zstd", H5E_CALLBACK, "zstd compression failed: %s", - ZSTD_getErrorName(compSize)); + PUSH_ERR2("H5Z_filter_zstd", H5E_CALLBACK, "zstd compression failed: %s", + ZSTD_getErrorName(compSize)); goto error; } From ed4e885a7d8a48afca5110a1361a69c9e2f335bb Mon Sep 17 00:00:00 2001 From: Stuart Geipel Date: Mon, 17 Aug 2026 14:21:37 -0400 Subject: [PATCH 4/4] better message for streaming --- ZSTD/src/H5Zzstd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ZSTD/src/H5Zzstd.c b/ZSTD/src/H5Zzstd.c index 401a22e25..52846435d 100644 --- a/ZSTD/src/H5Zzstd.c +++ b/ZSTD/src/H5Zzstd.c @@ -74,8 +74,8 @@ H5Z_filter_zstd(unsigned int flags, size_t cd_nelmts, const unsigned int cd_valu } if (contentSize == ZSTD_CONTENTSIZE_UNKNOWN) { PUSH_ERR("H5Z_filter_zstd", H5E_CALLBACK, - "zstd frame does not record its decompressed size; the data was likely compressed " - "with the zstd streaming API, which this filter does not support"); + "zstd frame missing decompressed size; data was likely compressed " + "with the zstd streaming API, which is not supported"); goto error; } if (contentSize == 0) {