@@ -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,46 @@ mod tests {
10321035 assert_eq ! ( Dropper :: count( ) , 0 ) ;
10331036 }
10341037
1038+ #[ test]
1039+ fn test_use_after_free_write ( ) {
1040+ // This tests that a panic in in a downstream drop implementation does not lead to an
1041+ // inconsistent state during unwinding that could lead to undefined behaviour.
1042+ // See https://github.com/rust-embedded/heapless/issues/659
1043+
1044+ static COUNT : AtomicI32 = AtomicI32 :: new ( 0 ) ;
1045+
1046+ #[ derive( Debug ) ]
1047+ struct Dropper ( bool ) ;
1048+
1049+ impl Dropper {
1050+ fn new ( should_panic : bool ) -> Self {
1051+ COUNT . fetch_add ( 1 , Ordering :: Relaxed ) ;
1052+ Self ( should_panic)
1053+ }
1054+ fn count ( ) -> i32 {
1055+ COUNT . load ( Ordering :: Relaxed )
1056+ }
1057+ }
1058+ impl Drop for Dropper {
1059+ fn drop ( & mut self ) {
1060+ COUNT . fetch_sub ( 1 , Ordering :: Relaxed ) ;
1061+ assert ! ( !self . 0 , "Testing panicking" ) ;
1062+ }
1063+ }
1064+
1065+ let mut histbuf = HistoryBuf :: < Dropper , 5 > :: new ( ) ;
1066+ histbuf. write ( Dropper :: new ( true ) ) ;
1067+ histbuf. write ( Dropper :: new ( false ) ) ;
1068+ histbuf. write ( Dropper :: new ( false ) ) ;
1069+ histbuf. write ( Dropper :: new ( false ) ) ;
1070+ histbuf. write ( Dropper :: new ( false ) ) ;
1071+ let mut unwind_safe = AssertUnwindSafe ( & mut histbuf) ;
1072+
1073+ catch_unwind ( move || unwind_safe. write ( Dropper :: new ( false ) ) ) . unwrap_err ( ) ;
1074+ drop ( histbuf) ;
1075+ assert_eq ! ( Dropper :: count( ) , 0 ) ;
1076+ }
1077+
10351078 fn _test_variance < ' a : ' b , ' b > ( x : HistoryBuf < & ' a ( ) , 42 > ) -> HistoryBuf < & ' b ( ) , 42 > {
10361079 x
10371080 }
0 commit comments