You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

NetCDF CDF-5 Attribute Array Integer Overflow in Header Parsing

Summary

libsrc/v1hpg.c allocates the global/variable attribute pointer array using untrusted nelems from the file header without an overflow guard:

ncap->value = (NC_attr **) malloc(ncap->nelems * sizeof(NC_attr *));

In CDF-5, nelems is read from the attacker-controlled header as a 64-bit integer. On 32-bit builds, ncap->nelems * sizeof(NC_attr *) can wrap to a small allocation, after which the parser iterates ncap->nelems times and writes past the undersized heap buffer while parsing attributes. This is reachable when opening a malicious .nc file through libnetcdf consumers such as Python netCDF4.Dataset().

Affected Code

File: libsrc/v1hpg.c:894

/* Read a NC_attrarray from the header */
static int
v1h_get_NC_attrarray(v1hs *gsp, NC_attrarray *ncap)
{
    ...
    status = v1h_get_size_t(gsp, &ncap->nelems);
    if(status != NC_NOERR)
        return status;

    if(ncap->nelems == 0)
        return NC_NOERR;
    /* else */
    if(type != NC_ATTRIBUTE)
        return EINVAL;

    ncap->value = (NC_attr **) malloc(ncap->nelems * sizeof(NC_attr *));
    if(ncap->value == NULL)
        return NC_ENOMEM;
    ncap->nalloc = ncap->nelems;
    ...
}

Missing Guard vs Existing Hardening

The same file already contains equivalent overflow protection for dimensions and variables, but not for attributes.

Dimension array: guarded

File: libsrc/v1hpg.c:544-546

if (ncap->nelems > SIZE_MAX / sizeof(NC_dim *))
    return NC_ERANGE;
ncap->value = (NC_dim **) calloc(1,ncap->nelems * sizeof(NC_dim *));

Variable array: guarded

File: libsrc/v1hpg.c:1198-1200

if (ncap->nelems > SIZE_MAX / sizeof(NC_var *))
    return NC_ERANGE;
ncap->value = (NC_var **) calloc(1,ncap->nelems * sizeof(NC_var *));

Attribute array: unguarded

File: libsrc/v1hpg.c:894

ncap->value = (NC_attr **) malloc(ncap->nelems * sizeof(NC_attr *));

This is an inconsistent omission in otherwise similar parser code.

Root Cause

v1h_get_NC_attrarray() trusts the header field nelems and uses it directly in a multiplication for heap allocation. The classic/CDF-5 file format grammar allows the parser to reach the attribute list before any attribute bodies are parsed:

header = magic numrecs dim_list gatt_list var_list
att_list = ABSENT | NC_ATTRIBUTE nelems [attr ...]

As soon as the parser reads NC_ATTRIBUTE and a large nelems, it performs the unchecked multiplication.

Exploitability

32-bit builds

On 32-bit, a crafted value such as nelems = 0xFFFFFFFF causes:

  • 0xFFFFFFFF * 4 or 0xFFFFFFFF * 8 style arithmetic to wrap (depending on pointer size / ABI)
  • malloc() to return a much smaller chunk than required
  • the subsequent loop to treat the buffer as if it contains ncap->nelems entries
  • heap out-of-bounds writes during attribute parsing

The write primitive is reached in the loop immediately after allocation:

NC_attr **app = ncap->value;
NC_attr *const *const end = &app[ncap->nelems];
for( /*NADA*/; app < end; app++)
{
    status = v1h_get_NC_attr(gsp, app);
    ...
}

64-bit builds

On 64-bit, the same value typically becomes a massive allocation request instead of a wrapped small allocation. For the verified PoC value 0xFFFFFFFF:

  • malloc(0xFFFFFFFF * 8) requests ~32 GiB
  • libnetcdf returns NC_ENOMEM
  • Python netCDF4.Dataset() surfaces this as OSError: [Errno -61] NetCDF: Memory allocation (malloc) failure when the encoded count exceeds SIZE_MAX / sizeof(NC_attr *) on the running architecture.

This demonstrates reliable attacker-triggered denial of service on common 64-bit targets, while the unchecked arithmetic remains memory-corruption-relevant on 32-bit targets.

Reachability

This bug is reachable through applications that open attacker-supplied classic/CDF-5 .nc files using libnetcdf, including Python bindings that bundle/liblink netcdf-c:

import netCDF4
netCDF4.Dataset('poc.nc')

Verified result with the attached PoC:

OSError: [Errno -61] NetCDF: Memory allocation (malloc) failure

Reproduction

  1. Generate the malicious file with the attached poc.py
  2. Open it with Python netCDF4.Dataset()
  3. Observe libnetcdf fail in header parsing before any normal dataset access occurs

The PoC defaults to a CDF-5 global attribute count of 0x1FFFFFFFFFFFFFFF so current 64-bit builds also hit the allocation failure path. You can pass --attr-count 0xFFFFFFFF to mirror the originally verified 32-bit-relevant wraparound value.

Security Impact

  • 32-bit: integer overflow in allocation size -> undersized heap allocation -> heap overflow during attribute parsing
  • 64-bit: attacker-triggered excessive allocation / memory exhaustion during file open
  • Attack surface: any service, desktop app, pipeline, or notebook that opens untrusted .nc files through libnetcdf/netCDF4

Known CVE Non-Overlap

I checked the 2025 netcdf-c CVEs you referenced:

  • CVE-2025-14932
  • CVE-2025-14933
  • CVE-2025-14934
  • CVE-2025-14935
  • CVE-2025-14936

None of them cover this bug class. In particular, CVE-2025-14936 concerns an attribute name stack overflow, not an attribute array allocation integer overflow in v1h_get_NC_attrarray().

Suggested Fix

Mirror the existing dimension/variable protections in the attribute path:

if (ncap->nelems > SIZE_MAX / sizeof(NC_attr *))
    return NC_ERANGE;
ncap->value = (NC_attr **) calloc(1, ncap->nelems * sizeof(NC_attr *));

At minimum, the SIZE_MAX / sizeof(NC_attr *) bound check is required before allocation.

PoC

See poc.py in the same directory.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support