safer exif decode

This commit is contained in:
Ryan Oldenburg 2023-05-28 23:49:15 -05:00
parent dc69535191
commit 4b7a9d1bb4

View file

@ -380,15 +380,16 @@ proc decodeSOF2(state: var DecoderState) =
proc decodeExif(state: var DecoderState) = proc decodeExif(state: var DecoderState) =
## Decode Exif header ## Decode Exif header
let var len = state.readUint16be().int - 2
len = state.readUint16be().int - 2
endOffset = state.pos + len
let exifHeader = state.readStr(6) let exifHeader = state.readStr(6)
len -= 6
if exifHeader != "Exif\0\0": if exifHeader != "Exif\0\0":
# Happens with progressive images, just ignore instead of error. # Happens with progressive images, just ignore instead of error.
# Skip to the end. # Skip to the end.
state.pos = endOffset state.skipBytes(len)
return return
# Read the endianess of the exif header # Read the endianess of the exif header
@ -402,22 +403,40 @@ proc decodeExif(state: var DecoderState) =
else: else:
failInvalid("invalid Tiff header") failInvalid("invalid Tiff header")
len -= 2
# Verify we got the endianess right. # Verify we got the endianess right.
if state.readUint16be().maybeSwap(littleEndian) != 0x002A.uint16: if state.readUint16be().maybeSwap(littleEndian) != 0x002A.uint16:
failInvalid("invalid Tiff header endianess") failInvalid("invalid Tiff header endianess")
len -= 2
# Skip any other tiff header data. # Skip any other tiff header data.
let offsetToFirstIFD = state.readUint32be().maybeSwap(littleEndian).int let offsetToFirstIFD = state.readUint32be().maybeSwap(littleEndian).int
len -= 4
if offsetToFirstIFD < 8:
failInvalid("invalid Tiff offset")
state.skipBytes(offsetToFirstIFD - 8) state.skipBytes(offsetToFirstIFD - 8)
len -= (offsetToFirstIFD - 8)
# Read the IFD0 (main image) tags. # Read the IFD0 (main image) tags.
let numTags = state.readUint16be().maybeSwap(littleEndian).int let numTags = state.readUint16be().maybeSwap(littleEndian).int
len -= 2
for i in 0 ..< numTags: for i in 0 ..< numTags:
let let
tagNumber = state.readUint16be().maybeSwap(littleEndian) tagNumber = state.readUint16be().maybeSwap(littleEndian)
dataFormat = state.readUint16be().maybeSwap(littleEndian) dataFormat = state.readUint16be().maybeSwap(littleEndian)
numberComponents = state.readUint32be().maybeSwap(littleEndian) numberComponents = state.readUint32be().maybeSwap(littleEndian)
dataOffset = state.readUint32be().maybeSwap(littleEndian).int dataOffset = state.readUint32be().maybeSwap(littleEndian).int
len -= 12
# For now we only care about orientation tag. # For now we only care about orientation tag.
case tagNumber: case tagNumber:
of 0x0112: # Orientation of 0x0112: # Orientation
@ -426,7 +445,7 @@ proc decodeExif(state: var DecoderState) =
discard discard
# Skip all of the data we do not want to read, IFD1, thumbnail, etc. # Skip all of the data we do not want to read, IFD1, thumbnail, etc.
state.pos = endOffset state.skipBytes(len) # Skip any remaining len
proc reset(state: var DecoderState) = proc reset(state: var DecoderState) =
## Rests the decoder state need for restart markers. ## Rests the decoder state need for restart markers.
@ -1153,18 +1172,15 @@ proc decodeJpegDimensions*(
of 0xD8: of 0xD8:
# SOI - Start of Image # SOI - Start of Image
discard discard
of 0xC0: of 0xC0, 0xC2:
# Start Of Frame (Baseline DCT) # Start Of Frame (Baseline DCT or Progressive DCT)
state.decodeSOF0() discard state.readUint16be().int # Chunk len
discard state.readUint8() # Precision
state.imageHeight = state.readUint16be().int
state.imageWidth = state.readUint16be().int
break break
of 0xC1: of 0xC1:
# Start Of Frame (Extended sequential DCT) failInvalid("unsupported extended sequential DCT format")
state.decodeSOF1()
break
of 0xC2:
# Start Of Frame (Progressive DCT)
state.decodeSOF2()
break
of 0xDB: of 0xDB:
# Define Quantization Table(s) # Define Quantization Table(s)
state.skipChunk() state.skipChunk()