Skip to content

Check exported buffers after the default callback returns - #735

Draft
AbhinavMir wants to merge 1 commit into
msgpack:mainfrom
AbhinavMir:packer-default-callback-exports
Draft

Check exported buffers after the default callback returns#735
AbhinavMir wants to merge 1 commit into
msgpack:mainfrom
AbhinavMir:packer-default-callback-exports

Conversation

@AbhinavMir

Copy link
Copy Markdown

Packer.pack() calls the default callback for an unsupported object. The callback can export the internal buffer with getbuffer() or memoryview(packer). The packer then packs the value that the callback returns. msgpack_pack_write() in pack.h reallocates the buffer when that value does not fit. The export then points at freed memory. A read of the export is a heap use-after-free.

_check_exports() already guards the six public entry points of Packer. It does not run again after the callback returns. This change calls it right after the callback returns. Packer.pack() now raises BufferError.

The pure Python packer already raises BufferError for the same code, because BytesIO.write() refuses to resize a buffer that has an export. This change makes the two implementations agree.

I also considered a guard inside msgpack_pack_write(). That needs an export count in the plain C msgpack_packer struct. The callback is the only place that runs user code during a pack, so I kept the check at the call site. I am happy to move it if you prefer the lower layer.

The new test is test/test_buffer.py::test_packer_getbuffer_in_default. It sits next to the existing test_packer_getbuffer test. It passes for both the C extension and the pure Python fallback. It fails on the current code, because the pack succeeds and leaves a dangling export.

The default callback can export the internal buffer with getbuffer().
The packer then continues and can reallocate that buffer.
The export points at freed memory after the reallocation.

Call _check_exports() after the callback returns. The packer now
raises BufferError. The pure Python packer already raises BufferError
in this case.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Other supported user-code hooks can still export the buffer before a reallocating write.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Prevents buffer invalidation after default exports the packer’s internal buffer.

Changes:

  • Checks active exports after default returns.
  • Adds regression coverage for the unsafe reallocation scenario.
File summaries
File Description
msgpack/_packer.pyx Adds post-callback export validation.
test/test_buffer.py Tests exported-buffer safety during callbacks.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread msgpack/_packer.pyx
Comment on lines +267 to +269
# The callback may have exported the internal buffer.
# Packing on would reallocate it and invalidate the export.
self._check_exports()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants