Skip to content
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

std: Fix segfaulting Weak<!>::new #49248

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 48 additions & 7 deletions src/liballoc/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -991,11 +991,13 @@ impl<T> Weak<T> {
pub fn new() -> Weak<T> {
unsafe {
Weak {
ptr: Box::into_raw_non_null(box ArcInner {
// Note that `box` isn't used specifically due to how it handles
// uninhabited types, see #48493 for more info.
ptr: Box::into_raw_non_null(Box::new(ArcInner {
strong: atomic::AtomicUsize::new(0),
weak: atomic::AtomicUsize::new(1),
data: uninitialized(),
}),
})),
}
}
}
Expand Down Expand Up @@ -1032,7 +1034,7 @@ impl<T: ?Sized> Weak<T> {
pub fn upgrade(&self) -> Option<Arc<T>> {
// We use a CAS loop to increment the strong count instead of a
// fetch_add because once the count hits 0 it must never be above 0.
let inner = self.inner();
let inner = self.inner()?;

// Relaxed load because any write of 0 that we can observe
// leaves the field in a permanently zero state (so a
Expand Down Expand Up @@ -1061,9 +1063,36 @@ impl<T: ?Sized> Weak<T> {
}

#[inline]
fn inner(&self) -> &ArcInner<T> {
fn inner(&self) -> Option<&ArcInner<T>> {
// See comments above for why this is "safe"
unsafe { self.ptr.as_ref() }
//
// Note that the `Option` being returned here is pretty tricky, but is
// in relation to #48493. You can create an instance of `Weak<!>` which
// internally contains `NonNull<ArcInner<!>>`. Now the layout of
// `ArcInner<!>` directly embeds `!` so rustc correctly deduces that the
// size of `ArcInner<!>` is zero. This means that `Box<ArcInner<!>>`
// which we create in `Weak::new()` is actually the pointer 1 (pointers
// to ZST types are currently `1 as *mut _`).
//
// As a result we may not actually have an `&ArcInner<T>` to hand out
// here. That type can't actually exist for all `T`, such as `!`, so we
// can only hand out a safe reference if we've actually got one to hand
// out, aka if the size of the value behind the pointer is nonzero.
//
// Note that this adds an extra branch on methods like `clone` with
// trait objects like `Weak<Any>`, but we should be able to recover the
// original performance as soon as we can change `ArcInner` to store a
// `MaybeInitialized<!>` internally instead of a `!` directly. That way
// our allocation will *always* be allocated and we won't need this
// branch.
unsafe {
let ptr = self.ptr.as_ref();
if mem::size_of_val(ptr) == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still inherently bogus since it's creating a reference to an uninhabited type, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we're actually safe here in the sense that we're not telling LLVM this is illegal today and this compiles ok, but it's certainly standing on quite the precipice. If you'd prefer though I could probably add enough conditions here to skip this check if T isn't a dynamically sized type and only run this check if it's a DST.

None
} else {
Some(ptr)
}
}
}
}

Expand All @@ -1082,11 +1111,19 @@ impl<T: ?Sized> Clone for Weak<T> {
/// ```
#[inline]
fn clone(&self) -> Weak<T> {
let inner = match self.inner() {
Some(i) => i,
// If we're something like `Weak<!>` then our destructor doesn't do
// anything anyway so return the same pointer without doing any
// work.
None => return Weak { ptr: self.ptr }
};

// See comments in Arc::clone() for why this is relaxed. This can use a
// fetch_add (ignoring the lock) because the weak count is only locked
// where are *no other* weak pointers in existence. (So we can't be
// running this code in that case).
let old_size = self.inner().weak.fetch_add(1, Relaxed);
let old_size = inner.weak.fetch_add(1, Relaxed);

// See comments in Arc::clone() for why we do this (for mem::forget).
if old_size > MAX_REFCOUNT {
Expand Down Expand Up @@ -1148,6 +1185,10 @@ impl<T: ?Sized> Drop for Weak<T> {
/// ```
fn drop(&mut self) {
let ptr = self.ptr.as_ptr();
let inner = match self.inner() {
Some(inner) => inner,
None => return, // nothing to change, skip everything
};

// If we find out that we were the last weak pointer, then its time to
// deallocate the data entirely. See the discussion in Arc::drop() about
Expand All @@ -1157,7 +1198,7 @@ impl<T: ?Sized> Drop for Weak<T> {
// weak count can only be locked if there was precisely one weak ref,
// meaning that drop could only subsequently run ON that remaining weak
// ref, which can only happen after the lock is released.
if self.inner().weak.fetch_sub(1, Release) == 1 {
if inner.weak.fetch_sub(1, Release) == 1 {
atomic::fence(Acquire);
unsafe {
Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr))
Expand Down
109 changes: 51 additions & 58 deletions src/liballoc/rc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ impl<T> Rc<T> {
// the strong count, and then remove the implicit "strong weak"
// pointer while also handling drop logic by just crafting a
// fake Weak.
this.dec_strong();
this.inner().dec_strong();
let _weak = Weak { ptr: this.ptr };
forget(this);
Ok(val)
Expand Down Expand Up @@ -448,7 +448,7 @@ impl<T: ?Sized> Rc<T> {
/// ```
#[stable(feature = "rc_weak", since = "1.4.0")]
pub fn downgrade(this: &Self) -> Weak<T> {
this.inc_weak();
this.inner().inc_weak();
Weak { ptr: this.ptr }
}

Expand All @@ -469,7 +469,7 @@ impl<T: ?Sized> Rc<T> {
#[inline]
#[stable(feature = "rc_counts", since = "1.15.0")]
pub fn weak_count(this: &Self) -> usize {
this.weak() - 1
this.inner().weak.get() - 1
}

/// Gets the number of strong (`Rc`) pointers to this value.
Expand All @@ -487,7 +487,7 @@ impl<T: ?Sized> Rc<T> {
#[inline]
#[stable(feature = "rc_counts", since = "1.15.0")]
pub fn strong_count(this: &Self) -> usize {
this.strong()
this.inner().strong.get()
}

/// Returns true if there are no other `Rc` or [`Weak`][weak] pointers to
Expand Down Expand Up @@ -557,6 +557,12 @@ impl<T: ?Sized> Rc<T> {
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
this.ptr.as_ptr() == other.ptr.as_ptr()
}

fn inner(&self) -> &RcBox<T> {
unsafe {
self.ptr.as_ref()
}
}
}

impl<T: Clone> Rc<T> {
Expand Down Expand Up @@ -600,10 +606,10 @@ impl<T: Clone> Rc<T> {
unsafe {
let mut swap = Rc::new(ptr::read(&this.ptr.as_ref().value));
mem::swap(this, &mut swap);
swap.dec_strong();
swap.inner().dec_strong();
// Remove implicit strong-weak ref (no need to craft a fake
// Weak here -- we know other Weaks can clean up for us)
swap.dec_weak();
swap.inner().dec_weak();
forget(swap);
}
}
Expand Down Expand Up @@ -836,16 +842,16 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc<T> {
unsafe {
let ptr = self.ptr.as_ptr();

self.dec_strong();
if self.strong() == 0 {
self.inner().dec_strong();
if self.inner().strong.get() == 0 {
// destroy the contained object
ptr::drop_in_place(self.ptr.as_mut());

// remove the implicit "strong weak" pointer now that we've
// destroyed the contents.
self.dec_weak();
self.inner().dec_weak();

if self.weak() == 0 {
if self.inner().weak.get() == 0 {
Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr));
}
}
Expand All @@ -871,7 +877,7 @@ impl<T: ?Sized> Clone for Rc<T> {
/// ```
#[inline]
fn clone(&self) -> Rc<T> {
self.inc_strong();
self.inner().inc_strong();
Rc { ptr: self.ptr, phantom: PhantomData }
}
}
Expand Down Expand Up @@ -1190,11 +1196,13 @@ impl<T> Weak<T> {
pub fn new() -> Weak<T> {
unsafe {
Weak {
ptr: Box::into_raw_non_null(box RcBox {
// Note that `box` isn't used specifically due to how it handles
// uninhabited types, see #48493 for more info.
ptr: Box::into_raw_non_null(Box::new(RcBox {
strong: Cell::new(0),
weak: Cell::new(1),
value: uninitialized(),
}),
})),
}
}
}
Expand Down Expand Up @@ -1229,13 +1237,27 @@ impl<T: ?Sized> Weak<T> {
/// ```
#[stable(feature = "rc_weak", since = "1.4.0")]
pub fn upgrade(&self) -> Option<Rc<T>> {
if self.strong() == 0 {
let inner = self.inner()?;
if inner.strong.get() == 0 {
None
} else {
self.inc_strong();
inner.inc_strong();
Some(Rc { ptr: self.ptr, phantom: PhantomData })
}
}

fn inner(&self) -> Option<&RcBox<T>> {
// see the comment in `arc.rs` for why this returns an `Option` rather
// than a `&RcBox<T>`
unsafe {
let ptr = self.ptr.as_ref();
if mem::size_of_val(ptr) == 0 {
None
} else {
Some(ptr)
}
}
}
}

#[stable(feature = "rc_weak", since = "1.4.0")]
Expand Down Expand Up @@ -1267,11 +1289,15 @@ impl<T: ?Sized> Drop for Weak<T> {
fn drop(&mut self) {
unsafe {
let ptr = self.ptr.as_ptr();
let inner = match self.inner() {
Some(inner) => inner,
None => return,
};

self.dec_weak();
inner.dec_weak();
// the weak count starts at 1, and will only go to zero if all
// the strong pointers have disappeared.
if self.weak() == 0 {
if inner.weak.get() == 0 {
Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr));
}
}
Expand All @@ -1293,7 +1319,9 @@ impl<T: ?Sized> Clone for Weak<T> {
/// ```
#[inline]
fn clone(&self) -> Weak<T> {
self.inc_weak();
if let Some(inner) = self.inner() {
inner.inc_weak();
}
Weak { ptr: self.ptr }
}
}
Expand Down Expand Up @@ -1335,56 +1363,21 @@ impl<T> Default for Weak<T> {
// This should have negligible overhead since you don't actually need to
// clone these much in Rust thanks to ownership and move-semantics.

#[doc(hidden)]
trait RcBoxPtr<T: ?Sized> {
fn inner(&self) -> &RcBox<T>;

#[inline]
fn strong(&self) -> usize {
self.inner().strong.get()
}

#[inline]
impl<T: ?Sized> RcBox<T> {
fn inc_strong(&self) {
self.inner().strong.set(self.strong().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
self.strong.set(self.strong.get().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
}

#[inline]
fn dec_strong(&self) {
self.inner().strong.set(self.strong() - 1);
}

#[inline]
fn weak(&self) -> usize {
self.inner().weak.get()
self.strong.set(self.strong.get() - 1);
}

#[inline]
fn inc_weak(&self) {
self.inner().weak.set(self.weak().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
self.weak.set(self.weak.get().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
}

#[inline]
fn dec_weak(&self) {
self.inner().weak.set(self.weak() - 1);
}
}

impl<T: ?Sized> RcBoxPtr<T> for Rc<T> {
#[inline(always)]
fn inner(&self) -> &RcBox<T> {
unsafe {
self.ptr.as_ref()
}
}
}

impl<T: ?Sized> RcBoxPtr<T> for Weak<T> {
#[inline(always)]
fn inner(&self) -> &RcBox<T> {
unsafe {
self.ptr.as_ref()
}
self.weak.set(self.weak.get() - 1);
}
}

Expand Down
47 changes: 47 additions & 0 deletions src/test/run-pass/void-collections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::collections::HashMap;
use std::collections::BTreeMap;

#[derive(Eq, PartialEq, Hash, PartialOrd, Ord)]
enum Void {}

trait Foo {}

impl<T> Foo for T {}

fn main() {
std::rc::Weak::<Void>::new();
std::rc::Weak::<Void>::new().clone();
(std::rc::Weak::<Void>::new() as std::rc::Weak<Foo>);
(std::rc::Weak::<Void>::new() as std::rc::Weak<Foo>).clone();
std::sync::Weak::<Void>::new();
(std::sync::Weak::<Void>::new() as std::sync::Weak<Foo>);
(std::sync::Weak::<Void>::new() as std::sync::Weak<Foo>).clone();

let mut h: HashMap<Void, Void> = HashMap::new();
assert_eq!(h.len(), 0);
assert_eq!(h.iter().count(), 0);
assert_eq!(h.iter_mut().count(), 0);
assert_eq!(h.into_iter().count(), 0);

let mut h: BTreeMap<Void, Void> = BTreeMap::new();
assert_eq!(h.len(), 0);
assert_eq!(h.iter().count(), 0);
assert_eq!(h.iter_mut().count(), 0);
assert_eq!(h.into_iter().count(), 0);

let mut h: Vec<Void> = Vec::new();
assert_eq!(h.len(), 0);
assert_eq!(h.iter().count(), 0);
assert_eq!(h.iter_mut().count(), 0);
assert_eq!(h.into_iter().count(), 0);
}