Skip to content

zonal.stats: unbounded allocation with return_type='xarray.DataArray' #2523

@brendancol

Description

@brendancol

Security finding (HIGH, Cat 1 — Unbounded Allocation / DoS)

xrspatial.zonal._stats_numpy allocates an N-stat result array sized by user-controlled dimensions without a memory check:

https://github.com/makepath/xarray-spatial-contrib/blob/main/xrspatial/zonal.py#L505

else:
    result = np.full((len(stats_funcs), values.size), np.nan)

This branch runs whenever the public stats(..., return_type='xarray.DataArray') is called. Memory required is n_stats * H * W * 8 bytes (float64), with n_stats user-controlled via the stats_funcs dict. For the documented default 8 stats on a 20000x20000 input the working buffer is ~25.6 GB; with a larger custom stats_funcs dict and/or input it scales linearly with both.

This mirrors the unbounded-allocation pattern already fixed in cost_distance (#1262), mahalanobis (#1288), kde (#1287), multispectral (#1291), sieve (#1296), resample (#1295), and several other modules.

Reproducer

import numpy as np
import xarray as xr
from xrspatial.zonal import stats

# Allocates n_stats * H * W * 8 bytes with no guard
zones = xr.DataArray(np.zeros((20000, 20000), dtype=np.int32))
values = xr.DataArray(np.zeros((20000, 20000), dtype=np.float32))
out = stats(zones=zones, values=values, return_type='xarray.DataArray')
# Working buffer >= 25 GB on top of the input rasters

Proposed fix

Add an _available_memory_bytes() / _check_memory(n_stats, h, w) helper to zonal.py and call it from _stats_numpy before the np.full allocation when return_type == 'xarray.DataArray'. Mirrors the established pattern (raise MemoryError with a clear message when n_stats * H * W * 8 exceeds 50% of available RAM). Dask / cupy paths return a DataFrame and are not affected; the cupy path already raises TypeError for 3D input.

Found by deep-sweep-security-zonal-2026-05-27 (carry-over MEDIUM from 2026-04-22 audit, re-classified HIGH given the same severity bar used for the related per-module unbounded-allocation fixes).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions