Skip to content

Using thread_local! with Drop from GlobalAlloc::alloc causes crashes #116390

Open
@nvzqz

Description

@nvzqz

I tried this code:

use std::alloc::{GlobalAlloc, Layout, System};

#[global_allocator]
static ALLOC: Alloc = Alloc;

struct Alloc;

struct Local;

impl Drop for Local {
    fn drop(&mut self) {}
}

thread_local! {
    static LOCAL: Local = Local;
}

unsafe impl GlobalAlloc for Alloc {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        LOCAL.with(|_local| {});
        System.alloc(layout)
    }

    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
        System.dealloc(ptr, layout)
    }
}

fn main() {
    println!("Hello, world!");
}

I expected to see this happen: it prints Hello, world!.

Instead, this happened: terminated by signal SIGILL (Illegal instruction)

When run under Miri, it reports undefined behavior:

error: Undefined Behavior: not granting access to tag <715> because that would remove [Unique for <729>] which is strongly protected because it is an argument of call 293
  --> /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/unix/thread_local_dtor.rs:72:16
   |
72 |     let list = &mut DTORS;
   |                ^^^^^^^^^^ not granting access to tag <715> because that would remove [Unique for <729>] which is strongly protected because it is an argument of call 293
   |
   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <715> was created here, as the base tag for alloc381
  --> src/main.rs:14:1
   |
14 | / thread_local! {
15 | |     static LOCAL: Local = Local;
16 | | }
   | |_^
help: <729> is this argument
  --> src/main.rs:14:1
   |
14 | / thread_local! {
15 | |     static LOCAL: Local = Local;
16 | | }
   | |_^
   = note: BACKTRACE (of the first span):
   = note: inside `std::sys::unix::thread_local_dtor::register_dtor` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/unix/thread_local_dtor.rs:72:16: 72:26
   = note: inside `std::thread::local_impl::Key::<Local>::try_register_dtor` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/common/thread_local/fast_local.rs:207:26: 207:88
   = note: inside `std::thread::local_impl::Key::<Local>::try_initialize::<[closure@/Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/common/thread_local/fast_local.rs:91:31: 91:38]>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/common/thread_local/fast_local.rs:188:48: 188:72
   = note: inside `std::thread::local_impl::Key::<Local>::get::<[closure@/Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/common/thread_local/fast_local.rs:91:31: 91:38]>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/common/thread_local/fast_local.rs:173:25: 173:50
note: inside `LOCAL::__getit`
  --> src/main.rs:14:1
   |
14 | / thread_local! {
15 | |     static LOCAL: Local = Local;
16 | | }
   | |_^
   = note: inside `std::thread::LocalKey::<Local>::try_with::<[closure@src/main.rs:20:20: 20:28], ()>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/thread/local.rs:269:32: 269:50
   = note: inside `std::thread::LocalKey::<Local>::with::<[closure@src/main.rs:20:20: 20:28], ()>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/thread/local.rs:246:9: 246:25
note: inside `<Alloc as std::alloc::GlobalAlloc>::alloc`
  --> src/main.rs:20:9
   |
20 |         LOCAL.with(|_local| {});
   |         ^^^^^^^^^^^^^^^^^^^^^^^
note: inside `_::__rust_alloc`
  --> src/main.rs:4:15
   |
3  | #[global_allocator]
   | ------------------- in this procedural macro expansion
