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