1
1
use crate :: alloc:: alloc:: { handle_alloc_error, Layout } ;
2
- use crate :: scopeguard:: { guard, ScopeGuard } ;
2
+ use crate :: scopeguard:: guard;
3
3
use crate :: TryReserveError ;
4
4
use core:: iter:: FusedIterator ;
5
5
use core:: marker:: PhantomData ;
@@ -2494,7 +2494,11 @@ impl<T: Clone, A: Allocator + Clone> Clone for RawTable<T, A> {
2494
2494
} else {
2495
2495
unsafe {
2496
2496
// Avoid `Result::ok_or_else` because it bloats LLVM IR.
2497
- let new_table = match Self :: new_uninitialized (
2497
+ //
2498
+ // SAFETY: This is safe as we are taking the size of an already allocated table
2499
+ // and therefore сapacity overflow cannot occur, `self.table.buckets()` is power
2500
+ // of two and all allocator errors will be caught inside `RawTableInner::new_uninitialized`.
2501
+ let mut new_table = match Self :: new_uninitialized (
2498
2502
self . table . alloc . clone ( ) ,
2499
2503
self . table . buckets ( ) ,
2500
2504
Fallibility :: Infallible ,
@@ -2503,29 +2507,29 @@ impl<T: Clone, A: Allocator + Clone> Clone for RawTable<T, A> {
2503
2507
Err ( _) => hint:: unreachable_unchecked ( ) ,
2504
2508
} ;
2505
2509
2506
- // If cloning fails then we need to free the allocation for the
2507
- // new table. However we don't run its drop since its control
2508
- // bytes are not initialized yet.
2509
- let mut guard = guard ( ManuallyDrop :: new ( new_table) , |new_table| {
2510
- new_table. free_buckets ( ) ;
2511
- } ) ;
2512
-
2513
- guard. clone_from_spec ( self ) ;
2514
-
2515
- // Disarm the scope guard and return the newly created table.
2516
- ManuallyDrop :: into_inner ( ScopeGuard :: into_inner ( guard) )
2510
+ // Cloning elements may fail (the clone function may panic). But we don't
2511
+ // need to worry about uninitialized control bits, since:
2512
+ // 1. The number of items (elements) in the table is zero, which means that
2513
+ // the control bits will not be readed by Drop function.
2514
+ // 2. The `clone_from_spec` method will first copy all control bits from
2515
+ // `self` (thus initializing them). But this will not affect the `Drop`
2516
+ // function, since the `clone_from_spec` function sets `items` only after
2517
+ // successfully clonning all elements.
2518
+ new_table. clone_from_spec ( self ) ;
2519
+ new_table
2517
2520
}
2518
2521
}
2519
2522
}
2520
2523
2521
2524
fn clone_from ( & mut self , source : & Self ) {
2522
2525
if source. table . is_empty_singleton ( ) {
2526
+ // Dereference drops old `self` table
2523
2527
* self = Self :: new_in ( self . table . alloc . clone ( ) ) ;
2524
2528
} else {
2525
2529
unsafe {
2526
2530
// Make sure that if any panics occurs, we clear the table and
2527
2531
// leave it in an empty state.
2528
- let mut self_ = guard ( self , |self_| {
2532
+ let mut guard = guard ( & mut * self , |self_| {
2529
2533
self_. clear_no_drop ( ) ;
2530
2534
} ) ;
2531
2535
@@ -2535,18 +2539,32 @@ impl<T: Clone, A: Allocator + Clone> Clone for RawTable<T, A> {
2535
2539
//
2536
2540
// This leak is unavoidable: we can't try dropping more elements
2537
2541
// since this could lead to another panic and abort the process.
2538
- self_. drop_elements ( ) ;
2542
+ //
2543
+ // SAFETY: We clear our table right after dropping the elements,
2544
+ // so there is no double drop, since `items` will be equal to zero.
2545
+ guard. drop_elements ( ) ;
2546
+
2547
+ // Okay, we've successfully dropped all elements, so we'll just set
2548
+ // `items` to zero (so that the `Drop` of `RawTable` doesn't try to
2549
+ // drop all elements twice) and just forget about the guard.
2550
+ guard. table . items = 0 ;
2551
+ mem:: forget ( guard) ;
2539
2552
2540
2553
// If necessary, resize our table to match the source.
2541
- if self_ . buckets ( ) != source. buckets ( ) {
2554
+ if self . buckets ( ) != source. buckets ( ) {
2542
2555
// Skip our drop by using ptr::write.
2543
- if !self_. table . is_empty_singleton ( ) {
2544
- self_. free_buckets ( ) ;
2556
+ if !self . table . is_empty_singleton ( ) {
2557
+ // SAFETY: We have verified that the table is allocated.
2558
+ self . free_buckets ( ) ;
2545
2559
}
2546
- ( & mut * * self_ as * mut Self ) . write (
2560
+ ( self as * mut Self ) . write (
2547
2561
// Avoid `Result::unwrap_or_else` because it bloats LLVM IR.
2562
+ //
2563
+ // SAFETY: This is safe as we are taking the size of an already allocated table
2564
+ // and therefore сapacity overflow cannot occur, `self.table.buckets()` is power
2565
+ // of two and all allocator errors will be caught inside `RawTableInner::new_uninitialized`.
2548
2566
match Self :: new_uninitialized (
2549
- self_ . table . alloc . clone ( ) ,
2567
+ self . table . alloc . clone ( ) ,
2550
2568
source. buckets ( ) ,
2551
2569
Fallibility :: Infallible ,
2552
2570
) {
@@ -2556,10 +2574,11 @@ impl<T: Clone, A: Allocator + Clone> Clone for RawTable<T, A> {
2556
2574
) ;
2557
2575
}
2558
2576
2559
- self_. clone_from_spec ( source) ;
2560
-
2561
- // Disarm the scope guard if cloning was successful.
2562
- ScopeGuard :: into_inner ( self_) ;
2577
+ // Cloning elements may fail (the clone function may panic), but the `ScopeGuard`
2578
+ // inside the `clone_from_impl` function will take care of that, dropping all
2579
+ // cloned elements if necessary. The `Drop` of `RawTable` takes care of the rest
2580
+ // by freeing up the allocated memory.
2581
+ self . clone_from_spec ( source) ;
2563
2582
}
2564
2583
}
2565
2584
}
@@ -3373,4 +3392,67 @@ mod test_map {
3373
3392
assert ! ( table. find( i + 100 , |x| * x == i + 100 ) . is_none( ) ) ;
3374
3393
}
3375
3394
}
3395
+
3396
+ /// CHECKING THAT WE ARE NOT TRYING TO READ THE MEMORY OF
3397
+ /// AN UNINITIALIZED TABLE DURING THE DROP
3398
+ #[ test]
3399
+ fn test_drop_uninitialized ( ) {
3400
+ use :: alloc:: vec:: Vec ;
3401
+
3402
+ let table = unsafe {
3403
+ // SAFETY: The `buckets` is power of two and we're not
3404
+ // trying to actually use the returned RawTable.
3405
+ RawTable :: < ( u64 , Vec < i32 > ) > :: new_uninitialized ( Global , 8 , Fallibility :: Infallible )
3406
+ . unwrap ( )
3407
+ } ;
3408
+ drop ( table) ;
3409
+ }
3410
+
3411
+ /// CHECKING THAT WE DON'T TRY TO DROP DATA IF THE `ITEMS`
3412
+ /// ARE ZERO, EVEN IF WE HAVE `FULL` CONTROL BYTES.
3413
+ #[ test]
3414
+ fn test_drop_zero_items ( ) {
3415
+ use :: alloc:: vec:: Vec ;
3416
+ unsafe {
3417
+ // SAFETY: The `buckets` is power of two and we're not
3418
+ // trying to actually use the returned RawTable.
3419
+ let table =
3420
+ RawTable :: < ( u64 , Vec < i32 > ) > :: new_uninitialized ( Global , 8 , Fallibility :: Infallible )
3421
+ . unwrap ( ) ;
3422
+
3423
+ // WE SIMULATE, AS IT WERE, A FULL TABLE.
3424
+
3425
+ // SAFETY: We checked that the table is allocated and therefore the table already has
3426
+ // `self.bucket_mask + 1 + Group::WIDTH` number of control bytes (see TableLayout::calculate_layout_for)
3427
+ // so writing `table.table.num_ctrl_bytes() == bucket_mask + 1 + Group::WIDTH` bytes is safe.
3428
+ table
3429
+ . table
3430
+ . ctrl ( 0 )
3431
+ . write_bytes ( EMPTY , table. table . num_ctrl_bytes ( ) ) ;
3432
+
3433
+ // SAFETY: table.capacity() is guaranteed to be smaller than table.buckets()
3434
+ table. table . ctrl ( 0 ) . write_bytes ( 0 , table. capacity ( ) ) ;
3435
+
3436
+ // Fix up the trailing control bytes. See the comments in set_ctrl
3437
+ // for the handling of tables smaller than the group width.
3438
+ if table. buckets ( ) < Group :: WIDTH {
3439
+ // SAFETY: We have `self.bucket_mask + 1 + Group::WIDTH` number of control bytes,
3440
+ // so copying `self.buckets() == self.bucket_mask + 1` bytes with offset equal to
3441
+ // `Group::WIDTH` is safe
3442
+ table
3443
+ . table
3444
+ . ctrl ( 0 )
3445
+ . copy_to ( table. table . ctrl ( Group :: WIDTH ) , table. table . buckets ( ) ) ;
3446
+ } else {
3447
+ // SAFETY: We have `self.bucket_mask + 1 + Group::WIDTH` number of
3448
+ // control bytes,so copying `Group::WIDTH` bytes with offset equal
3449
+ // to `self.buckets() == self.bucket_mask + 1` is safe
3450
+ table
3451
+ . table
3452
+ . ctrl ( 0 )
3453
+ . copy_to ( table. table . ctrl ( table. table . buckets ( ) ) , Group :: WIDTH ) ;
3454
+ }
3455
+ drop ( table) ;
3456
+ }
3457
+ }
3376
3458
}
0 commit comments