Skip to content

Commit 06cd4cf

Browse files
committed
std: Fix segfaulting Weak<!>::new
This commit is a fix for rust-lang#48493 where calling `Weak::new` where `T` is an uninhabited type would segfault. The cause for this issue was pretty subtle and the fix here is mostly a holdover until rust-lang#47650 is implemented. The `Weak<!>` struct internally contains a `NonNull<RcBox<!>>`. The `RcBox<!>` type is uninhabited, however, as it directly embeds the `!` type. Consequently the size of `RcBox<!>` is zero, which means that `NonNull<RcBox<!>>` always contains a pointer with a value of 1. Currently all boxes of zero-sized-types are actually pointers to the address 1 (as they shouldn't be read anyway). The problem comes about when later on we have a method called `Weak::inner` which previously returned `&RcBox<T>`. This was actually invalid because the instance of `&RcBox<T> ` never existed (only the uninitialized part). This means that when we try to actually modify `&RcBox`'s weak count in the destructor for `Weak::new` we're modifying the address 1! This ends up causing a segfault. This commit takes the strategy of modifying the `Weak::inner` method to return an `Option<&RcBox<T>>` that is `None` whenever the size of `RcBox<T>` is 0 (so it couldn't actually exist). This does unfortunately add more dispatch code to operations like `Weak<Any>::clone`. Eventually the "correct" fix for this is to have `RcBox<T>` store a union of `T` and a ZST like `()`, and that way the size of `RcBox<T>` will never be 0 and we won't need this branch. Until rust-lang#47650 is implemented, though, we can't use `union`s and unsized types together. Closes rust-lang#48493
1 parent ab8b961 commit 06cd4cf

File tree

3 files changed

+146
-65
lines changed

3 files changed

+146
-65
lines changed

src/liballoc/arc.rs

