]> git.lizzy.rs Git - rust.git/commitdiff
Fix illegal instruction caused by overflow in channel cloning
authorKeith Yeung <kungfukeith11@gmail.com>
Sun, 28 Aug 2016 22:48:56 +0000 (15:48 -0700)
committerKeith Yeung <kungfukeith11@gmail.com>
Mon, 29 Aug 2016 17:27:59 +0000 (10:27 -0700)
src/libstd/sync/mpsc/shared.rs
src/libstd/sync/mpsc/sync.rs

index baa4db7e5c0fa3e8e08b94395ee7c572fe4e2ac4..2a9618251ff52ef21597c733eeae677ff9c2f8a2 100644 (file)
@@ -21,6 +21,7 @@
 pub use self::Failure::*;
 
 use core::cmp;
+use core::intrinsics::abort;
 use core::isize;
 
 use sync::atomic::{AtomicUsize, AtomicIsize, AtomicBool, Ordering};
@@ -34,6 +35,7 @@
 
 const DISCONNECTED: isize = isize::MIN;
 const FUDGE: isize = 1024;
+const MAX_REFCOUNT: usize = (isize::MAX) as usize;
 #[cfg(test)]
 const MAX_STEALS: isize = 5;
 #[cfg(not(test))]
@@ -46,7 +48,7 @@ pub struct Packet<T> {
     to_wake: AtomicUsize, // SignalToken for wake up
 
     // The number of channels which are currently using this packet.
-    channels: AtomicIsize,
+    channels: AtomicUsize,
 
     // See the discussion in Port::drop and the channel send methods for what
     // these are used for
@@ -72,7 +74,7 @@ pub fn new() -> Packet<T> {
             cnt: AtomicIsize::new(0),
             steals: 0,
             to_wake: AtomicUsize::new(0),
-            channels: AtomicIsize::new(2),
+            channels: AtomicUsize::new(2),
             port_dropped: AtomicBool::new(false),
             sender_drain: AtomicIsize::new(0),
             select_lock: Mutex::new(()),
@@ -340,7 +342,14 @@ pub fn try_recv(&mut self) -> Result<T, Failure> {
     // Prepares this shared packet for a channel clone, essentially just bumping
     // a refcount.
     pub fn clone_chan(&mut self) {
-        self.channels.fetch_add(1, Ordering::SeqCst);
+        let old_count = self.channels.fetch_add(1, Ordering::SeqCst);
+
+        // See comments on Arc::clone() on why we do this (for `mem::forget`).
+        if old_count > MAX_REFCOUNT {
+            unsafe {
+                abort();
+            }
+        }
     }
 
     // Decrement the reference count on a channel. This is called whenever a
index 9985daaba8f69a2eac4772cf2c91e11517b0f077..1d16e002a2bef460ce2d1cdb16397606e49f035e 100644 (file)
@@ -36,6 +36,8 @@
 pub use self::Failure::*;
 use self::Blocker::*;
 
+use core::intrinsics::abort;
+use core::isize;
 use core::mem;
 use core::ptr;
 
@@ -45,6 +47,8 @@
 use sync::{Mutex, MutexGuard};
 use time::Instant;
 
+const MAX_REFCOUNT: usize = (isize::MAX) as usize;
+
 pub struct Packet<T> {
     /// Only field outside of the mutex. Just done for kicks, but mainly because
     /// the other shared channel already had the code implemented
@@ -350,7 +354,14 @@ fn wakeup_senders(&self, waited: bool, mut guard: MutexGuard<State<T>>) {
     // Prepares this shared packet for a channel clone, essentially just bumping
     // a refcount.
     pub fn clone_chan(&self) {
-        self.channels.fetch_add(1, Ordering::SeqCst);
+        let old_count = self.channels.fetch_add(1, Ordering::SeqCst);
+
+        // See comments on Arc::clone() on why we do this (for `mem::forget`).
+        if old_count > MAX_REFCOUNT {
+            unsafe {
+                abort();
+            }
+        }
     }
 
     pub fn drop_chan(&self) {