@@ -1498,14 +1498,20 @@ class BufferedWriter
14981498 return PyInteger::create (len);
14991499 }
15001500
1501+ // everything below re-enters Python (flush() and raw_write() both end up calling
1502+ // raw.write()), which can run arbitrary code that reallocates the source object's
1503+ // storage - e.g. a raw whose write() resizes the very bytearray being written. Take a
1504+ // snapshot now so `start` cannot dangle mid-write.
1505+ const std::vector<char > payload{ start, start + len };
1506+
15011507 // the data does not fit: drain our buffer to raw first
15021508 if (auto r = flush (); r.is_err ()) { return r; }
15031509
15041510 // write oversized data directly to raw, bypassing the buffer
15051511 size_t written = 0 ;
15061512 size_t remaining = len;
15071513 while (remaining > m_buffer_size) {
1508- auto r = raw_write (start + written, remaining);
1514+ auto r = raw_write (payload. data () + written, remaining);
15091515 if (r.is_err ()) { return r; }
15101516 if (r.unwrap () == py_none ()) {
15111517 // TODO: raise BlockingIOError
@@ -1535,7 +1541,7 @@ class BufferedWriter
15351541
15361542 // buffer the tail, which is now guaranteed to fit
15371543 if (remaining > 0 ) {
1538- const auto count = this ->buffer ->sputn (start + written, remaining);
1544+ const auto count = this ->buffer ->sputn (payload. data () + written, remaining);
15391545 ASSERT (static_cast <size_t >(count) == remaining);
15401546 m_buffered_bytes += remaining;
15411547 written += remaining;
0 commit comments