Repository: rust-lang/futures-rs (Rust)

Issue:
`Shared` poisoning has false positives
```rust
use futures::future::{Ready, ready, Shared};
use futures_util::FutureExt;

struct S(Shared<Ready<()>>);

impl Drop for S {
    fn drop(&mut self) {
        self.0.clone().now_or_never();
        self.0.clone().now_or_never();
    }
}


#[tokio::main]
async fn main() {
    let _s = S(ready(()).shared());
    panic!("foo");
}
```

([playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=66bf7a25732d816829093ea86ec56843))

Leads to:

```text
thread 'main' panicked at 'foo', src/main.rs:17:5
note: [run with `RUST_BACKTRACE=1` environment variable to display a backtrace](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=66bf7a25732d816829093ea86ec56843#)
thread 'main' panicked at 'inner future panicked during poll', /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.19/src/future/future/shared.rs:258:25
stack backtrace:
   0:     0x5572d8afa40c - std::backtrace_rs::backtrace::libunwind::trace::h09f7e4e089375279
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
   1:     0x5572d8afa40c - std::backtrace_rs::backtrace::trace_unsynchronized::h1ec96f1c7087094e
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:     0x5572d8afa40c - std::sys_common::backtrace::_print_fmt::h317b71fc9a5cf964
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/sys_common/backtrace.rs:67:5
   3:     0x5572d8afa40c - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::he3555b48e7dfe7f0
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/sys_common/backtrace.rs:46:22
   4:     0x5572d8b1598c - core::fmt::write::h513b07ca38f4fb1b
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/core/src/fmt/mod.rs:1149:17
   5:     0x5572d8af7695 - std::io::Write::write_fmt::haf8c932b52111354
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/io/mod.rs:1697:15
   6:     0x5572d8afbf90 - std::sys_common::backtrace::_print::h195c38364780a303
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/sys_common/backtrace.rs:49:5
   7:     0x5572d8afbf90 - std::sys_common::backtrace::print::hc09dfdea923b6730
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/sys_common/backtrace.rs:36:9
   8:     0x5572d8afbf90 - std::panicking::default_hook::{{closure}}::hb2e38ec0d91046a3
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panicking.rs:211:50
   9:     0x5572d8afbb45 - std::panicking::default_hook::h60284635b0ad54a8
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panicking.rs:228:9
  10:     0x5572d8afc644 - std::panicking::rust_panic_with_hook::ha677a669fb275654
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panicking.rs:606:17
  11:     0x5572d8ae2b82 - std::panicking::begin_panic::{{closure}}::hdeace178bc11b919
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panicking.rs:526:9
  12:     0x5572d8ae2a19 - std::sys_common::backtrace::__rust_end_short_backtrace::hee59954a7cdfee75
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/sys_common/backtrace.rs:139:18
  13:     0x5572d8ae2ac3 - std::panicking::begin_panic::hec5561246fefbde6
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panicking.rs:525:12
  14:     0x5572d89fb90c - <futures_util::future::future::shared::Shared<Fut> as core::future::future::Future>::poll::h9167668814ce39fe
                               at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.19/src/future/future/shared.rs:258:25
  15:     0x5572d89fd045 - futures_util::future::future::FutureExt::now_or_never::h58327d1e53214308
                               at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.19/src/future/future/mod.rs:605:15
  16:     0x5572d8a0715f - <playground::S as core::ops::drop::Drop>::drop::hbf956478a09dba90
                               at /playground/src/main.rs:9:9
  17:     0x5572d89feb43 - core::ptr::drop_in_place<playground::S>::heb512adf05cb3887
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/core/src/ptr/mod.rs:188:1
  18:     0x5572d8a073b3 - playground::main::{{closure}}::h502ad2e50f9868a7
                               at /playground/src/main.rs:18:1
  19:     0x5572d8a06e6b - <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::h4f8459e995267e11
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/core/src/future/mod.rs:80:19
  20:     0x5572d8a03a70 - tokio::park::thread::CachedParkThread::block_on::{{closure}}::h90bb4f55de89d3d0
                               at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.16.1/src/park/thread.rs:263:54
  21:     0x5572d8a03080 - tokio::coop::with_budget::{{closure}}::h931e52988056ce26
                               at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.16.1/src/coop.rs:102:9
  22:     0x5572d89fdad8 - std::thread::local::LocalKey<T>::try_with::h8c68619aabf6f690
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/thread/local.rs:399:16
  23:     0x5572d89fd71d - std::thread::local::LocalKey<T>::with::h48ae72c098c4595a
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/thread/local.rs:375:9
  24:     0x5572d8a034f4 - tokio::coop::with_budget::hd58ac0e790fdd548
                               at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.16.1/src/coop.rs:95:5
  25:     0x5572d8a034f4 - tokio::coop::budget::h7ce03a03d513905e
                               at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.16.1/src/coop.rs:72:5
  26:     0x5572d8a034f4 - tokio::park::thread::CachedParkThread::block_on::h0a4105101d53b48c
                               at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.16.1/src/park/thread.rs:263:31
  27:     0x5572d8a05979 - tokio::runtime::enter::Enter::block_on::hd1173ca2b1ca3841
                               at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.16.1/src/runtime/enter.rs:151:13
  28:     0x5572d8a03cae - tokio::runtime::thread_pool::ThreadPool::block_on::h525b6ed0a0da9b78
                               at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.16.1/src/runtime/thread_pool/mod.rs:73:9
  29:     0x5572d8a05a92 - tokio::runtime::Runtime::block_on::h400b9c3f509cabaf
                               at /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.16.1/src/runtime/mod.rs:477:43
  30:     0x5572d8a0724a - playground::main::hd151cc835d256286
                               at /playground/src/main.rs:17:5
  31:     0x5572d89fe4db - core::ops::function::FnOnce::call_once::h72747aebcdb8fd0a
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/core/src/ops/function.rs:227:5
  32:     0x5572d89fd39e - std::sys_common::backtrace::__rust_begin_short_backtrace::hf6055d3f4d4c2d00
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/sys_common/backtrace.rs:123:18
  33:     0x5572d89fd431 - std::rt::lang_start::{{closure}}::h0276c4ff91ad8392
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/rt.rs:145:18
  34:     0x5572d8afa09b - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::h7e688d7cdfeb7e00
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/core/src/ops/function.rs:259:13
  35:     0x5572d8afa09b - std::panicking::try::do_call::h4be824d2350b44c9
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panicking.rs:406:40
  36:     0x5572d8afa09b - std::panicking::try::h0a6fc7affbe5088d
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panicking.rs:370:19
  37:     0x5572d8afa09b - std::panic::catch_unwind::h22c320f732ec805e
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panic.rs:133:14
  38:     0x5572d8afa09b - std::rt::lang_start_internal::{{closure}}::hd38309c108fe679d
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/rt.rs:128:48
  39:     0x5572d8afa09b - std::panicking::try::do_call::h8fcaf501f097a28e
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panicking.rs:406:40
  40:     0x5572d8afa09b - std::panicking::try::h20e906825f98acc1
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panicking.rs:370:19
  41:     0x5572d8afa09b - std::panic::catch_unwind::h8c5234dc632124ef
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/panic.rs:133:14
  42:     0x5572d8afa09b - std::rt::lang_start_internal::hc4dd8cd3ec4518c2
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/rt.rs:128:20
  43:     0x5572d89fd400 - std::rt::lang_start::h4e240450ca2783a1
                               at /rustc/db9d1b20bba1968c1ec1fc49616d4742c1725b4b/library/std/src/rt.rs:144:17
  44:     0x5572d8a073ec - main
  45:     0x7f2c3030b0b3 - __libc_start_main
  46:     0x5572d89fb52e - _start
  47:                0x0 - <unknown>
thread panicked while panicking. aborting.
timeout: the monitored command dumped core
/playground/tools/entrypoint.sh: line 11:     8 Illegal instruction     timeout --signal=KILL ${timeout} "$@"
```

