Skip to content

Commit 9a3c061

Browse files
committed
HistoryBuffer::write: Fix use-after-free in case of a panicking drop implementation
1 parent 76259c4 commit 9a3c061

1 file changed

Lines changed: 43 additions & 2 deletions

File tree

src/history_buf.rs

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -385,9 +385,12 @@ 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+
let _tmp;
388389
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()) }
390+
// Copy the old so that it is dropped at the end
391+
// We don't drop it now so that a panic in its destructor doesn't
392+
// lead to an invalid state
393+
_tmp = unsafe { ptr::read(self.data.borrow_mut()[self.write_at].as_mut_ptr()) };
391394
}
392395
self.data.borrow_mut()[self.write_at] = MaybeUninit::new(t);
393396

@@ -1032,6 +1035,44 @@ mod tests {
10321035
assert_eq!(Dropper::count(), 0);
10331036
}
10341037

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

0 commit comments

Comments
 (0)