+48-7
Original file line numberDiff line numberDiff line change
@@ -991,11 +991,13 @@ impl<T> Weak<T> {
991991
pub fn new() -> Weak<T> {
992992
unsafe {
993993
Weak {
994-
ptr: Box::into_raw_non_null(box ArcInner {
994+
// Note that `box` isn't used specifically due to how it handles
995+
// uninhabited types, see #48493 for more info.
996+
ptr: Box::into_raw_non_null(Box::new(ArcInner {
995997
strong: atomic::AtomicUsize::new(0),
996998
weak: atomic::AtomicUsize::new(1),
997999
data: uninitialized(),
998-
}),
1000+
})),
9991001
}
10001002
}
10011003
}
@@ -1032,7 +1034,7 @@ impl<T: ?Sized> Weak<T> {
10321034
pub fn upgrade(&self) -> Option<Arc<T>> {
10331035
// We use a CAS loop to increment the strong count instead of a
10341036
// fetch_add because once the count hits 0 it must never be above 0.
1035-
let inner = self.inner();
1037+
let inner = self.inner()?;
10361038

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

10631065
#[inline]
1064-
fn inner(&self) -> &ArcInner<T> {
1066+
fn inner(&self) -> Option<&ArcInner<T>> {
10651067
// See comments above for why this is "safe"
1066-
unsafe { self.ptr.as_ref() }
1068+
//
1069+
// Note that the `Option` being returned here is pretty tricky, but is
1070+
// in relation to #48493. You can create an instance of `Weak<!>` which
1071+
// internally contains `NonNull<ArcInner<!>>`. Now the layout of
1072+
// `ArcInner<!>` directly embeds `!` so rustc correctly deduces that the
1073+
// size of `ArcInner<!>` is zero. This means that `Box<ArcInner<!>>`
1074+
// which we create in `Weak::new()` is actually the pointer 1 (pointers
1075+
// to ZST types are currently `1 as *mut _`).
1076+
//
1077+
// As a result we may not actually have an `&ArcInner<T>` to hand out
1078+
// here. That type can't actually exist for all `T`, such as `!`, so we
1079+
// can only hand out a safe reference if we've actually got one to hand
1080+
// out, aka if the size of the value behind the pointer is nonzero.
1081+
//
1082+
// Note that this adds an extra branch on methods like `clone` with
1083+
// trait objects like `Weak<Any>`, but we should be able to recover the
1084+
// original performance as soon as we can change `ArcInner` to store a
1085+
// `MaybeInitialized<!>` internally instead of a `!` directly. That way
1086+
// our allocation will *always* be allocated and we won't need this
1087+
// branch.
1088+
unsafe {
1089+
let ptr = self.ptr.as_ref();
1090+
if mem::size_of_val(ptr) == 0 {
1091+
None
1092+
} else {
1093+
Some(ptr)
1094+
}
1095+
}
10671096
}
10681097
}
10691098

@@ -1082,11 +1111,19 @@ impl<T: ?Sized> Clone for Weak<T> {
10821111
/// ```
10831112
#[inline]
10841113
fn clone(&self) -> Weak<T> {
1114+
let inner = match self.inner() {
1115+
Some(i) => i,
1116+
// If we're something like `Weak<!>` then our destructor doesn't do
1117+
// anything anyway so return the same pointer without doing any
1118+
// work.
1119+
None => return Weak { ptr: self.ptr }
1120+
};
1121+
10851122
// See comments in Arc::clone() for why this is relaxed. This can use a
10861123
// fetch_add (ignoring the lock) because the weak count is only locked
10871124
// where are *no other* weak pointers in existence. (So we can't be
10881125
// running this code in that case).
1089-
let old_size = self.inner().weak.fetch_add(1, Relaxed);
1126+
let old_size = inner.weak.fetch_add(1, Relaxed);
10901127

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

11521193
// If we find out that we were the last weak pointer, then its time to
11531194
// deallocate the data entirely. See the discussion in Arc::drop() about
@@ -1157,7 +1198,7 @@ impl<T: ?Sized> Drop for Weak<T> {
11571198
// weak count can only be locked if there was precisely one weak ref,
11581199
// meaning that drop could only subsequently run ON that remaining weak
11591200
// ref, which can only happen after the lock is released.
1160-
if self.inner().weak.fetch_sub(1, Release) == 1 {
1201+
if inner.weak.fetch_sub(1, Release) == 1 {
11611202
atomic::fence(Acquire);
11621203
unsafe {
11631204
Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr))

src/liballoc/rc.rs

+51-58
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ impl<T> Rc<T> {
352352
// the strong count, and then remove the implicit "strong weak"
353353
// pointer while also handling drop logic by just crafting a
354354
// fake Weak.
355-
this.dec_strong();
355+
this.inner().dec_strong();
356356
let _weak = Weak { ptr: this.ptr };
357357
forget(this);
358358
Ok(val)
@@ -448,7 +448,7 @@ impl<T: ?Sized> Rc<T> {
448448
/// ```
449449
#[stable(feature = "rc_weak", since = "1.4.0")]
450450
pub fn downgrade(this: &Self) -> Weak<T> {
451-
this.inc_weak();
451+
this.inner().inc_weak();
452452
Weak { ptr: this.ptr }
453453
}
454454

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

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

493493
/// Returns true if there are no other `Rc` or [`Weak`][weak] pointers to
@@ -557,6 +557,12 @@ impl<T: ?Sized> Rc<T> {
557557
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
558558
this.ptr.as_ptr() == other.ptr.as_ptr()
559559
}
560+
561+
fn inner(&self) -> &RcBox<T> {
562+
unsafe {
563+
self.ptr.as_ref()
564+
}
565+
}
560566
}
561567

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

839-
self.dec_strong();
840-
if self.strong() == 0 {
845+
self.inner().dec_strong();
846+
if self.inner().strong.get() == 0 {
841847
// destroy the contained object
842848
ptr::drop_in_place(self.ptr.as_mut());
843849

844850
// remove the implicit "strong weak" pointer now that we've
845851
// destroyed the contents.
846-
self.dec_weak();
852+
self.inner().dec_weak();
847853

848-
if self.weak() == 0 {
854+
if self.inner().weak.get() == 0 {
849855
Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr));
850856
}
851857
}
@@ -871,7 +877,7 @@ impl<T: ?Sized> Clone for Rc<T> {
871877
/// ```
872878
#[inline]
873879
fn clone(&self) -> Rc<T> {
874-
self.inc_strong();
880+
self.inner().inc_strong();
875881
Rc { ptr: self.ptr, phantom: PhantomData }
876882
}
877883
}
@@ -1190,11 +1196,13 @@ impl<T> Weak<T> {
11901196
pub fn new() -> Weak<T> {
11911197
unsafe {
11921198
Weak {
1193-
ptr: Box::into_raw_non_null(box RcBox {
1199+
// Note that `box` isn't used specifically due to how it handles
1200+
// uninhabited types, see #48493 for more info.
1201+
ptr: Box::into_raw_non_null(Box::new(RcBox {
11941202
strong: Cell::new(0),
11951203
weak: Cell::new(1),
11961204
value: uninitialized(),
1197-
}),
1205+
})),
11981206
}
11991207
}
12001208
}
@@ -1229,13 +1237,27 @@ impl<T: ?Sized> Weak<T> {
12291237
/// ```
12301238
#[stable(feature = "rc_weak", since = "1.4.0")]
12311239
pub fn upgrade(&self) -> Option<Rc<T>> {
1232-
if self.strong() == 0 {
1240+
let inner = self.inner()?;
1241+
if inner.strong.get() == 0 {
12331242
None
12341243
} else {
1235-
self.inc_strong();
1244+
inner.inc_strong();
12361245
Some(Rc { ptr: self.ptr, phantom: PhantomData })
12371246
}
12381247
}
1248+
1249+
fn inner(&self) -> Option<&RcBox<T>> {
1250+
// see the comment in `arc.rs` for why this returns an `Option` rather
1251+
// than a `&RcBox<T>`
1252+
unsafe {
1253+
let ptr = self.ptr.as_ref();
1254+
if mem::size_of_val(ptr) == 0 {
1255+
None
1256+
} else {
1257+
Some(ptr)
1258+
}
1259+
}
1260+
}
12391261
}
12401262

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

1271-
self.dec_weak();
1297+
inner.dec_weak();
12721298
// the weak count starts at 1, and will only go to zero if all
12731299
// the strong pointers have disappeared.
1274-
if self.weak() == 0 {
1300+
if inner.weak.get() == 0 {
12751301
Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr));
12761302
}
12771303
}
@@ -1293,7 +1319,9 @@ impl<T: ?Sized> Clone for Weak<T> {
12931319
/// ```
12941320
#[inline]
12951321
fn clone(&self) -> Weak<T> {
1296-
self.inc_weak();
1322+
if let Some(inner) = self.inner() {
1323+
inner.inc_weak();
1324+
}
12971325
Weak { ptr: self.ptr }
12981326
}
12991327
}
@@ -1335,56 +1363,21 @@ impl<T> Default for Weak<T> {
13351363
// This should have negligible overhead since you don't actually need to
13361364
// clone these much in Rust thanks to ownership and move-semantics.
13371365

1338-
#[doc(hidden)]
1339-
trait RcBoxPtr<T: ?Sized> {
1340-
fn inner(&self) -> &RcBox<T>;
1341-
1342-
#[inline]
1343-
fn strong(&self) -> usize {
1344-
self.inner().strong.get()
1345-
}
1346-
1347-
#[inline]
1366+
impl<T: ?Sized> RcBox<T> {
13481367
fn inc_strong(&self) {
1349-
self.inner().strong.set(self.strong().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
1368+
self.strong.set(self.strong.get().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
13501369
}
13511370

1352-
#[inline]
13531371
fn dec_strong(&self) {
1354-
self.inner().strong.set(self.strong() - 1);
1355-
}
1356-
1357-
#[inline]
1358-
fn weak(&self) -> usize {
1359-
self.inner().weak.get()
1372+
self.strong.set(self.strong.get() - 1);
13601373
}
13611374

1362-
#[inline]
13631375
fn inc_weak(&self) {
1364-
self.inner().weak.set(self.weak().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
1376+
self.weak.set(self.weak.get().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
13651377
}
13661378

1367-
#[inline]
13681379
fn dec_weak(&self) {
1369-
self.inner().weak.set(self.weak() - 1);
1370-
}
1371-
}
1372-
1373-
impl<T: ?Sized> RcBoxPtr<T> for Rc<T> {
1374-
#[inline(always)]
1375-
fn inner(&self) -> &RcBox<T> {
1376-
unsafe {
1377-
self.ptr.as_ref()
1378-
}
1379-
}
1380-
}
1381-
1382-
impl<T: ?Sized> RcBoxPtr<T> for Weak<T> {
1383-
#[inline(always)]
1384-
fn inner(&self) -> &RcBox<T> {
1385-
unsafe {
1386-
self.ptr.as_ref()
1387-
}
1380+
self.weak.set(self.weak.get() - 1);
13881381
}
13891382
}
13901383

src/test/run-pass/void-collections.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use std::collections::HashMap;
12+
use std::collections::BTreeMap;
13+
14+
#[derive(Eq, PartialEq, Hash, PartialOrd, Ord)]
15+
enum Void {}
16+
17+
trait Foo {}
18+
19+
impl<T> Foo for T {}
20+
21+
fn main() {
22+
std::rc::Weak::<Void>::new();
23+
std::rc::Weak::<Void>::new().clone();
24+
(std::rc::Weak::<Void>::new() as std::rc::Weak<Foo>);
25+
(std::rc::Weak::<Void>::new() as std::rc::Weak<Foo>).clone();
26+
std::sync::Weak::<Void>::new();
27+
(std::sync::Weak::<Void>::new() as std::sync::Weak<Foo>);
28+
(std::sync::Weak::<Void>::new() as std::sync::Weak<Foo>).clone();
29+
30+
let mut h: HashMap<Void, Void> = HashMap::new();
31+
assert_eq!(h.len(), 0);
32+
assert_eq!(h.iter().count(), 0);
33+
assert_eq!(h.iter_mut().count(), 0);
34+
assert_eq!(h.into_iter().count(), 0);
35+
36+
let mut h: BTreeMap<Void, Void> = BTreeMap::new();
37+
assert_eq!(h.len(), 0);
38+
assert_eq!(h.iter().count(), 0);
39+
assert_eq!(h.iter_mut().count(), 0);
40+
assert_eq!(h.into_iter().count(), 0);
41+
42+
let mut h: Vec<Void> = Vec::new();
43+
assert_eq!(h.len(), 0);
44+
assert_eq!(h.iter().count(), 0);
45+
assert_eq!(h.iter_mut().count(), 0);
46+
assert_eq!(h.into_iter().count(), 0);
47+
}

0 commit comments

Comments
 (0)