Summary
parse_all_ifds in xrspatial/geotiff/_header.py silently returns a truncated IFD list when the IFD chain in a TIFF file forms a cycle. The function already raises ValueError for two other malformed-chain conditions:
- offset past EOF (
_header.py:833)
- chain exceeds
MAX_IFDS (_header.py:844)
A cyclic chain is just as broken, but the current loop condition exits without complaint:
while offset != 0 and offset not in seen: # _header.py:830
seen.add(offset)
...
The caller gets back whatever was parsed before the cycle. That may look like a plausible first IFD while overview, mask, or metadata chains further down are corrupt.
Reproduction
import struct
from xrspatial.geotiff._header import parse_header, parse_all_ifds, TAG_IMAGE_WIDTH
out = bytearray()
out.extend(b'II'); out.extend(struct.pack('<H', 42)); out.extend(struct.pack('<I', 8))
# IFD A at offset 8 -> IFD B at offset 26
out.extend(struct.pack('<H', 1)); out.extend(struct.pack('<HHI', TAG_IMAGE_WIDTH, 4, 1))
out.extend(struct.pack('<I', 1)); out.extend(struct.pack('<I', 26))
# IFD B -> back to IFD A (cycle)
out.extend(struct.pack('<H', 1)); out.extend(struct.pack('<HHI', TAG_IMAGE_WIDTH, 4, 1))
out.extend(struct.pack('<I', 2)); out.extend(struct.pack('<I', 8))
data = bytes(out)
header = parse_header(data)
ifds = parse_all_ifds(data, header)
print(len(ifds)) # 2 -- no error raised
Fix
Restructure the loop so a repeat offset raises ValueError with the same file is malformed wording as the existing sibling errors:
while offset != 0:
if offset in seen:
raise ValueError(
f"TIFF IFD chain has a cycle at offset {offset}; "
f"file is malformed"
)
seen.add(offset)
...
Severity: low (defensive hardening / consistency).
Summary
parse_all_ifdsinxrspatial/geotiff/_header.pysilently returns a truncated IFD list when the IFD chain in a TIFF file forms a cycle. The function already raisesValueErrorfor two other malformed-chain conditions:_header.py:833)MAX_IFDS(_header.py:844)A cyclic chain is just as broken, but the current loop condition exits without complaint:
The caller gets back whatever was parsed before the cycle. That may look like a plausible first IFD while overview, mask, or metadata chains further down are corrupt.
Reproduction
Fix
Restructure the loop so a repeat offset raises
ValueErrorwith the samefile is malformedwording as the existing sibling errors:Severity: low (defensive hardening / consistency).