Repository navigation
Description
Summary
With the netCDF4 wheels 1.7.4 and 1.7.5.1.post1, opening a netCDF-4 file
crashes the process with SIGSEGV after this sequence in one process:
- open handle A on the file and keep it open;
- open handle B on the same file;
- through B, read a variable-length string variable whose HDF5 fill
value is a string and whose_FillValueattribute is a vlen string; - close B;
- open the file again: SIGSEGV during
netCDF4.Dataset.__init__.
Other orders tested do not crash, for example: B reads and is not
closed; A reads instead of B; A is closed instead of B; no handle is
left open at step 5. Only the netCDF4 extension is loaded in the
crashing process (h5py is used only to write the file, in another
process).
We met it with Sentinel-5P Level-2 products (variable
PRODUCT/time_utc), opened through xarray's netcdf4 engine (which
closes and reopens its handle) while another netCDF4.Dataset was open
on the same file.
Environment
| netCDF4 wheel | netCDF-C | bundled HDF5 | Result |
|---|---|---|---|
| 1.7.5.1.post1 | 4.10.1 | 2.2.0 | SIGSEGV (return code -11) |
| 1.7.4 | 4.9.3 | 1.14.6 | SIGSEGV (return code -11) |
| 1.7.2 | 4.9.4-development | 1.14.2 | no crash |
Common: Linux x86_64 (kernel 7.0, glibc 2.43), CPython 3.12.13,
numpy 2.5.4, PyPI manylinux wheels. Writer of the test file:
h5py 3.16.0 (HDF5 2.0.0).
Reproducer (9 kB file)
"""Reproducer: SIGSEGV in libhdf5 when reopening a netCDF-4 file."""
import os
import subprocess
import sys
import tempfile
import h5py
import numpy
path = os.path.join(tempfile.mkdtemp(), 'vlen.nc')
text = h5py.string_dtype('utf-8')
with h5py.File(path, 'w') as f:
var = f.create_group('PRODUCT').create_dataset(
'time_utc', shape=(1, 4), dtype=text, fillvalue=b'')
var[0, :] = [f'2021-03-24T11:30:2{i}Z' for i in range(4)]
var.attrs.create('_FillValue', numpy.array([''], dtype=object),
dtype=text)
# Run in a child process so that the crash shows as a return code.
child = f'''
import netCDF4
a = netCDF4.Dataset({path!r}) # handle A, stays open
b = netCDF4.Dataset({path!r}) # handle B
b['PRODUCT'].variables['time_utc'][...] # B reads the vlen string variable
b.close()
c = netCDF4.Dataset({path!r}) # SIGSEGV here
print('no crash')
'''
proc = subprocess.run([sys.executable, '-X', 'faulthandler', '-c', child])
print('return code:', proc.returncode) # -11 on affected versionsOutput on 1.7.4 and 1.7.5.1.post1: return code: -11 (with
Fatal Python error: Segmentation fault from faulthandler).
Output on 1.7.2: no crash / return code: 0.
The crash needs both the HDF5 fill value b'' on the dataset and the
vlen string _FillValue attribute: with only one of the two, there is
no crash.
Native stack (gdb, netCDF4 1.7.5.1.post1)
#0 H5F_addr_decode () libhdf5-34cf00cd.so.320.2.0
#1 H5VL__native_blob_specific () libhdf5
#2 H5VL_blob_specific () libhdf5
#3 H5T__vlen_disk_isnull () libhdf5
#4 H5T__conv_vlen () libhdf5
#5 H5T_convert_with_ctx () libhdf5
#6 H5T_convert () libhdf5
#7 H5D_get_create_plist () libhdf5
#8 H5VL__native_dataset_get () libhdf5
#9 H5VL_dataset_get () libhdf5
#10 H5Dget_create_plist () libhdf5
#11 nc4_get_var_meta () libnetcdf-3622e713.so.22
#12 nc4_hdf5_find_grp_var_att () libnetcdf
#13 NC4_HDF5_inq_var_all () libnetcdf
#14 nc_inq_var () libnetcdf
#15 nc_inq_varname () libnetcdf
#16 __pyx_f_7netCDF4_8_netCDF4__get_vars _netCDF4.c:19659
#17 netCDF4._netCDF4.Group.__init__ _netCDF4.c:42799
#23 __pyx_f_7netCDF4_8_netCDF4__get_grps _netCDF4.c:18983
#24 netCDF4._netCDF4.Dataset.__init__ _netCDF4.c:24689
The crash is in the conversion of the vlen fill value, read when
netCDF-C fetches the dataset creation property list at open.
What is not known
- Not bisected between 1.7.2 and 1.7.4: the first affected wheel, and
whether the change is in HDF5 (1.14.2 -> 1.14.6) or in netCDF-C
(4.9.x), is unknown. A test against a plain netCDF-C / HDF5 build
(without the Python wheel) has not been done. - The internal mechanism is not verified. Our guess, from the stack and
the sequence: HDF5 shares one underlying file between the open
handles, and data cached while B read the vlen strings still points to
B's file object after B is closed; the next open uses that freed
pointer. - Not tested on other platforms (macOS, Windows) or with conda builds.
Workaround used
In our application, every xarray dataset built on a given file shares one
netCDF4.Dataset handle that is closed only when the owning object is
closed, so the file is never reopened while another handle on it is open.
Activity
jswhit commentedon Oct 29, 2026
This may be due to h5py and netcdf4-python using different versions of the hdf5 library (this has been the source of many bug reports in the past). What happens if you import netCDF4 before h5py?
pidancier commentedon Oct 31, 2026
Thanks,
Thanks. h5py is not involved in the crashing process. In the reproducer, h5py only writes the file in a separate process; importing netCDF4 before h5py in the reader makes no difference (still SIGSEGV with 1.7.5.1.post1).
More to the point, the same sequence crashes on an original Sentinel-5P L2 product (S5P_OFFL_L2__NO2____20200201T112236_..._010302_20200204T163837.nc, variable PRODUCT/time_utc), with no h5py anywhere: only numpy, cftime and netCDF4 are loaded. Same native stack (H5F_addr_decode ← H5T__conv_vlen ← H5D_get_create_plist ← nc4_get_var_meta).