It is possible to poll a future while panicking. In this case `Shared` thinks the inner future panicked and poisons its state. Every subsequent `poll` now leads to a panic. In certain complex applications or test scenarios this can easily lead to double-panics (SIGILL).


Discussion:

Patch:
diff --git a/futures-util/src/future/future/shared.rs b/futures-util/src/future/future/shared.rs
index d7ba49ec44..cc900ba5b6 100644
--- a/futures-util/src/future/future/shared.rs
+++ b/futures-util/src/future/future/shared.rs
@@ -262,19 +262,20 @@ where
         let waker = waker_ref(&inner.notifier);
         let mut cx = Context::from_waker(&waker);
 
-        struct Reset<'a>(&'a AtomicUsize);
+        struct Reset<'a> {
+            state: &'a AtomicUsize,
+            did_not_panic: bool,
+        }
 
         impl Drop for Reset<'_> {
             fn drop(&mut self) {
-                use std::thread;
-
-                if thread::panicking() {
-                    self.0.store(POISONED, SeqCst);
+                if !self.did_not_panic {
+                    self.state.store(POISONED, SeqCst);
                 }
             }
         }
 
-        let _reset = Reset(&inner.notifier.state);
+        let mut reset = Reset { state: &inner.notifier.state, did_not_panic: false };
 
         let output = {
             let future = unsafe {
@@ -284,12 +285,15 @@ where
                 }
             };
 
