From 997ca35eb55c50d8707930f7a83f615cff7b9c6a Mon Sep 17 00:00:00 2001 From: treeform Date: Thu, 30 Apr 2026 09:03:41 -0700 Subject: [PATCH] Reject truncated PPM pixel data --- src/pixie/fileformats/ppm.nim | 24 ++++++++++++++++-------- tests/test_ppm.nim | 8 +++++++- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/pixie/fileformats/ppm.nim b/src/pixie/fileformats/ppm.nim index 4bba3d4f..9b3d0b23 100644 --- a/src/pixie/fileformats/ppm.nim +++ b/src/pixie/fileformats/ppm.nim @@ -53,15 +53,20 @@ proc decodeHeader( result.dataOffset = i -proc decodeP6Data(data: string, maxVal: int): seq[ColorRGBX] {.raises: [].} = +proc decodeP6Data( + data: string, maxVal: int +): seq[ColorRGBX] {.raises: [PixieError].} = let needsUint16 = maxVal > 0xFF - - result = newSeq[ColorRGBX]( + let bytesPerPixel = if needsUint16: - data.len div 6 + 6 else: - data.len div 3 - ) + 3 + + if data.len mod bytesPerPixel != 0: + failInvalid() + + result = newSeq[ColorRGBX](data.len div bytesPerPixel) # Let's calculate the real maximum value multiplier. # rgbx() accepts a maximum value of 255. Most of the time, @@ -129,15 +134,18 @@ proc decodePpm*(data: string): Image {.raises: [PixieError].} = if not (header.version in ppmSignatures): failInvalid() - if 0 > header.maxVal or header.maxVal > 0xFFFF: + if header.maxVal <= 0 or header.maxVal > 0xFFFF: failInvalid() result = newImage(header.width, header.height) - result.data = + let pixels = if header.version == "P3": decodeP3Data(data[header.dataOffset .. ^1], header.maxVal) else: decodeP6Data(data[header.dataOffset .. ^1], header.maxVal) + if pixels.len != result.data.len: + failInvalid() + result.data = pixels proc decodePpmDimensions*( data: pointer, len: int diff --git a/tests/test_ppm.nim b/tests/test_ppm.nim index f668edfe..6721bd24 100644 --- a/tests/test_ppm.nim +++ b/tests/test_ppm.nim @@ -1,4 +1,4 @@ -import pixie/fileformats/ppm +import pixie, pixie/fileformats/ppm block: for format in @["p3", "p6"]: @@ -23,3 +23,9 @@ block: let p6Master = readFile("tests/fileformats/ppm/feep.p6.master.ppm") for image in @["p3", "p6", "p3.hidepth"]: doAssert readFile("tests/fileformats/ppm/feep." & $image & ".ppm") == p6Master + +block: + let payload = "P6\n10 10\n255\n" & "\x12\x34\x56" + + doAssertRaises PixieError: + discard decodeImage(payload)