Skip to content

Guard min_observable_height() against unbounded boolean stack (#1317)#1320

Merged
brendancol merged 1 commit into
mainfrom
issue-1317
Apr 29, 2026
Merged

Guard min_observable_height() against unbounded boolean stack (#1317)#1320
brendancol merged 1 commit into
mainfrom
issue-1317

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Summary

  • Adds _available_memory_bytes() and _check_memory(n_steps, rows, cols) to min_observable_height() (1 byte/cell for the bool stack, 50% threshold).
  • Guard runs after parameter validation but before np.empty((n_steps, H, W), bool).
  • Records the audit result for experimental in .claude/sweep-security-state.csv (separate commit on main).

Fixes #1317.

Background

The visibility stack at line 122 was sized directly from raster.shape and n_steps = ceil(log2(max_height / precision)) + 1. Default args produce n_steps = 8, but a caller can drive it much higher: max_height=1e9, precision=1e-9 gives n_steps = 60. On a 50000x50000 raster that is ~150 GB of bool stack before anything errors.

Same asymmetric guard pattern used in sky_view_factor (#1300), sieve (#1296), kde (#1287), resample (#1295), focal (#1284), geodesic (#1283), mahalanobis (#1288), true_color (#1291), diffuse (#1267), erode (#1275), emerging_hotspots (#1274), dasymetric (#1261).

Test plan

  • pytest xrspatial/tests/test_min_observable_height.py -- 15 passed (4 new memory-guard cases, 11 existing)
  • Oversize stack on numpy raises MemoryError with a clear message
  • Error message includes raster dimensions
  • Normal-size input still succeeds with ample memory
  • Invalid max_height raises ValueError before the memory guard runs

`np.empty((n_steps, H, W), bool)` was sized directly from raster
dimensions and the binary-search step count with no memory check.
n_steps = ceil(log2(max_height/precision)) + 1, so a caller can drive
the stack to ~150 GB on a 50000x50000 raster with extreme parameters.

Adds `_available_memory_bytes()` and `_check_memory(n_steps, rows, cols)`
helpers (1 byte/cell for bool, 50% threshold) and calls the guard after
parameter validation but before the np.empty allocation.

Same asymmetric guard pattern used in sky_view_factor (#1300), sieve
(#1296), kde (#1287), resample (#1295), focal (#1284), geodesic (#1283),
mahalanobis (#1288), true_color (#1291), diffuse (#1267), erode (#1275),
emerging_hotspots (#1274), dasymetric (#1261).

Tests cover oversize-stack rejection, error-message dimensions, normal
sizing with ample memory, and that ValueError validation still runs
before the memory guard.
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label Apr 29, 2026
@brendancol brendancol merged commit 6bff131 into main Apr 29, 2026
10 of 11 checks passed
@brendancol brendancol deleted the issue-1317 branch May 4, 2026 13:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

min_observable_height(): no memory guard on boolean visibility stack

1 participant