diff --git a/msgpack/_packer.pyx b/msgpack/_packer.pyx index ca6cf983..b2dce7f6 100644 --- a/msgpack/_packer.pyx +++ b/msgpack/_packer.pyx @@ -264,6 +264,9 @@ cdef class Packer: ret = self._pack_inner(o, 1, nest_limit) if ret == -2: o = self._default(o) + # The callback may have exported the internal buffer. + # Packing on would reallocate it and invalidate the export. + self._check_exports() else: return ret return self._pack_inner(o, 0, nest_limit) diff --git a/test/test_buffer.py b/test/test_buffer.py index ca097222..ed17f215 100644 --- a/test/test_buffer.py +++ b/test/test_buffer.py @@ -47,3 +47,25 @@ def test_packer_getbuffer(): buffer.release() packer.pack(42) assert bytes(packer) == b"\x92*\xa5hello*" + + +def test_packer_getbuffer_in_default(): + # The default callback can export the internal buffer. + # The packer must refuse to pack on, because packing on reallocates + # the buffer and leaves the export pointing at freed memory. + exported = [] + + class Unsupported: + pass + + def default(obj): + exported.append(packer.getbuffer()) + return b"A" * (2 * 1024 * 1024) + + packer = Packer(default=default, autoreset=False) + with raises(BufferError): + packer.pack([Unsupported()]) + + assert len(exported) == 1 + assert bytes(exported[0]) == b"\x91" + exported[0].release()