Skip to content

Commit 6501ac1

Browse files
committed
HistoryBuffer::write: Fix use-after-free in case of a panicking drop implementation
1 parent 7a05f0e commit 6501ac1

1 file changed

Lines changed: 44 additions & 2 deletions

File tree

src/history_buf.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,13 @@ impl<T, S: HistoryBufStorage<T> + ?Sized> HistoryBufInner<T, S> {
385385

386386
/// Writes an element to the buffer, overwriting the oldest value.
387387
pub fn write(&mut self, t: T) {
388+
#[allow(unused_assignments)]
389+
let tmp;
388390
if self.filled {
389-
// Drop the old before we overwrite it.
390-
unsafe { ptr::drop_in_place(self.data.borrow_mut()[self.write_at].as_mut_ptr()) }
391+
// Copy the old so that it is dropped at the end
392+
// We don't drop it now so that a panic in its destructor doesn't
393+
// lead to an invalid state
394+
tmp = unsafe { ptr::read(self.data.borrow_mut()[self.write_at].as_mut_ptr()) };
391395
}
392396
self.data.borrow_mut()[self.write_at] = MaybeUninit::new(t);
393397

@@ -1032,6 +1036,44 @@ mod tests {
10321036
assert_eq!(Dropper::count(), 0);
10331037
}
10341038

1039+
#[test]
1040+
fn test_use_after_free_write() {
1041+
// See https://github.com/rust-embedded/heapless/issues/659
1042+
1043+
static COUNT: AtomicI32 = AtomicI32::new(0);
1044+
1045+
#[derive(Debug)]
1046+
struct Dropper(bool);
1047+
1048+
impl Dropper {
1049+
fn new(should_panic: bool) -> Self {
1050+
COUNT.fetch_add(1, Ordering::Relaxed);
1051+
Self(should_panic)
1052+
}
1053+
fn count() -> i32 {
1054+
COUNT.load(Ordering::Relaxed)
1055+
}
1056+
}
1057+
impl Drop for Dropper {
1058+
fn drop(&mut self) {
1059+
COUNT.fetch_sub(1, Ordering::Relaxed);
1060+
assert!(!self.0, "Testing panicking");
1061+
}
1062+
}
1063+
1064+
let mut histbuf = HistoryBuf::<Dropper, 5>::new();
1065+
histbuf.write(Dropper::new(true));
1066+
histbuf.write(Dropper::new(false));
1067+
histbuf.write(Dropper::new(false));
1068+
histbuf.write(Dropper::new(false));
1069+
histbuf.write(Dropper::new(false));
1070+
let mut unwind_safe = AssertUnwindSafe(&mut histbuf);
1071+
1072+
catch_unwind(move || unwind_safe.write(Dropper::new(false))).unwrap_err();
1073+
drop(histbuf);
1074+
assert_eq!(Dropper::count(), 0);
1075+
}
1076+
10351077
fn _test_variance<'a: 'b, 'b>(x: HistoryBuf<&'a (), 42>) -> HistoryBuf<&'b (), 42> {
10361078
x
10371079
}

0 commit comments

Comments
 (0)