Team Ai
Modelpublic

pantumanolo/netcdf_fpe_dos_poc

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
Model Card

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 136

Debugger 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  %rsi

Platform: 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.c

This 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 — builds poc_fpe_cdf5.nc and opens it; the process dies with SIGFPE (exit 136). Self-contained, prints netCDF4 and libnetcdf versions.
  • —gen_fpe.py — generator helper
  • —poc_fpe_cdf5.nc — the 184-byte malicious file
  • —EVIDENCE_wheel_gdb.txt, EVIDENCE_ubsan.txt — debugger and UBSan evidence