docs: record the 2026-09-25 HDF5 audit fixes and open gaps
CHANGELOG: upgrade notes (changed read results for max-shape files, saturating conversions, new writer errors, format-crate API changes) and the reader/writer correctness fixes. known-issues: the silent-wrong-data table with before/after sweep numbers, the gaps still open, and a correction to the Extensible Array entry, which said files we wrote were unaffected. CLAUDE.md: clawhdf5-gpu is vector distance computation, not I/O, and clawhdf5-filters holds only deflate backends (no Blosc). Also a facade test that libhdf5's 20-bit N-Bit float test data reads as libhdf5's values. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
+97
-1
@@ -7,6 +7,99 @@ deleting it.
|
||||
|
||||
---
|
||||
|
||||
## Silent wrong data found by the 2026-09-25 HDF5 audit
|
||||
|
||||
**Status:** fixed after v2.7.0 (2026-09-25). **Every release up
|
||||
to and including v2.7.0 is affected.**
|
||||
|
||||
An audit on tank checked clawhdf5 against libhdf5 in three ways:
|
||||
- a sweep of 686 public files: the libhdf5 test files, the HDF Group's
|
||||
`cve_hdf5` reproducers, and the pyfive, netcdf-c, netcdf4-python, h5wasm,
|
||||
h5py and xarray corpora;
|
||||
- 567 read cases generated with h5py 3.16 / HDF5 2.0;
|
||||
- 96 write cases checked with h5py builds linking HDF5 1.10, 1.12, 1.14 and
|
||||
2.0, plus h5dump 1.14.6.
|
||||
|
||||
It found these cases where a value came back wrong **without an error**:
|
||||
|
||||
| Area | What happened | Who is affected |
|
||||
|---|---|---|
|
||||
| Chunk index (read) | Fixed/Extensible Array indexes laid out by the current shape, not the max shape: chunks returned from the wrong place | any file with a max shape larger than its shape and `libver='latest'` (h5py `maxshape=(10, None)`, `(20, 10)`) |
|
||||
| Chunk index (write) | Extensible Array chunks from index 244 on never indexed (read as 0); unlimited dimension not first: data scrambled | files we wrote with one unlimited dimension and > 244 chunks, or e.g. `maxshape=(20, None)` |
|
||||
| 4-byte offsets | unfiltered chunked datasets read as zeros | files created with `sizeof_addr = 4` |
|
||||
| Filter mask | any skipped filter skipped the whole pipeline | files with partially filtered chunks (optional filters, direct chunk writes) |
|
||||
| Numeric reads | float read as integer returned the bit pattern; narrowing integer reads kept the low bits; bfloat16 decoded as IEEE half | `read_i32`/`read_i64`/`read_u64` callers on float or wider data; HDF5 2.0 bf16 data |
|
||||
| SZIP | garbage or zeros | every libhdf5-written SZIP dataset |
|
||||
| Scale-offset | float values 1 ULP off | libhdf5 D-scale float data |
|
||||
| Shared fill value | read as zero fill | fill values stored as shared messages |
|
||||
| VL sequences | `read_vl_bytes` truncated non-byte base types | VL int/float sequences |
|
||||
| Chunk cache | two threads reading two chunked datasets through one `File` could get each other's chunks | multi-threaded readers, including Python with the GIL released |
|
||||
|
||||
The audit also found files we wrote that libhdf5 **refuses**, now fixed:
|
||||
- Fixed Array datasets with more than 1 024 chunks.
|
||||
- Header messages over 64 KiB (large attributes).
|
||||
- Reference, Opaque, BitField and Time datatypes.
|
||||
- Files written with `with_page_size`.
|
||||
- Several unlimited dimensions.
|
||||
- A finite max shape larger than the shape.
|
||||
- An empty-string attribute, which broke every attribute on its object.
|
||||
- `FillTime` codes, which were rotated.
|
||||
|
||||
Our LZ4 and Zstd output could not be read by libhdf5's registered plugins, and
|
||||
our pcodec filter used Granular BitRound's ID. The details are in
|
||||
`CHANGELOG.md` under Correctness and Interop.
|
||||
|
||||
Before the fix, 419 of the 686 files read correctly and 43 differed from h5py.
|
||||
After it, 448 read correctly and 23 differ. Of those 23:
|
||||
- 17 are N-Bit float files. The probe compares raw file-type bytes; the typed
|
||||
reader returns libhdf5's values (`nbit_custom_float_decodes_like_libhdf5`).
|
||||
- 2 are an h5py bug: VL data with a big-endian base type comes back
|
||||
byte-swapped in h5py, and h5dump agrees with us.
|
||||
- The rest are object or attribute listing differences.
|
||||
|
||||
There were no panics, hangs or crashes before or after, including on all 147
|
||||
CVE and fuzzer files. On some of those files, h5dump 1.14.6 and h5py/HDF5 2.0
|
||||
segfault or abort.
|
||||
|
||||
## Gaps found by the 2026-09-25 HDF5 audit (open)
|
||||
|
||||
**Status:** open. These fail with an error; none returns wrong data, except
|
||||
the VDS item, which is marked.
|
||||
|
||||
- **Layout message versions 1 and 2** (HDF5 1.6-era files): 84 of the 686
|
||||
sweep files, `InvalidLayoutVersion`. This is the largest single gap.
|
||||
- **Virtual datasets:**
|
||||
- **Wrong data:** unmapped regions read as 0 instead of the fill value.
|
||||
- `%b` printf-style source names are not expanded.
|
||||
- Hyperslab selection versions 1 and 2 are refused.
|
||||
- **Files with a user block:** the base address is not applied.
|
||||
- **Old-style shared messages (version 1)** read the wrong address.
|
||||
- **Groups and links:**
|
||||
- Groups with a user-defined link type (e.g. 187) cannot be listed.
|
||||
- Dense groups with more than about 22 000 links cannot be listed.
|
||||
- Soft links are left out of `datasets()`.
|
||||
- **Dense attributes:** a large attribute stored as a fractal-heap "huge"
|
||||
object makes every attribute on the object fail. This affects real NetCDF
|
||||
files (`issue671.nc`).
|
||||
- **Other readers:**
|
||||
- VL-string datasets are not readable through `File`.
|
||||
- Metadata cache images are not supported.
|
||||
- x87 long double and binary128 are refused.
|
||||
- N-Bit on 64-bit scale-offset data and some N-Bit parameter layouts fail.
|
||||
- **Filters:** blosc, blosc2, bitshuffle, bzip2, LZF and zfp are not
|
||||
implemented.
|
||||
- **Header checks:** on 12 CVE datasets libhdf5 rejects a corrupt header and
|
||||
we read data anyway. We need stricter header checks.
|
||||
- **Writer:**
|
||||
- Nested groups beyond one level: path-like names are now refused, not
|
||||
created.
|
||||
- Dense attribute storage for attributes over 64 KiB.
|
||||
- Output that HDF5 1.8 can read.
|
||||
- A B-tree v2 chunk index larger than one leaf, so datasets with several
|
||||
unlimited dimensions are limited to 65 535 chunks.
|
||||
|
||||
---
|
||||
|
||||
## Compound datatype message version 5 is not parsed (HDF5 2.0)
|
||||
|
||||
**Status:** fixed on `main` in `a13ff51` (2026-06-03); **not in the v2.1.0
|
||||
@@ -226,7 +319,10 @@ block-offset field in the super block, and a page-init bitmap read from the
|
||||
wrong structure. All four are fixed and covered by interop tests against
|
||||
HDF5 2.0 at sizes that cross each boundary, including paged data blocks.
|
||||
|
||||
Files written by this crate are unaffected — this was purely a read-path bug.
|
||||
Files written by this crate were not affected by *this* read bug, but the
|
||||
writer had its own: it indexed only the first 244 chunks, so later chunks
|
||||
read back as 0 in libhdf5 and in clawhdf5. See "Silent wrong data found by
|
||||
the 2026-09-25 HDF5 audit" below.
|
||||
|
||||
## Every `f32` dataset we wrote was unreadable by h5py / libhdf5
|
||||
|
||||
|
||||
Reference in New Issue
Block a user