From 7905842d833f899f1d3228af7e7467ad80277016 Mon Sep 17 00:00:00 2001 From: Stefan <96178532+stefan6419846@users.noreply.github.com> Date: Tue, 17 Feb 2026 17:38:44 +0100 Subject: [PATCH] SEC: Limit FlateDecode recovery attempts (#3644) --- docs/user/security.md | 3 +++ PyPDF2/filters.py | 16 ++++++++++++---- tests/test_filters.py | 13 ++++++++++--- 3 files changed, 25 insertions(+), 7 deletions(-) Index: PyPDF2-2.11.1/PyPDF2/filters.py =================================================================== --- PyPDF2-2.11.1.orig/PyPDF2/filters.py +++ PyPDF2-2.11.1/PyPDF2/filters.py @@ -60,6 +60,11 @@ from .constants import LzwFilterParamete from .constants import StreamAttributes as SA from .errors import LimitReachedError, PdfReadError, PdfStreamError +ZLIB_MAX_RECOVERY_INPUT_LENGTH = 5_000_000 + +# Reuse cached 1-byte values in the fallback loop to avoid per-byte allocations. +_SINGLE_BYTES = tuple(bytes((i,)) for i in range(256)) + def decompress(data: bytes) -> bytes: try: @@ -67,10 +72,22 @@ def decompress(data: bytes) -> bytes: except zlib.error: d = zlib.decompressobj(zlib.MAX_WBITS | 32) result_str = b"" - for b in [data[i : i + 1] for i in range(len(data))]: + remaining_limit = ZLIB_MAX_OUTPUT_LENGTH + for index in range(len(data)): + chunk = _SINGLE_BYTES[data[index]] try: - result_str += d.decompress(b) + decompressed = d.decompress(chunk) + result_str += decompressed + remaining_limit -= len(decompressed) + if remaining_limit <= 0: + raise LimitReachedError( + f"Limit reached while decompressing. {len(data) - index} bytes remaining." + ) except zlib.error: + if index > ZLIB_MAX_RECOVERY_INPUT_LENGTH: + raise LimitReachedError( + f"Recovery limit reached while decompressing. {data_length - index} bytes remaining." + ) pass return result_str