Add gif fuzzing.
This commit is contained in:
parent
c7ff50660e
commit
dc52e6f77b
2 changed files with 40 additions and 1 deletions
|
@ -27,9 +27,13 @@ proc read*(bs: BitStream, bits: int): int =
|
||||||
result += bs.readBit(bs.pos + bits - i - 1)
|
result += bs.readBit(bs.pos + bits - i - 1)
|
||||||
bs.pos += bits
|
bs.pos += bits
|
||||||
|
|
||||||
|
template failInvalid() =
|
||||||
|
raise newException(PixieError, "Invalid GIF buffer, unable to load.")
|
||||||
|
|
||||||
proc decodeGIF*(data: string): Image =
|
proc decodeGIF*(data: string): Image =
|
||||||
## Decodes GIF data into an Image.
|
## Decodes GIF data into an Image.
|
||||||
|
|
||||||
|
if data.len <= 0xD: failInvalid()
|
||||||
let version = data[0 .. 5]
|
let version = data[0 .. 5]
|
||||||
if version notin gifSignatures:
|
if version notin gifSignatures:
|
||||||
raise newException(PixieError, "Invalid GIF file signature.")
|
raise newException(PixieError, "Invalid GIF file signature.")
|
||||||
|
@ -52,6 +56,7 @@ proc decodeGIF*(data: string): Image =
|
||||||
var colors: seq[ColorRGBA]
|
var colors: seq[ColorRGBA]
|
||||||
var i = 0xD
|
var i = 0xD
|
||||||
if hasColorTable:
|
if hasColorTable:
|
||||||
|
if i + colorTableSize * 3 >= data.len: failInvalid()
|
||||||
for c in 0 ..< colorTableSize:
|
for c in 0 ..< colorTableSize:
|
||||||
let
|
let
|
||||||
r = data.readUint8(i + 0)
|
r = data.readUint8(i + 0)
|
||||||
|
@ -66,6 +71,7 @@ proc decodeGIF*(data: string): Image =
|
||||||
i += 1
|
i += 1
|
||||||
case blockType:
|
case blockType:
|
||||||
of 0x2c: # IMAGE
|
of 0x2c: # IMAGE
|
||||||
|
if i + 9 >= data.len: failInvalid()
|
||||||
let
|
let
|
||||||
left = data.readUint16(i + 0)
|
left = data.readUint16(i + 0)
|
||||||
top = data.readUint16(i + 2)
|
top = data.readUint16(i + 2)
|
||||||
|
@ -92,15 +98,18 @@ proc decodeGIF*(data: string): Image =
|
||||||
raise newException(PixieError, "Interlacing not supported.")
|
raise newException(PixieError, "Interlacing not supported.")
|
||||||
|
|
||||||
# Read the lzw data chunks.
|
# Read the lzw data chunks.
|
||||||
|
if i >= data.len: failInvalid()
|
||||||
let lzwMinBitSize = data.readUint8(i)
|
let lzwMinBitSize = data.readUint8(i)
|
||||||
i += 1
|
i += 1
|
||||||
var lzwData = ""
|
var lzwData = ""
|
||||||
while true:
|
while true:
|
||||||
|
if i >= data.len: failInvalid()
|
||||||
let lzwEncodedLen = data.readUint8(i)
|
let lzwEncodedLen = data.readUint8(i)
|
||||||
i += 1
|
i += 1
|
||||||
if lzwEncodedLen == 0:
|
if lzwEncodedLen == 0:
|
||||||
# Stop reading when chunk len is 0.
|
# Stop reading when chunk len is 0.
|
||||||
break
|
break
|
||||||
|
if i + lzwEncodedLen.int > data.len: failInvalid()
|
||||||
lzwData.add data[i ..< i + lzwEncodedLen.int]
|
lzwData.add data[i ..< i + lzwEncodedLen.int]
|
||||||
i += lzwEncodedLen.int
|
i += lzwEncodedLen.int
|
||||||
|
|
||||||
|
@ -119,7 +128,7 @@ proc decodeGIF*(data: string): Image =
|
||||||
|
|
||||||
# Main decode loop.
|
# Main decode loop.
|
||||||
while codeLast != endMark:
|
while codeLast != endMark:
|
||||||
|
if bs.pos + bitSize.int > bs.len: failInvalid()
|
||||||
var
|
var
|
||||||
# Read variable bits out of the table.
|
# Read variable bits out of the table.
|
||||||
codeId = bs.read(bitSize.int)
|
codeId = bs.read(bitSize.int)
|
||||||
|
@ -147,6 +156,7 @@ proc decodeGIF*(data: string): Image =
|
||||||
|
|
||||||
elif codeLast != -1 and codeLast != clearMark and codeLast != endMark:
|
elif codeLast != -1 and codeLast != clearMark and codeLast != endMark:
|
||||||
# Its in the current table use it.
|
# Its in the current table use it.
|
||||||
|
if codeLast >= codeTable.len: failInvalid()
|
||||||
var previous = codeTable[codeLast]
|
var previous = codeTable[codeLast]
|
||||||
carryOver = @[previous[0]]
|
carryOver = @[previous[0]]
|
||||||
colorIndexes.add(previous & carryOver)
|
colorIndexes.add(previous & carryOver)
|
||||||
|
@ -158,12 +168,14 @@ proc decodeGIF*(data: string): Image =
|
||||||
|
|
||||||
if codeLast != -1 and codeLast != clearMark and codeLast != endMark:
|
if codeLast != -1 and codeLast != clearMark and codeLast != endMark:
|
||||||
# We had some left over and need to expand table.
|
# We had some left over and need to expand table.
|
||||||
|
if codeLast >= codeTable.len: failInvalid()
|
||||||
codeTable.add(codeTable[codeLast] & carryOver)
|
codeTable.add(codeTable[codeLast] & carryOver)
|
||||||
|
|
||||||
codeLast = codeId
|
codeLast = codeId
|
||||||
|
|
||||||
# Convert color indexes into real colors.
|
# Convert color indexes into real colors.
|
||||||
for j, idx in colorIndexes:
|
for j, idx in colorIndexes:
|
||||||
|
if idx >= colors.len or j >= result.data.len: failInvalid()
|
||||||
result.data[j] = colors[idx]
|
result.data[j] = colors[idx]
|
||||||
|
|
||||||
of 0x21:
|
of 0x21:
|
||||||
|
|
27
tests/fuzz_gif.nim
Normal file
27
tests/fuzz_gif.nim
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import pixie/common, pixie/fileformats/gif, random, strformat
|
||||||
|
|
||||||
|
randomize()
|
||||||
|
|
||||||
|
let original = readFile("tests/images/gif/sunflower.gif")
|
||||||
|
|
||||||
|
for i in 0 ..< 10_000:
|
||||||
|
var data = original
|
||||||
|
let
|
||||||
|
pos = rand(data.len)
|
||||||
|
value = rand(255).char
|
||||||
|
# pos = 27355
|
||||||
|
# value = '&'
|
||||||
|
data[pos] = value
|
||||||
|
echo &"{i} {pos} {value}"
|
||||||
|
try:
|
||||||
|
let img = decodeGif(data)
|
||||||
|
doAssert img.height > 0 and img.width > 0
|
||||||
|
except PixieError:
|
||||||
|
discard
|
||||||
|
|
||||||
|
data = data[0 ..< pos]
|
||||||
|
try:
|
||||||
|
let img = decodeGif(data)
|
||||||
|
doAssert img.height > 0 and img.width > 0
|
||||||
|
except PixieError:
|
||||||
|
discard
|
Loading…
Reference in a new issue