@@ -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