4  | static ALLOC: Alloc = Alloc;
   |               ^^^^^
   = note: inside `std::alloc::alloc` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/alloc.rs:98:9: 98:52
   = note: inside `std::alloc::Global::alloc_impl` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/alloc.rs:181:73: 181:86
   = note: inside `<std::alloc::Global as std::alloc::Allocator>::allocate` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/alloc.rs:241:9: 241:39
   = note: inside `alloc::raw_vec::finish_grow::<std::alloc::Global>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:485:9: 485:35
   = note: inside `alloc::raw_vec::RawVec::<(*mut u8, unsafe extern "C" fn(*mut u8))>::grow_amortized` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:404:19: 404:82
   = note: inside `alloc::raw_vec::RawVec::<(*mut u8, unsafe extern "C" fn(*mut u8))>::reserve_for_push` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:302:24: 302:51
   = note: inside `std::vec::Vec::<(*mut u8, unsafe extern "C" fn(*mut u8))>::push` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:1829:13: 1829:48
   = note: inside `std::sys::unix::thread_local_dtor::register_dtor` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/unix/thread_local_dtor.rs:73:5: 73:25
   = note: inside `std::thread::local_impl::Key::<Local>::try_register_dtor` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/common/thread_local/fast_local.rs:207:26: 207:88
   = note: inside `std::thread::local_impl::Key::<Local>::try_initialize::<[closure@/Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/common/thread_local/fast_local.rs:91:31: 91:38]>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/common/thread_local/fast_local.rs:188:48: 188:72
   = note: inside `std::thread::local_impl::Key::<Local>::get::<[closure@/Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/common/thread_local/fast_local.rs:91:31: 91:38]>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/sys/common/thread_local/fast_local.rs:173:25: 173:50
note: inside `LOCAL::__getit`
  --> src/main.rs:14:1
   |
14 | / thread_local! {
15 | |     static LOCAL: Local = Local;
16 | | }
   | |_^
   = note: inside `std::thread::LocalKey::<Local>::try_with::<[closure@src/main.rs:20:20: 20:28], ()>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/thread/local.rs:269:32: 269:50
   = note: inside `std::thread::LocalKey::<Local>::with::<[closure@src/main.rs:20:20: 20:28], ()>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/thread/local.rs:246:9: 246:25
note: inside `<Alloc as std::alloc::GlobalAlloc>::alloc`
  --> src/main.rs:20:9
   |
20 |         LOCAL.with(|_local| {});
   |         ^^^^^^^^^^^^^^^^^^^^^^^
note: inside `_::__rust_alloc`
  --> src/main.rs:4:15
   |
3  | #[global_allocator]
   | ------------------- in this procedural macro expansion
4  | static ALLOC: Alloc = Alloc;
   |               ^^^^^
   = note: inside `std::alloc::alloc` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/alloc.rs:98:9: 98:52
   = note: inside `std::alloc::Global::alloc_impl` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/alloc.rs:181:73: 181:86
   = note: inside `<std::alloc::Global as std::alloc::Allocator>::allocate` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/alloc.rs:241:9: 241:39
   = note: inside `alloc::raw_vec::RawVec::<u8>::allocate_in` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:184:45: 184:67
   = note: inside `alloc::raw_vec::RawVec::<u8>::with_capacity_in` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/raw_vec.rs:130:9: 130:69
   = note: inside `std::vec::Vec::<u8>::with_capacity_in` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:670:20: 670:61
   = note: inside `std::vec::Vec::<u8>::with_capacity` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:479:9: 479:49
   = note: inside `std::ffi::CString::new::spec_new_impl_bytes` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/ffi/c_str.rs:287:30: 287:58
   = note: inside `<&str as std::ffi::CString::new::SpecNewImpl>::spec_new_impl` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/ffi/c_str.rs:306:17: 306:53
   = note: inside `std::ffi::CString::new::<&str>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/ffi/c_str.rs:316:9: 316:26
   = note: inside `std::rt::init` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/rt.rs:104:53: 104:73
   = note: inside closure at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/rt.rs:147:42: 147:67
   = note: inside `std::panicking::r#try::do_call::<[closure@std::rt::lang_start_internal::{closure#1}], ()>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/panicking.rs:524:40: 524:43
   = note: inside `std::panicking::r#try::<(), [closure@std::rt::lang_start_internal::{closure#1}]>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/panicking.rs:488:19: 488:81
   = note: inside `std::panic::catch_unwind::<[closure@std::rt::lang_start_internal::{closure#1}], ()>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/panic.rs:142:14: 142:33
   = note: inside `std::rt::lang_start_internal` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/rt.rs:147:5: 147:70
   = note: inside `std::rt::lang_start::<()>` at /Users/nv/.rustup/toolchains/nightly-aarch64-apple-darwin/lib/rustlib/src/rust/library/std/src/rt.rs:165:17: 170:6
   = note: this error originates in the macro `$crate::thread::local_impl::thread_local_inner` which comes from the expansion of the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to previous error

It appears the UB is caused by register_dtor calling Vec::push and thus getting multiple mutable references simultaneously.

Meta

rustc --version --verbose:

rustc 1.72.1 (d5c2e9c34 2023-09-13)
binary: rustc
commit-hash: d5c2e9c342b358556da91d61ed4133f6f50fc0c3
commit-date: 2023-09-13
host: aarch64-apple-darwin
release: 1.72.1
LLVM version: 16.0.5

rustc +nightly --version --verbose:

rustc 1.75.0-nightly (2e5a9dd6c 2023-10-02)
binary: rustc
commit-hash: 2e5a9dd6c9eaa42f0684b4b760bd68fc27cbe51b
commit-date: 2023-10-02
host: aarch64-apple-darwin
release: 1.75.0-nightly
LLVM version: 17.0.2

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-allocatorsArea: Custom and system allocatorsC-bugCategory: This is a bug.P-highHigh 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