Describe the bug
_read_strips in xrspatial/geotiff/_reader.py validates StripOffsets / StripByteCounts against n_strips_expected = (height + rps - 1) // rps at line 1410. For PlanarConfiguration=2 (planar/separate bands) the strip tables must hold strips_per_band * samples_per_pixel entries, not just strips_per_band. The pre-flight check is too lenient for planar files.
The planar loop at lines 1436-1441 then handles the missing tail with:
for band_idx in range(samples):
band_offset = band_idx * strips_per_band
for strip_idx in range(first_strip, last_strip + 1):
global_idx = band_offset + strip_idx
if global_idx >= len(offsets):
continue
continue skips strips that should have existed for bands 1..N-1, leaving the corresponding region of the pre-allocated np.empty output untouched. The caller sees garbage uninitialised memory for those bands.
The tiled path already does the right thing through validate_tile_layout() (which knows about planar layout); the strip path should mirror that.
Expected behavior
When PlanarConfiguration == 2 and samples_per_pixel > 1, the pre-flight check should require len(offsets) >= strips_per_band * samples_per_pixel (and the same for byte_counts), and raise a typed ValueError if not. The silent if global_idx >= len(offsets): continue skip inside the loop can then be removed.
Categories
- Cat 1 (memory safety): uninitialised
np.empty exposed to callers
- Cat 5 (backend inconsistency): tile path validates via
validate_tile_layout(); strip path does not
Describe the bug
_read_stripsinxrspatial/geotiff/_reader.pyvalidatesStripOffsets/StripByteCountsagainstn_strips_expected = (height + rps - 1) // rpsat line 1410. ForPlanarConfiguration=2(planar/separate bands) the strip tables must holdstrips_per_band * samples_per_pixelentries, not juststrips_per_band. The pre-flight check is too lenient for planar files.The planar loop at lines 1436-1441 then handles the missing tail with:
continueskips strips that should have existed for bands 1..N-1, leaving the corresponding region of the pre-allocatednp.emptyoutput untouched. The caller sees garbage uninitialised memory for those bands.The tiled path already does the right thing through
validate_tile_layout()(which knows about planar layout); the strip path should mirror that.Expected behavior
When
PlanarConfiguration == 2andsamples_per_pixel > 1, the pre-flight check should requirelen(offsets) >= strips_per_band * samples_per_pixel(and the same forbyte_counts), and raise a typedValueErrorif not. The silentif global_idx >= len(offsets): continueskip inside the loop can then be removed.Categories
np.emptyexposed to callersvalidate_tile_layout(); strip path does not