-            match future.poll(&mut cx) {
+            let poll_result = future.poll(&mut cx);
+            reset.did_not_panic = true;
+
+            match poll_result {
                 Poll::Pending => {
                     if inner.notifier.state.compare_exchange(POLLING, IDLE, SeqCst, SeqCst).is_ok()
                     {
                         // Success
-                        drop(_reset);
+                        drop(reset);
                         this.inner = Some(inner);
                         return Poll::Pending;
                     } else {
@@ -313,7 +317,7 @@ where
             waker.wake();
         }
 
-        drop(_reset); // Make borrow checker happy
+        drop(reset); // Make borrow checker happy
         drop(wakers_guard);
 
         // Safety: We're in the COMPLETE state


Test patch:
diff --git a/futures/tests/future_shared.rs b/futures/tests/future_shared.rs
index 3ceaebb5c8..20a021bc35 100644
--- a/futures/tests/future_shared.rs
+++ b/futures/tests/future_shared.rs
@@ -3,6 +3,7 @@ use futures::executor::{block_on, LocalPool};
 use futures::future::{self, FutureExt, LocalFutureObj, TryFutureExt};
 use futures::task::LocalSpawn;
 use std::cell::{Cell, RefCell};
+use std::panic::AssertUnwindSafe;
 use std::rc::Rc;
 use std::task::Poll;
 use std::thread;
@@ -194,3 +195,34 @@ fn shared_future_that_wakes_itself_until_pending_is_returned() {
     // has returned pending
     assert_eq!(block_on(futures::future::join(fut, async { proceed.set(true) })), ((), ()));
 }
+
+#[test]
+#[should_panic(expected = "inner future panicked during poll")]
+fn panic_while_poll() {
+    let fut = futures::future::poll_fn::<i8, _>(|_cx| panic!("test")).shared();
+
+    let fut_captured = fut.clone();
+    std::panic::catch_unwind(AssertUnwindSafe(|| {
+        block_on(fut_captured);
+    }))
+    .unwrap_err();
+
+    block_on(fut);
+}
+
+#[test]
+#[should_panic(expected = "test_marker")]
+fn poll_while_panic() {
+    struct S;
+
+    impl Drop for S {
+        fn drop(&mut self) {
+            let fut = futures::future::ready(1).shared();
+            assert_eq!(block_on(fut.clone()), 1);
+            assert_eq!(block_on(fut), 1);
+        }
+    }
+
+    let _s = S {};
+    panic!("test_marker");
+}