Repository navigation
gh-157364: Fix use-after-free in TextIOWrapper during reentrant detach - #157370
Conversation
|
I will need a couple more days to look at this, it seems like this is more invasive than necessary to me and makes a number of not needed for the core UAF bug report. |
|
For gh-157364 I think the bug, and solution, is actually in Buffered I/O. This PR changes Text I/O to keep one more reference, which may also be needed. The issue though is that Buffered I/O has an internal allocation which it passes as an argument to the Raw I/O That makes two pieces to fix here:
|
|
Thanks for looking into this, @cmaloney! Regarding point 1: passing mbuf->master = *info;
mbuf->master.obj = NULL;Because of this, the resulting Regarding point 2: CPython method calls via |
|
For 1: The |
|
I don't understand well how _bufferedreader_raw_read_getbuffer() works. How is it different from the current PyBuffer_FillInfo() + PyMemoryView_FromBuffer() code? |
|
The key difference is reference ownership:
We can't use standard |
|
I have been triaging some old I/O bugs and came across gh-60198 which is exactly the BufferedReader memoryview bug here. That could be pulled out as a separate smaller PR that is likely quicker to land with its own NEWS. I think this should have two news entries: one the io.TexTIOWrapper keeping a reference to I don't like adding the two new members to In the TextIO the Incref and decref living in very different functions I'm not a big fan of... I would much rather keep |
|
@vstinner @cmaloney I narrowed this PR to the TextIOWrapper fix for gh-157364. I removed the BufferedReader change and added the requested check that the wrapper is detached after |
Just a general remark: you're making many large changes on this PR. It's not easy to follow these changes. |
vstinner
left a comment
There was a problem hiding this comment.
Good: the test does crash without the fix. So it confirms that the test checks the modified C code.
$ ./python -m test -v test_io.test_textio -m test_reentrant_detach_during_read
(...)Warning -- Unraisable exception
Exception ignored while finalizing file <_io.BufferedReader>:
Traceback (most recent call last):
File "/home/vstinner/python/main/Lib/test/test_io/test_textio.py", line 1651, in readinto
wrapper.detach()
~~~~~~~~~~~~~~^^
RuntimeError: reentrant call inside <_io.BufferedReader>
Fatal Python error: Segmentation fault
<Cannot show all threads while the GIL is disabled>
Stack (most recent call first):
File "/home/vstinner/python/main/Lib/test/test_io/test_textio.py", line 1661 in test_reentrant_detach_during_read
vstinner
left a comment
There was a problem hiding this comment.
LGTM. Returning a strong reference in buffer_access_safe() sounds like a safe move. I checked that buffer_access_safe() callers do Py_DECREF() on the buffer.
./python -m test -R 3:3 test_io doesn't report leaks. But it reports negative reference count which is surprising:
$ ./python -m test -R 3:3 test_io.test_textio
(...)
test_io.test_textio leaked [-1, -1, -1] references, sum=-3 (this is fine)
$ ./python -m test -R 3:3 test_io.test_bufferedio
(...)
test_io.test_bufferedio leaked [-3, -3, -3] references, sum=-9 (this is fine)
Ah, I get the same result on the main branch. So it's not caused by this change. |
|
Thanks the updates. I enabled auto-merge, please don't touch the PR (your branch) anymore. This change is a bugfix which should be backported to 3.14 and 3.15 branches. |
|
CI failed on test_profiling which looks independent, re-running. |
|
Sorry, @ashm-dev and @vstinner, I could not cleanly backport this to Please backport manually with cherry_picker, see the devguide for more information. |
|
Sorry, @ashm-dev and @vstinner, I could not cleanly backport this to Please backport manually with cherry_picker, see the devguide for more information. |
|
Aha, more conflicts. I will backport the change to 3.14 and 3.15 once the 3.15 branch will be unblocked (next week). |
|
PR merged, thanks @ashm-dev for your fix. |
|
Backporting this change is non trivial. I have to backport at least these commits
There are some minor conflicts, and then test_io.textio fails. I will retry next week. |
|
Ok, I found the right order and the correct commits to backport: I created #159591 for the 3.15 branch. |
…rant detach (#159591) * gh-143008: Fix Null pointer dereferences in TextIOWrapper underlying stream access (#145957) TextIOWrapper keeps its underlying stream in a member called `self->buffer`. That stream can be detached by user code, such as custom `.flush` implementations resulting in `self->buffer` being set to NULL. The implementation often checked at the start of functions if `self->buffer` is in a good state, but did not always recheck after other Python code was called which could modify `self->buffer`. The cases which need to be re-checked are hard to spot so rather than rely on reviewer effort create better safety by making all self->buffer access go through helper functions. Thank you yihong0618 for the test, NEWS and initial implementation in gh-143041. Co-authored-by: yihong0618 <zouzou0208@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org> (cherry picked from commit db4b194) * gh-154523: Fix data-race in `TextIOWrapper.detach()` (#154565) (cherry picked from commit 8440a6a) * gh-157364: Fix use-after-free in io.TextIOWrapper during reentrant detach (#157370) Co-authored-by: Victor Stinner <vstinner@python.org> (cherry picked from commit e19dc47) --------- Co-authored-by: Cody Maloney <cmaloney@users.noreply.github.com> Co-authored-by: yihong0618 <zouzou0208@gmail.com> Co-authored-by: Brij Kapadia <97006829+brijkapadia@users.noreply.github.com> Co-authored-by: Shamil <ashm.tech@proton.me>
…rant detach (#159600) [3.15] gh-157364: Fix use-after-free in io.TextIOWrapper during reentrant detach (#159591) * gh-143008: Fix Null pointer dereferences in TextIOWrapper underlying stream access (#145957) TextIOWrapper keeps its underlying stream in a member called `self->buffer`. That stream can be detached by user code, such as custom `.flush` implementations resulting in `self->buffer` being set to NULL. The implementation often checked at the start of functions if `self->buffer` is in a good state, but did not always recheck after other Python code was called which could modify `self->buffer`. The cases which need to be re-checked are hard to spot so rather than rely on reviewer effort create better safety by making all self->buffer access go through helper functions. Thank you yihong0618 for the test, NEWS and initial implementation in gh-143041. (cherry picked from commit db4b194) * gh-154523: Fix data-race in `TextIOWrapper.detach()` (#154565) (cherry picked from commit 8440a6a) * gh-157364: Fix use-after-free in io.TextIOWrapper during reentrant detach (#157370) (cherry picked from commit e19dc47) --------- (cherry picked from commit ea1312b) Co-authored-by: Cody Maloney <cmaloney@users.noreply.github.com> Co-authored-by: yihong0618 <zouzou0208@gmail.com> Co-authored-by: Brij Kapadia <97006829+brijkapadia@users.noreply.github.com> Co-authored-by: Shamil <ashm.tech@proton.me>
Uh oh!
There was an error while loading. Please reload this page.