Skip to content

Commit f2b6b87

Browse files
stbuehlercramertj
authored andcommitted
Fix use-after-free on panic during ArcWake::wake_by_ref
Wrap temporary `Arc<>`s in `ManuallyDrop` early instead of calling `forget()` later: that way even during unwinding for panics it doesn't drop the refcount it doesn't actually own. Also it means `wake_by_ref` doesn't need an unwind section anymore. Same thing in `increase_refcount` (although `Arc::clone` should only abort, not unwind).
1 parent 7b86128 commit f2b6b87

File tree

2 files changed

+25
-8
lines changed

2 files changed

+25
-8
lines changed

futures-util/src/task/waker.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@ where
2222
// code here. We should guard against this by aborting.
2323

2424
unsafe fn increase_refcount<T: ArcWake>(data: *const ()) {
25-
// Retain Arc by creating a copy
26-
let arc: Arc<T> = Arc::from_raw(data as *const T);
27-
let arc_clone = arc.clone();
28-
// Forget the Arcs again, so that the refcount isn't decrased
29-
mem::forget(arc);
30-
mem::forget(arc_clone);
25+
// Retain Arc, but don't touch refcount by wrapping in ManuallyDrop
26+
let arc = mem::ManuallyDrop::new(Arc::<T>::from_raw(data as *const T));
27+
// Now increase refcount, but don't drop new refcount either
28+
let _arc_clone: mem::ManuallyDrop<_> = arc.clone();
3129
}
3230

3331
// used by `waker_ref`
@@ -43,9 +41,9 @@ unsafe fn wake_arc_raw<T: ArcWake>(data: *const ()) {
4341

4442
// used by `waker_ref`
4543
pub(super) unsafe fn wake_by_ref_arc_raw<T: ArcWake>(data: *const ()) {
46-
let arc: Arc<T> = Arc::from_raw(data as *const T);
44+
// Retain Arc, but don't touch refcount by wrapping in ManuallyDrop
45+
let arc = mem::ManuallyDrop::new(Arc::<T>::from_raw(data as *const T));
4746
ArcWake::wake_by_ref(&arc);
48-
mem::forget(arc);
4947
}
5048

5149
unsafe fn drop_arc_raw<T: ArcWake>(data: *const ()) {

futures/tests/arc_wake.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,22 @@ fn create_waker_from_arc() {
4444
drop(w1);
4545
assert_eq!(1, Arc::strong_count(&some_w));
4646
}
47+
48+
struct PanicWaker;
49+
50+
impl ArcWake for PanicWaker {
51+
fn wake_by_ref(_arc_self: &Arc<Self>) {
52+
panic!("WAKE UP");
53+
}
54+
}
55+
56+
#[test]
57+
fn proper_refcount_on_wake_panic() {
58+
let some_w = Arc::new(PanicWaker);
59+
60+
let w1: Waker = task::waker(some_w.clone());
61+
assert_eq!("WAKE UP", *std::panic::catch_unwind(|| w1.wake_by_ref()).unwrap_err().downcast::<&str>().unwrap());
62+
assert_eq!(2, Arc::strong_count(&some_w)); // some_w + w1
63+
drop(w1);
64+
assert_eq!(1, Arc::strong_count(&some_w)); // some_w
65+
}

0 commit comments

Comments
 (0)