pantumanolo/netcdf_fpe_dos_poc
netCDF4 / libnetcdf NCvarshape SIGFPE divide-by-zero (CWE-369)
1. Summary
Opening a 184-byte crafted classic NetCDF file with netCDF4.Dataset triggers an integer divide-by-zero in the bundled libnetcdf and the process dies with SIGFPE. The crash is an uncatchable hardware exception, not a Python error, so the caller cannot guard it with try/except. Denial of service on the open path.
2. Affected product and version
- Package: official
netCDF4(PyPI). Confirmed on netCDF4 1.7.4, the current PyPI release at the time of testing, which bundles libnetcdf 4.9.3. - Fixed upstream in netcdf-c 4.10.0 (PR 3191, commit 2ec6c18b), not yet shipped in a netCDF4 wheel.
3. Vulnerability class
CWE-369 divide by zero, with a secondary CWE-190 signed integer overflow as the root cause. Native code in the bundled libnetcdf.
4. Reachability
The only action is opening the file. netCDF4.Dataset(path) reaches the crash with no further calls:
netCDF4.Dataset(path) -> nc_open -> NC3_open -> nc_get_NC -> NC_var_shape (divide by zero)The crash happens during open, before any data is read.
5. Root cause
NC_var_shape in libsrc/var.c multiplies each variable's dimension sizes into a signed off_t accumulator product, guarded by OFF_T_MAX / product to detect overflow. With 64-bit CDF-5 dimension sizes, the signed multiply can reach exactly zero, and the next iteration divides OFF_T_MAX by product == 0. The idiv instruction with a zero divisor raises SIGFPE.
6. Trigger
The 184-byte PoC declares three dimensions of size 1, 2**63, 2, and one fixed-size variable using all three. The product walk: 1 * 2 = 2, then 2 * 2**63 = 2**64, which is 0 in 64-bit, then OFF_T_MAX / 0.
7. Reproduction on the released wheel
python -m venv venv-netcdf
venv-netcdf/bin/pip install netCDF4==1.7.4
venv-netcdf/bin/python poc.py
# [+] libnetcdf: 4.9.3 ...
# [+] netCDF4 : 1.7.4
# Floating point exception (core dumped) -> exit code 136Debugger on the stock wheel (EVIDENCE_wheel_gdb.txt):
Thread 1 "python" received signal SIGFPE, Arithmetic exception.
0x...c4 in NC_var_shape () from .../netcdf4.libs/libnetcdf-...so.22
#0 NC_var_shape #1 nc_get_NC #2 NC3_open #4 nc_open
#5 __pyx_pf_7netCDF4_8_netCDF4_7Dataset___init__ ... src/netCDF4/_netCDF4.c
=> 0x...c4 <NC_var_shape+516>: idiv %rsiPlatform: Ubuntu 24.04.4 LTS, x86-64, Python 3.12.3, netCDF4 1.7.4 wheel.
8. Root cause on an instrumented build
UBSan on a classic-only build of libnetcdf 4.9.3 (EVIDENCE_ubsan.txt):
var.c:482:23: runtime error: signed integer overflow:
-9223372036854775808 * 2 cannot be represented in type 'long int'
#0 NC_var_shape libsrc/var.c:482
#1 NC_computeshapes libsrc/v1hpg.c:1256
#2 nc_get_NC libsrc/v1hpg.c:1559
#3 NC3_open libsrc/nc3internal.c:1198
#4 NC_open / nc_open libdispatch/dfile.cThis isolates the overflowing multiply at var.c:482 that drives product to zero before the division.
9. Expected versus actual
Expected: opening a file with an inconsistent dimension product fails with a clean error (status code or Python exception). Actual: the process receives an uncatchable SIGFPE and dies.
10. Impact
Denial of service. A 184-byte file crashes the interpreter of any application that opens attacker-supplied NetCDF input through netCDF4.Dataset or nc_open. Because SIGFPE is a hardware signal and not a Python exception, the application cannot catch and recover from it. Not memory corruption, not code execution. Severity Medium.
11. Suggested fix
Validate that the shape product is greater than zero before the division and return an error instead of dividing (this is exactly what netcdf-c 4.10.0 does: if (product <= 0) return NC_ERANGE;). At the consumer level, the netCDF4 wheel should bundle libnetcdf 4.10.0 or later.
12. Duplicate note
CVE-2025-14934 is a different bug (a variable-name stack overflow via ZDI), not this divide-by-zero. No CVE, advisory, or public issue found describing this NC_var_shape SIGFPE; the upstream fix is referenced only by an internal ticket. Confirm against current pending and resolved reports.
Files
poc.py— buildspoc_fpe_cdf5.ncand opens it; the process dies with SIGFPE (exit 136). Self-contained, prints netCDF4 and libnetcdf versions.gen_fpe.py— generator helperpoc_fpe_cdf5.nc— the 184-byte malicious fileEVIDENCE_wheel_gdb.txt,EVIDENCE_ubsan.txt— debugger and UBSan evidence
