From 4254f299611faf8bdfbe52b0c3237bb20ae02943 Mon Sep 17 00:00:00 2001 From: Inada Naoki Date: Tue, 27 Oct 2026 17:49:39 +0900 Subject: [PATCH] Reject ext32 length 0xffffffff in the C unpacker The trail length of an ext value includes the typecode byte, so 0xffffffff + 1 wrapped to zero in 32-bit arithmetic. Unpacker.skip() then treated the 5-byte ext32 header as a complete object and parsed the remaining bytes as the next object. unpack() was already rejected by the max_ext_len check, but with a confusing "0 exceeds" message. Raise ValueError before adding the typecode byte. Reported by Oz Kaiser (@tatikoma). Co-Authored-By: Claude Opus 5.5 --- msgpack/unpack_template.h | 13 +++++++++---- test/test_limits.py | 9 +++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/msgpack/unpack_template.h b/msgpack/unpack_template.h index 5f0709a4..9e3e3ce0 100644 --- a/msgpack/unpack_template.h +++ b/msgpack/unpack_template.h @@ -232,10 +232,15 @@ static inline int unpack_execute(bool construct, unpack_context* ctx, const char again_fixed_trail_if_zero(ACS_EXT_VALUE, _msgpack_load16(uint16_t,n)+1, _ext_zero); - case CS_EXT_32: - again_fixed_trail_if_zero(ACS_EXT_VALUE, - _msgpack_load32(uint32_t,n)+1, - _ext_zero); + case CS_EXT_32: { + uint32_t size = _msgpack_load32(uint32_t,n); + // trail includes the typecode, so size+1 must not wrap around. + if (size == UINT32_MAX) { + PyErr_Format(PyExc_ValueError, "%u exceeds max ext length", size); + goto _failed; + } + again_fixed_trail_if_zero(ACS_EXT_VALUE, size+1, _ext_zero); + } case CS_FLOAT: { double f; #if PY_VERSION_HEX >= 0x030B00A7 diff --git a/test/test_limits.py b/test/test_limits.py index 6ba73ce3..af0ce299 100644 --- a/test/test_limits.py +++ b/test/test_limits.py @@ -191,6 +191,15 @@ def test_max_ext_len(): unpacker.unpack() +@pytest.mark.parametrize("method", ["unpack", "skip"]) +def test_ext32_max_length(method): + # ext32 with length 0xffffffff followed by bytes that look like another object. + unpacker = Unpacker() + unpacker.feed(b"\xc9\xff\xff\xff\xff" + packb({"a": 1})) + with pytest.raises(UnpackValueError): + getattr(unpacker, method)() + + # PyPy fails following tests because of constant folding? # https://bugs.pypy.org/issue1721 # @pytest.mark.skipif(True, reason="Requires very large memory.")