Skip to content

Commit e6f2f89

Browse files
authored
Merge pull request #33 from frengor/dev
* Update to 0.6.1 * Fix use-after-free related to weak pointers
2 parents aa024b4 + 49f5af1 commit e6f2f89

File tree

7 files changed

+61
-17
lines changed

7 files changed

+61
-17
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ edition.workspace = true
1414
members = ["derive"]
1515

1616
[workspace.package]
17-
version = "0.6.0" # Also update in [dependencies.rust-cc-derive.version]
17+
version = "0.6.1" # Also update in [dependencies.rust-cc-derive.version]
1818
authors = ["fren_gor <goro@frengor.com>"]
1919
repository = "https://github.com/frengor/rust-cc"
2020
categories = ["memory-management", "no-std"]
@@ -49,7 +49,7 @@ std = ["slotmap?/std", "thiserror/std"]
4949
pedantic-debug-assertions = []
5050

5151
[dependencies]
52-
rust-cc-derive = { path = "./derive", version = "=0.6.0", optional = true }
52+
rust-cc-derive = { path = "./derive", version = "=0.6.1", optional = true }
5353
slotmap = { version = "1.0", optional = true }
5454
thiserror = { version = "1.0", package = "thiserror-core", default-features = false }
5555

src/cc.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ impl<T: Trace> Cc<T> {
7777
let cc = ManuallyDrop::new(self); // Never drop the Cc
7878

7979
if cc.strong_count() != 1 {
80+
// cc is not unique
8081
// No need to access the state here
8182
return Err(ManuallyDrop::into_inner(cc));
8283
}
@@ -94,14 +95,15 @@ impl<T: Trace> Cc<T> {
9495

9596
remove_from_list(cc.inner.cast());
9697

97-
// Disable upgrading weak ptrs
98+
// SAFETY: cc is unique
99+
let t = unsafe { ptr::read(cc.inner().get_elem()) };
100+
let layout = cc.inner().layout();
101+
102+
// Disable upgrading weak ptrs (after having read the layout)
98103
#[cfg(feature = "weak-ptrs")]
99104
cc.inner().drop_metadata();
100-
// There's no reason here to call CounterMarker::set_dropped, since the pointed value will not be dropped
105+
// There's no reason here to call CounterMarker::set_dropped, since the pointed value will not be dropped and the allocation will be freed
101106

102-
// SAFETY: cc is unique and no weak pointer can be upgraded
103-
let t = unsafe { ptr::read(cc.inner().get_elem()) };
104-
let layout = cc.inner().layout();
105107
// SAFETY: cc is unique, is not inside any list and no weak pointer can be upgraded
106108
unsafe {
107109
cc_dealloc(cc.inner, layout, state);
@@ -304,8 +306,6 @@ impl<T: ?Sized + Trace> Drop for Cc<T> {
304306
// Set the object as dropped before dropping and deallocating it
305307
// This feature is used only in weak pointers, so do this only if they're enabled
306308
self.counter_marker().set_dropped(true);
307-
308-
self.inner().drop_metadata();
309309
}
310310

311311
// SAFETY: we're the only one to have a pointer to this allocation
@@ -318,6 +318,10 @@ impl<T: ?Sized + Trace> Drop for Cc<T> {
318318
"Trying to deallocate a CcBox with a reference counter > 0"
319319
);
320320

321+
// Free metadata (the layout has already been read). Necessary only with weak pointers enabled
322+
#[cfg(feature = "weak-ptrs")]
323+
self.inner().drop_metadata();
324+
321325
cc_dealloc(self.inner, layout, state);
322326
}
323327
// _dropping_guard is dropped here, resetting state.dropping

src/lib.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ cleanable.clean();
137137
#![cfg_attr(not(feature = "std"), no_std)]
138138

139139
#![deny(rustdoc::broken_intra_doc_links)]
140-
#![allow(clippy::thread_local_initializer_can_be_made_const)]
140+
#![allow(clippy::missing_const_for_thread_local)]
141141

142142
#[cfg(all(not(feature = "std"), not(feature = "nightly")))]
143143
compile_error!("Feature \"std\" cannot be disabled without enabling feature \"nightly\" (due to #[thread_local] not being stable).");
@@ -403,9 +403,6 @@ fn deallocate_list(to_deallocate_list: LinkedList, state: &State) {
403403
unsafe {
404404
debug_assert!(ptr.as_ref().counter_marker().is_in_list());
405405

406-
#[cfg(feature = "weak-ptrs")]
407-
ptr.as_ref().drop_metadata();
408-
409406
CcBox::drop_inner(ptr.cast());
410407
};
411408

@@ -428,6 +425,12 @@ fn deallocate_list(to_deallocate_list: LinkedList, state: &State) {
428425
// and then the allocation gets deallocated immediately after.
429426
unsafe {
430427
let layout = ptr.as_ref().layout();
428+
429+
// Free metadata only after having read the layout from the vtable
430+
// Necessary only with weak pointers enabled
431+
#[cfg(feature = "weak-ptrs")]
432+
ptr.as_ref().drop_metadata();
433+
431434
cc_dealloc(ptr, layout, state);
432435
}
433436
});

src/tests/weak/mod.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,3 +411,38 @@ fn weak_new_ptr_eq() {
411411
assert!(Weak::ptr_eq(&cc.downgrade(), &cc.downgrade()));
412412
assert!(!Weak::ptr_eq(&cc.downgrade(), &other_cc.downgrade()));
413413
}
414+
415+
#[test]
416+
fn drop_zero_weak_counter() {
417+
reset_state();
418+
419+
let cc = Cc::new(5u32);
420+
let _ = cc.downgrade();
421+
drop(cc);
422+
}
423+
424+
#[test]
425+
fn cyclic_drop_zero_weak_counter() {
426+
reset_state();
427+
428+
struct Cyclic {
429+
cyclic: RefCell<Option<Cc<Cyclic>>>,
430+
}
431+
432+
unsafe impl Trace for Cyclic {
433+
fn trace(&self, ctx: &mut Context<'_>) {
434+
self.cyclic.trace(ctx);
435+
}
436+
}
437+
438+
impl Finalize for Cyclic {}
439+
440+
let cc = Cc::new(Cyclic {
441+
cyclic: RefCell::new(None),
442+
});
443+
*cc.cyclic.borrow_mut() = Some(cc.clone());
444+
445+
let _ = cc.downgrade();
446+
drop(cc);
447+
collect_cycles();
448+
}

src/weak/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,13 @@ let cyclic = Cc::new_cyclic(|weak| {
286286
impl<T: Trace> Drop for PanicGuard<T> {
287287
fn drop(&mut self) {
288288
unsafe {
289-
// Deallocate only the metadata allocation
289+
let layout = self.invalid_cc.as_ref().layout();
290+
291+
// Deallocate only the metadata allocation (the layout has already been read)
290292
self.invalid_cc.as_ref().drop_metadata();
293+
291294
// Deallocate the CcBox. Use try_state to avoid panicking inside a Drop
292295
let _ = try_state(|state| {
293-
let layout = self.invalid_cc.as_ref().layout();
294296
cc_dealloc(self.invalid_cc, layout, state);
295297
});
296298
}

src/weak/weak_counter_marker.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ pub(crate) const MAX: u16 = !ACCESSIBLE_MASK; // First 15 bits to 1
1616
/// +-----------+-----------+
1717
/// ```
1818
///
19-
/// * `A` is `1` when the `Cc` is accessible, `0` otherwise
19+
/// * `A` is `1` when the `CcBox` is accessible (i.e., not deallocated), `0` otherwise
2020
/// * `B` is the weak counter
2121
#[derive(Clone, Debug)]
2222
pub(crate) struct WeakCounterMarker {

tests/cc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![allow(clippy::thread_local_initializer_can_be_made_const)]
1+
#![allow(clippy::missing_const_for_thread_local)]
22

33
use std::cell::{Cell, RefCell};
44
use std::rc::{Rc, Weak};

0 commit comments

Comments
 (0)