From dd3d432573913cd5948833cb6574bea85cbdc680 Mon Sep 17 00:00:00 2001 From: Kiko Date: Wed, 11 Nov 2026 22:34:01 +0000 Subject: [PATCH] raven-image --margin: restore the IHDR size bound before any header-sized allocation, with the 2**32-1 test that pins it Co-Authored-By: Claude Sonnet 5.5 --- client/README.md | 2 +- client/raven-image | 2 ++ tests/test-raven-image.py | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/client/README.md b/client/README.md index d35fd5e..809671e 100644 --- a/client/README.md +++ b/client/README.md @@ -80,7 +80,7 @@ the subject also contains (green in a green-eyed character) is untouched except a rectangle, so the pin cannot ask for a vignette or a round sticker. A subject cannot be asked to stay off the seam: only the exit code tells you it did not. -Offline test (fake ComfyUI on 127.0.0.1, 158 checks): `python3 tests/test-raven-image.py`. +Offline test (fake ComfyUI on 127.0.0.1, 159 checks): `python3 tests/test-raven-image.py`. ## What it does diff --git a/client/raven-image b/client/raven-image index 425bb43..ecda0bd 100644 --- a/client/raven-image +++ b/client/raven-image @@ -160,6 +160,8 @@ def png_decode(data): if hdr is None or not idat: raise ValueError("no IHDR or no image data") w, h, depth, ctype, _, _, interlace = hdr + if not 0 < w * h <= 2 ** 26: # before any allocation sized by the header: 2**32-1 squared would ask for 12 GB and overflow ssize_t + raise ValueError(f"image size {w}x{h} is out of range") if depth != 8 or ctype not in (2, 6) or interlace: raise ValueError(f"only 8-bit non-interlaced RGB/RGBA is handled (got depth {depth}, colour type {ctype}, interlace {interlace})") bpp = 3 if ctype == 2 else 4 diff --git a/tests/test-raven-image.py b/tests/test-raven-image.py index bc8b57a..cbf176b 100644 --- a/tests/test-raven-image.py +++ b/tests/test-raven-image.py @@ -307,12 +307,13 @@ def blob(w, h, depth=8, ctype=2, idat=None, il=0): + ck(b"IDAT", idat if idat is not None else zlib.compress(b"\0" * (h * (w * 3 + 1)) if w * h < 10 ** 7 else b"")) + ck(b"IEND", b"")) good = mkpng(8, 8, lambda x, y: BG) bad = bytearray(good); bad[40] ^= 1 -for data, what in ((bytes(bad), "flipped byte (CRC)"), (blob(2 ** 21, 2 ** 21), "a header that promises far more than the data"), (blob(8, 8, idat=zlib.compress(b"\0" * (8 * 25 - 1))), "image data shorter than the header says"), +for data, what in ((bytes(bad), "flipped byte (CRC)"), (blob(2 ** 21, 2 ** 21), "a header that promises far more than the data"), (blob(2 ** 32 - 1, 2 ** 32 - 1), "a 2**32-1 square header (must refuse before allocating)"), (blob(8, 8, idat=zlib.compress(b"\0" * (8 * 25 - 1))), "image data shorter than the header says"), (blob(8, 8, idat=zlib.compress(b"\0" * (8 * 25 + 500))), "image data longer than the header says"), (blob(8, 8, depth=16), "16-bit"), (blob(8, 8, ctype=3), "palette"), (blob(8, 8, il=1), "interlaced"), (good[:-20], "truncated"), (good[:8] + struct.pack(">I", 10 ** 6) + good[12:], "chunk length past the end")): try: mod2.png_decode(data); check(False, f"png_decode refuses {what}") except (ValueError, zlib.error, struct.error): check(True, "") + except BaseException as e: check(False, f"png_decode refuses {what} cleanly, got {type(e).__name__}") # size and interior validation, before any request refused(["--size", "foo", "cap"], "--size is WxH", "--size foo") refused(["--size", "1000x1024", "cap"], "multiples of 16", "--size not on the grid")