Skip to content

perlin() silently corrupts int-dtyped input rasters (all pixels -> INT_MIN) #1232

@brendancol

Description

@brendancol

Describe the bug

perlin() returns a fully corrupted array when the input DataArray has an integer dtype. Every pixel ends up as INT_MIN (-2147483648 for int32).

_validate_raster() accepts integer dtypes, but all four perlin backends write float noise back into the user's buffer in place and then normalize by ptp. With an integer buffer the float noise (values in [-1, 1]) casts to 0, so ptp is 0, and (data - min) / ptp divides by zero. The NaN/Inf then casts back to int32 as INT_MIN.

Reproduction

import numpy as np
import xarray as xr
from xrspatial import perlin

data = np.zeros((20, 20), dtype=np.int32)
raster = xr.DataArray(data, dims=['y', 'x'])
result = perlin(raster)
print(result.data.min(), result.data.max())
# -2147483648 -2147483648

A RuntimeWarning fires but the corrupted result is still returned, so a caller who isn't watching warnings has no signal that anything went wrong.

Expected behavior

perlin() should raise ValueError on a non-floating-point DataArray. It's a generator that overwrites the raster in place, so integer storage doesn't make sense for it.

Affected backends

_perlin_numpy, _perlin_cupy, _perlin_dask_numpy, and _perlin_dask_cupy all use the same write-in-place-then-normalize pattern, so all four are affected.

Proposed fix

After _validate_raster(...) in perlin(), check agg.dtype and raise ValueError if it isn't floating-point.

Follow-up (not fixed here)

Even with float input, degenerate cases like freq=(0, 0) produce constant noise, which makes ptp zero and emits NaN through the same normalization. A zero-ptp guard would close that gap; tracking it separately.

Context

Found during the perlin security sweep on 2026-04-22 - Category 6 (Dtype Confusion), silent wrong results.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions