Skip to content

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

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

Reject TIFFs whose declared tile grid exceeds TileOffsets length (#1219)#1221
brendancol merged 2 commits into
masterfrom
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
@brendancol brendancol deleted the issue-1219 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.

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

2 participants