-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
gh-143308: fix UAF when PickleBuffer is concurrently mutated in a callback #143312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
1e4c48f
f359533
4129734
a6a5713
a6a3a91
b2117f5
ec61e9c
47396ab
fbc62c5
334cc62
0d70297
fc01ce1
170b73f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| :mod:`pickle`: fix use-after-free crashes when a :class:`~pickle.PickleBuffer` | ||
|
picnixz marked this conversation as resolved.
|
||
| is concurrently mutated by a custom buffer callback during pickling. | ||
| Patch by Bénédikt Tran and Aaron Wieczorek. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2646,43 +2646,58 @@ save_picklebuffer(PickleState *st, PicklerObject *self, PyObject *obj) | |
| "pointing to a non-contiguous buffer"); | ||
|
Comment on lines
2645
to
2646
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Out of curiosity, but do we have a test for this?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Neither of the tests added test for non-contiguous, but should be straightforward to add a test against a buffer with strides...
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just wanted to be sure that this branch was covered by existing tests or by other tests. We can increase the coverage in a follow-up PR. |
||
| return -1; | ||
| } | ||
|
|
||
| int rc = 0; | ||
| Py_buffer keepalive_view; // to ensure that 'view' is kept alive | ||
| if (PyObject_GetBuffer(view->obj, &keepalive_view, PyBUF_FULL_RO) != 0) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could not we simply apply
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potentially! I'd want to test it. @picnixz what do you think? Since this hasn't been merged yet now would seem to be the time.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should work.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This worked great, thanks for the suggestion @serhiy-storchaka. @picnixz The change was a little more involved than expected, but by doing this I was able to remove a redundant view, update all the |
||
| return -1; | ||
| } | ||
|
|
||
| int in_band = 1; | ||
| if (self->buffer_callback != NULL) { | ||
| PyObject *ret = PyObject_CallOneArg(self->buffer_callback, obj); | ||
| if (ret == NULL) { | ||
| return -1; | ||
| goto error; | ||
| } | ||
| in_band = PyObject_IsTrue(ret); | ||
| Py_DECREF(ret); | ||
| if (in_band == -1) { | ||
| return -1; | ||
| goto error; | ||
| } | ||
| } | ||
| if (in_band) { | ||
| /* Write data in-band */ | ||
| if (view->readonly) { | ||
| return _save_bytes_data(st, self, obj, (const char *)view->buf, | ||
| rc = _save_bytes_data(st, self, obj, (const char *)view->buf, | ||
| view->len); | ||
| } | ||
| else { | ||
| return _save_bytearray_data(st, self, obj, (const char *)view->buf, | ||
| rc = _save_bytearray_data(st, self, obj, (const char *)view->buf, | ||
| view->len); | ||
| } | ||
| goto exit; | ||
| } | ||
| else { | ||
| /* Write data out-of-band */ | ||
| const char next_buffer_op = NEXT_BUFFER; | ||
| if (_Pickler_Write(self, &next_buffer_op, 1) < 0) { | ||
| return -1; | ||
| goto error; | ||
| } | ||
| if (view->readonly) { | ||
| const char readonly_buffer_op = READONLY_BUFFER; | ||
| if (_Pickler_Write(self, &readonly_buffer_op, 1) < 0) { | ||
| return -1; | ||
| goto error; | ||
| } | ||
| } | ||
| } | ||
| return 0; | ||
|
|
||
| exit: | ||
| PyBuffer_Release(&keepalive_view); | ||
| return rc; | ||
|
|
||
| error: | ||
| PyBuffer_Release(&keepalive_view); | ||
| return -1; | ||
| } | ||
|
|
||
| /* A copy of PyUnicode_AsRawUnicodeEscapeString() that also translates | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.