@@ -243,11 +243,18 @@ impl<T, S: VecStorage<T> + ?Sized> DequeInner<T, S> {
243243
244244 /// Clears the deque, removing all values.
245245 pub fn clear ( & mut self ) {
246- // safety: we're immediately setting a consistent empty state.
247- unsafe { self . drop_contents ( ) }
248- self . front = 0 ;
249- self . back = 0 ;
250- self . full = false ;
246+ struct Guard < ' a , T , S : VecStorage < T > + ?Sized > ( & ' a mut DequeInner < T , S > ) ;
247+ impl < ' a , T , S : VecStorage < T > + ?Sized > Drop for Guard < ' a , T , S > {
248+ fn drop ( & mut self ) {
249+ self . 0 . front = 0 ;
250+ self . 0 . back = 0 ;
251+ self . 0 . full = false ;
252+ }
253+ }
254+ let this = Guard ( self ) ;
255+ // SAFETY: Guard will be dropped and lead to a consistent empty state even in the case of a
256+ // panic leading to unwinding during the dropping of an item
257+ unsafe { this. 0 . drop_contents ( ) }
251258 }
252259
253260 /// Drop all items in the `Deque`, leaving the state `back/front/full` unmodified.
@@ -1306,6 +1313,12 @@ impl<T, const NS: usize, const ND: usize> TryFrom<[T; NS]> for Deque<T, ND> {
13061313
13071314#[ cfg( test) ]
13081315mod tests {
1316+ use std:: {
1317+ mem:: ManuallyDrop ,
1318+ panic:: { catch_unwind, AssertUnwindSafe } ,
1319+ sync:: atomic:: { AtomicI32 , Ordering :: Relaxed } ,
1320+ } ;
1321+
13091322 use super :: Deque ;
13101323 use crate :: CapacityError ;
13111324 use static_assertions:: assert_not_impl_any;
@@ -1324,7 +1337,7 @@ mod tests {
13241337 }
13251338
13261339 #[ test]
1327- fn drop ( ) {
1340+ fn test_drop ( ) {
13281341 droppable ! ( ) ;
13291342
13301343 {
@@ -1629,20 +1642,23 @@ mod tests {
16291642
16301643 #[ test]
16311644 fn clear ( ) {
1632- let mut q: Deque < i32 , 4 > = Deque :: new ( ) ;
1645+ droppable ! ( ) ;
1646+ let mut q: Deque < Droppable , 4 > = Deque :: new ( ) ;
16331647 assert_eq ! ( q. len( ) , 0 ) ;
16341648
1635- q. push_back ( 0 ) . unwrap ( ) ;
1636- q. push_back ( 1 ) . unwrap ( ) ;
1637- q. push_back ( 2 ) . unwrap ( ) ;
1638- q. push_back ( 3 ) . unwrap ( ) ;
1649+ q. push_back ( Droppable :: new ( ) ) . unwrap ( ) ;
1650+ q. push_back ( Droppable :: new ( ) ) . unwrap ( ) ;
1651+ q. push_back ( Droppable :: new ( ) ) . unwrap ( ) ;
1652+ q. push_back ( Droppable :: new ( ) ) . unwrap ( ) ;
16391653 assert_eq ! ( q. len( ) , 4 ) ;
16401654
16411655 q. clear ( ) ;
16421656 assert_eq ! ( q. len( ) , 0 ) ;
1657+ assert_eq ! ( Droppable :: count( ) , 0 ) ;
16431658
1644- q. push_back ( 0 ) . unwrap ( ) ;
1659+ q. push_back ( Droppable :: new ( ) ) . unwrap ( ) ;
16451660 assert_eq ! ( q. len( ) , 1 ) ;
1661+ assert_eq ! ( Droppable :: count( ) , 1 ) ;
16461662 }
16471663
16481664 #[ test]
@@ -2275,4 +2291,42 @@ mod tests {
22752291 assert_eq ! ( deq. pop_back_if( |_| true ) , None ) ;
22762292 assert ! ( deq. is_empty( ) ) ;
22772293 }
2294+
2295+ #[ test]
2296+ fn test_use_after_free_clear ( ) {
2297+ // See https://github.com/rust-embedded/heapless/issues/646
2298+
2299+ static COUNT : AtomicI32 = AtomicI32 :: new ( 0 ) ;
2300+
2301+ #[ derive( Debug ) ]
2302+ #[ allow( unused) ]
2303+ struct Dropper ( ) ;
2304+
2305+ impl Dropper {
2306+ fn new ( ) -> Self {
2307+ COUNT . fetch_add ( 1 , Relaxed ) ;
2308+ Self ( )
2309+ }
2310+ fn count ( ) -> i32 {
2311+ COUNT . load ( Relaxed )
2312+ }
2313+ }
2314+ impl Drop for Dropper {
2315+ fn drop ( & mut self ) {
2316+ COUNT . fetch_sub ( 1 , Relaxed ) ;
2317+ panic ! ( "Testing panicking" ) ;
2318+ }
2319+ }
2320+
2321+ let mut deque = Deque :: < Dropper , 5 > :: new ( ) ;
2322+ deque. push_back ( Dropper :: new ( ) ) . unwrap ( ) ;
2323+ // Don't panic on dropping of the deque
2324+ let mut deque = ManuallyDrop :: new ( deque) ;
2325+ let mut unwind_safe_deque = AssertUnwindSafe ( & mut deque) ;
2326+
2327+ catch_unwind ( move || unwind_safe_deque. clear ( ) ) . unwrap_err ( ) ;
2328+ assert_eq ! ( deque. len( ) , 0 ) ;
2329+ deque. clear ( ) ;
2330+ assert_eq ! ( Dropper :: count( ) , 0 ) ;
2331+ }
22782332}
0 commit comments