Skip to content

fix: segfault when moving scoped_ostream_redirect#6033

Open
kounelisagis wants to merge 2 commits intopybind:masterfrom
kounelisagis:fix/scoped-ostream-redirect-move-segfault
Open

fix: segfault when moving scoped_ostream_redirect#6033
kounelisagis wants to merge 2 commits intopybind:masterfrom
kounelisagis:fix/scoped-ostream-redirect-move-segfault

Conversation

@kounelisagis
Copy link
Copy Markdown

Description

The default move constructor of scoped_ostream_redirect caused a segfault when the moved-to object (or the original stream) was subsequently used.

The root cause: = default moved the internal pythonbuf (transferring its d_buffer, pywrite, pyflush and nulling the streambuf base pointers), but left the stream (e.g. std::cout) still pointing at the moved-from buffer. Any subsequent write through the stream called overflow() on the zombie pythonbuf, which dereferenced pptr() (now null) — immediate segfault.

Reproducer

py::scoped_ostream_redirect redir1(std::cout, py::module_::import("sys").attr("stdout"));
std::cout << "before" << std::flush;              // OK
py::scoped_ostream_redirect redir2(std::move(redir1));
std::cout << "after" << std::flush;               // SIGSEGV

Fix

Replace = default with an explicit move constructor that:

  1. Moves the buffer to the new location
  2. Re-points the stream to the new buffer (costream.rdbuf(&buffer))
  3. Sets other.old = nullptr to disarm the moved-from destructor

The destructor is guarded with if (old) so the moved-from object skips restoring the stream.

Suggested changelog entry:

  • Fixed segfault when moving scoped_ostream_redirect: the default move constructor left the stream pointing at a moved-from pythonbuf with null internal pointers, causing a null dereference on the next write.

The default move constructor left the stream (`std::cout`) pointing at
the moved-from `pythonbuf`, whose internal buffer and streambuf pointers
were nulled by the move. Any subsequent write through the stream
dereferenced null, causing a segfault.

Replace `= default` with an explicit move constructor that re-points
the stream to the new buffer and disarms the moved-from destructor.
@kounelisagis kounelisagis force-pushed the fix/scoped-ostream-redirect-move-segfault branch from 2b243d9 to 156332c Compare April 8, 2026 23:46
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.

1 participant