Skip to content

Reject TIFFs whose declared tile grid exceeds TileOffsets length (#1219)#1221

Merged
brendancol merged 2 commits intomasterfrom
issue-1219
Apr 20, 2026
Merged

Reject TIFFs whose declared tile grid exceeds TileOffsets length (#1219)#1221
brendancol merged 2 commits intomasterfrom
issue-1219

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Summary

A TIFF can declare ImageWidth / ImageLength and TileWidth / TileLength that imply tiles_across * tiles_down = N tiles while its TileOffsets tag has fewer than N entries. The GPU _assemble_tiles_kernel computes tile_idx = tile_row * tiles_across + tile_col from the output pixel position and reads tile_out_offsets[tile_idx] past the end of the device buffer. The CPU path silently skips the missing tiles (if tile_idx >= len(offsets): continue in _read_tiles), returning a zero-padded raster with no error.

Fix

Fail fast in the header layer. A new validate_tile_layout(ifd) in _header.py checks that len(tile_offsets) >= tiles_across * tiles_down (times samples_per_pixel when planar config is 2) and raises ValueError with the mismatch. It's called from:

  • _read_tiles (the windowed-read path)
  • _read_cog_http (the HTTP COG path)
  • open_geotiff_gpu in geotiff/__init__.py before the GPU dispatch

Test plan

TestTileLayoutValidation in xrspatial/geotiff/tests/test_security.py:

  • validate_tile_layout raises when TileOffsets count is smaller than the declared grid
  • validate_tile_layout accepts a well-formed tiled TIFF
  • validate_tile_layout is a no-op for stripped TIFFs
  • _read_tiles raises ValueError instead of silently zero-padding
  • read_to_array end-to-end raises ValueError on the malformed input
  • Exact-count TIFFs still read correctly

The helper _make_short_offsets_tiff builds a normal tiled TIFF and rewrites the TileOffsets IFD entry to advertise a smaller count without truncating the data blob, so the resulting file passes every earlier check.

Full geotiff suite: 441 passed, 4 skipped. 3 pre-existing matplotlib-deepcopy failures in TestPalette reproduce on master.

Closes #1219. Related to #1215.

A TIFF can declare image dimensions that imply more tiles than its
TileOffsets tag supplies. The GPU _assemble_tiles_kernel reads
tile_out_offsets[tile_idx] where tile_idx is computed from the output
pixel position, so threads whose pixel maps past the supplied count
perform out-of-bounds device reads. The CPU _read_tiles loop silently
skips those tiles with `if tile_idx >= len(offsets): continue` and
returns a zero-padded raster.

Add validate_tile_layout(ifd) in _header.py and call it from _read_tiles,
_read_cog_http, and open_geotiff_gpu. Raises ValueError with the mismatch
count before any decode work runs.
@github-actions github-actions bot added the performance PR touches performance-sensitive code label Apr 19, 2026
@brendancol
Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Co-authored-by: brendancol <433221+brendancol@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 19, 2026

@copilot resolve the merge conflicts in this pull request

Merge conflicts resolved in f40dd67. The only conflict was in .claude/sweep-security-state.json where both branches modified the geotiff entry — our branch added followup_issues: [1219] and master added a notes field. The resolution combines both changes: followup_issues: [1219, 1220] plus the notes from master. All 25 security tests still pass.

@brendancol brendancol merged commit 8a57907 into master Apr 20, 2026
11 checks passed
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.

Security: OOB GPU read when TIFF tile grid exceeds TileOffsets length

2 participants