-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
VecDeque::shrink_to
leads to UB if handle_alloc_error
unwinds.
#123369
Comments
@rustbot label +T-libs +I-unsound |
And we can't even run it in miri (cc @rust-lang/miri):
|
Yeah the Miri allocator never fails so we never even tried running the alloc_error_handler... filed rust-lang/miri#3439 to track that. |
This is nightly-only since it requires an unstable flag |
Could there hypothetically be a scenario (on stable) where you're not linking against std, but you still have an unwinding panic handler? In that case I think this UB could still occur (because |
No, all of the machinery related to unwinding depends on unstable implementation details. |
In that case, would it be possible to just back out of allowing |
Looks like the docs were changed to allow unwinding in #115007 based on the arguments in #114898 that the custom alloc error hook is allowed to unwind and that 1.68 release notes stated that future versions of std may choose to call the panic handler on OOM. To me these points don't seem mutually exclusive to |
Unwinding on OOM was introduced in RFC 2116 (tracking issue), and unsafe code not handling this correct was a known issue. I think we should keep this feature as unstable until we have at least audited the entire standard library for incorrect handling of unwinds on OOM. |
…acrum Fix `VecDeque::shrink_to` UB when `handle_alloc_error` unwinds. Fixes rust-lang#123369 For `VecDeque` it's relatively simple to restore the buffer into a consistent state so this PR does just that. Note that with its current implementation, `shrink_to` may change the internal arrangement of elements in the buffer, so e.g. `[D, <uninit>, A, B, C]` will become `[<uninit>, A, B, C, D]` and `[<uninit>, <uninit>, A, B, C]` may become `[B, C, <uninit>, <uninit>, A]` if `shrink_to` unwinds. This shouldn't be an issue though as we don't make any guarantees about the stability of the internal buffer arrangement (and this case is impossible to hit on stable anyways). This PR also includes a test with code adapted from rust-lang#123369 which fails without the new `shrink_to` code. Does this suffice or do we maybe need more exhaustive tests like in rust-lang#108475? cc `@Amanieu` `@rustbot` label +T-libs
Running this in Miri works now, and does report "Undefined Behavior: using uninitialized data, but this operation requires initialized memory". |
Rollup merge of rust-lang#123803 - Sp00ph:shrink_to_fix, r=Mark-Simulacrum Fix `VecDeque::shrink_to` UB when `handle_alloc_error` unwinds. Fixes rust-lang#123369 For `VecDeque` it's relatively simple to restore the buffer into a consistent state so this PR does just that. Note that with its current implementation, `shrink_to` may change the internal arrangement of elements in the buffer, so e.g. `[D, <uninit>, A, B, C]` will become `[<uninit>, A, B, C, D]` and `[<uninit>, <uninit>, A, B, C]` may become `[B, C, <uninit>, <uninit>, A]` if `shrink_to` unwinds. This shouldn't be an issue though as we don't make any guarantees about the stability of the internal buffer arrangement (and this case is impossible to hit on stable anyways). This PR also includes a test with code adapted from rust-lang#123369 which fails without the new `shrink_to` code. Does this suffice or do we maybe need more exhaustive tests like in rust-lang#108475? cc `@Amanieu` `@rustbot` label +T-libs
I tried this code:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=a2e4bd9f59882c88c4f232379cbb0001
I expected to see this happen:
shrink_to_fit
causes an alloc error, the thread unwinds,Foo(0)
andFoo(1)
are dropped in some order.Instead, this happened:
(the exact value printed differs between runs, which makes sense if it's reading uninit memory)
This occurs because of the way
VecDeque::shrink_to
is implemented: It first moves the memory around to fit the new capacity, then callsRawVec::shrink_to_fit(new_capacity)
with no regards to potential unwinding. Specifically, this would look something like this:shrink_to
copies the head to just behind the tail:shrink_to
callsRawVec::shrink_to_fit
callsAllocator::shrink
which returns an alloc error, the error handler unwinds, leaving the buffer looking like this:Drop
impl ofVecDeque
drops its elements, reading the uninit memory.Meta
While this specific setup requires nightly to manually set the alloc error handle hook and to trigger the alloc error in
shrink
, this same bug could occur on stable ifshrink
OOMs. The docs forhandle_alloc_error
also explicitly state that it may either abort or unwind.I'm not sure what would be the best way to fix this. Maybe just
catch_unwind
the call toRawVec::shrink_to_fit
and manually abort the process would work well enough?The text was updated successfully, but these errors were encountered: