Skip to content

VecDeque::shrink_to leads to UB if handle_alloc_error unwinds. #123369

Closed
@Sp00ph

Description

@Sp00ph

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) and Foo(1) are dropped in some order.

Instead, this happened:

thread 'main' panicked at src/main.rs:37:30:
alloc error
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Dropping Foo(1)
Dropping Foo(3472329422384804195)

(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 calls RawVec::shrink_to_fit(new_capacity) with no regards to potential unwinding. Specifically, this would look something like this:

  1. After initialization, the deque buffer looks like this:
[Foo(0), <uninit> * 13, Foo(1)]
 ^                      ^
last                   head      
  1. shrink_to copies the head to just behind the tail:
[Foo(0), Foo(1), <uninit> * 13]
 ^       ^
last    head
  1. shrink_to calls RawVec::shrink_to_fit calls Allocator::shrink which returns an alloc error, the error handler unwinds, leaving the buffer looking like this:
[Foo(0), Foo(1), <uninit>, <uninit> * 12]
         ^       ^
        head    last
  1. The Drop impl of VecDeque 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 if shrink OOMs. The docs for handle_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 to RawVec::shrink_to_fit and manually abort the process would work well enough?

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: This is a bug.I-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-mediumMedium priorityT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions