Skip to content

Commit db2af71

Browse files
committed
Auto merge of #27174 - Gankro:rc-sat, r=alexcrichton
See https://internals.rust-lang.org/t/rc-is-unsafe-mostly-on-32-bit-targets-due-to-overflow/2120 for detailed discussion of this problem.
2 parents 8b9ada5 + 22e2100 commit db2af71

File tree

2 files changed

+42
-7
lines changed

2 files changed

+42
-7
lines changed

Diff for: src/liballoc/arc.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,18 @@ use core::atomic::Ordering::{Relaxed, Release, Acquire, SeqCst};
7878
use core::fmt;
7979
use core::cmp::Ordering;
8080
use core::mem::{align_of_val, size_of_val};
81-
use core::intrinsics::drop_in_place;
81+
use core::intrinsics::{drop_in_place, abort};
8282
use core::mem;
8383
use core::nonzero::NonZero;
8484
use core::ops::{Deref, CoerceUnsized};
8585
use core::ptr;
8686
use core::marker::Unsize;
8787
use core::hash::{Hash, Hasher};
88-
use core::usize;
88+
use core::{usize, isize};
8989
use heap::deallocate;
9090

91+
const MAX_REFCOUNT: usize = (isize::MAX) as usize;
92+
9193
/// An atomically reference counted wrapper for shared state.
9294
///
9395
/// # Examples
@@ -312,7 +314,21 @@ impl<T: ?Sized> Clone for Arc<T> {
312314
// another must already provide any required synchronization.
313315
//
314316
// [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html)
315-
self.inner().strong.fetch_add(1, Relaxed);
317+
let old_size = self.inner().strong.fetch_add(1, Relaxed);
318+
319+
// However we need to guard against massive refcounts in case someone
320+
// is `mem::forget`ing Arcs. If we don't do this the count can overflow
321+
// and users will use-after free. We racily saturate to `isize::MAX` on
322+
// the assumption that there aren't ~2 billion threads incrementing
323+
// the reference count at once. This branch will never be taken in
324+
// any realistic program.
325+
//
326+
// We abort because such a program is incredibly degenerate, and we
327+
// don't care to support it.
328+
if old_size > MAX_REFCOUNT {
329+
unsafe { abort(); }
330+
}
331+
316332
Arc { _ptr: self._ptr }
317333
}
318334
}
@@ -617,7 +633,13 @@ impl<T: ?Sized> Clone for Weak<T> {
617633
// fetch_add (ignoring the lock) because the weak count is only locked
618634
// where are *no other* weak pointers in existence. (So we can't be
619635
// running this code in that case).
620-
self.inner().weak.fetch_add(1, Relaxed);
636+
let old_size = self.inner().weak.fetch_add(1, Relaxed);
637+
638+
// See comments in Arc::clone() for why we do this (for mem::forget).
639+
if old_size > MAX_REFCOUNT {
640+
unsafe { abort(); }
641+
}
642+
621643
return Weak { _ptr: self._ptr }
622644
}
623645
}

Diff for: src/liballoc/rc.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ use core::cell::Cell;
161161
use core::cmp::Ordering;
162162
use core::fmt;
163163
use core::hash::{Hasher, Hash};
164-
use core::intrinsics::{assume, drop_in_place};
164+
use core::intrinsics::{assume, drop_in_place, abort};
165165
use core::marker::{self, Unsize};
166166
use core::mem::{self, align_of, size_of, align_of_val, size_of_val, forget};
167167
use core::nonzero::NonZero;
@@ -858,6 +858,15 @@ impl<T: ?Sized+fmt::Debug> fmt::Debug for Weak<T> {
858858
}
859859
}
860860

861+
// NOTE: We checked_add here to deal with mem::forget safety. In particular
862+
// if you mem::forget Rcs (or Weaks), the ref-count can overflow, and then
863+
// you can free the allocation while outstanding Rcs (or Weaks) exist.
864+
// We abort because this is such a degenerate scenario that we don't care about
865+
// what happens -- no real program should ever experience this.
866+
//
867+
// This should have negligible overhead since you don't actually need to
868+
// clone these much in Rust thanks to ownership and move-semantics.
869+
861870
#[doc(hidden)]
862871
trait RcBoxPtr<T: ?Sized> {
863872
fn inner(&self) -> &RcBox<T>;
@@ -866,7 +875,9 @@ trait RcBoxPtr<T: ?Sized> {
866875
fn strong(&self) -> usize { self.inner().strong.get() }
867876

868877
#[inline]
869-
fn inc_strong(&self) { self.inner().strong.set(self.strong() + 1); }
878+
fn inc_strong(&self) {
879+
self.inner().strong.set(self.strong().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
880+
}
870881

871882
#[inline]
872883
fn dec_strong(&self) { self.inner().strong.set(self.strong() - 1); }
@@ -875,7 +886,9 @@ trait RcBoxPtr<T: ?Sized> {
875886
fn weak(&self) -> usize { self.inner().weak.get() }
876887

877888
#[inline]
878-
fn inc_weak(&self) { self.inner().weak.set(self.weak() + 1); }
889+
fn inc_weak(&self) {
890+
self.inner().weak.set(self.weak().checked_add(1).unwrap_or_else(|| unsafe { abort() }));
891+
}
879892

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

0 commit comments

Comments
 (0)