Skip to content

Commit 42b9a04

Browse files
authored
Rollup merge of rust-lang#58431 - RalfJung:btree, r=Mark-Simulacrum
fix overlapping references in BTree This fixes two kinds of overlapping references in BTree (both found by running the BTree test suite in Miri). In `into_slices_mut`, we did `k.into_key_slice_mut()` followed by `self.into_val_slice_mut()` (where `k` is a copy of `self`). Calling `into_val_slice_mut` calls `self.len()`, which creates a shared reference to `NodeHeader`, which unfortunately (due to padding) overlaps with the mutable reference returned by `into_key_slice_mut`. Hence the key slice got (partially) invalidated. The fix is to avoid creating an `&NodeHeader` after the first slice got created. In the iterators, we used to first create the references that will be returned, and then perform the walk on the tree. Walking the tree creates references (such as `&mut InternalNode`) that overlap with all of the keys and values stored in a pointer; in particular, they overlap with the references the iterator will later return. This is fixed by reordering the operations of walking the tree and obtaining the inner references. The test suite still passes (and it passes in Miri now!), but there is a lot of code here that I do not understand...
2 parents 894141b + f0bef49 commit 42b9a04

File tree

2 files changed

+42
-15
lines changed

2 files changed

+42
-15
lines changed

src/liballoc/collections/btree/map.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -1634,9 +1634,11 @@ impl<'a, K, V> RangeMut<'a, K, V> {
16341634

16351635
let mut cur_handle = match handle.right_kv() {
16361636
Ok(kv) => {
1637-
let (k, v) = ptr::read(&kv).into_kv_mut();
1638-
self.front = kv.right_edge();
1639-
return (k, v);
1637+
self.front = ptr::read(&kv).right_edge();
1638+
// Doing the descend invalidates the references returned by `into_kv_mut`,
1639+
// so we have to do this last.
1640+
let (k, v) = kv.into_kv_mut();
1641+
return (k, v); // coerce k from `&mut K` to `&K`
16401642
}
16411643
Err(last_edge) => {
16421644
let next_level = last_edge.into_node().ascend().ok();
@@ -1647,9 +1649,11 @@ impl<'a, K, V> RangeMut<'a, K, V> {
16471649
loop {
16481650
match cur_handle.right_kv() {
16491651
Ok(kv) => {
1650-
let (k, v) = ptr::read(&kv).into_kv_mut();
1651-
self.front = first_leaf_edge(kv.right_edge().descend());
1652-
return (k, v);
1652+
self.front = first_leaf_edge(ptr::read(&kv).right_edge().descend());
1653+
// Doing the descend invalidates the references returned by `into_kv_mut`,
1654+
// so we have to do this last.
1655+
let (k, v) = kv.into_kv_mut();
1656+
return (k, v); // coerce k from `&mut K` to `&K`
16531657
}
16541658
Err(last_edge) => {
16551659
let next_level = last_edge.into_node().ascend().ok();
@@ -1680,9 +1684,11 @@ impl<'a, K, V> RangeMut<'a, K, V> {
16801684

16811685
let mut cur_handle = match handle.left_kv() {
16821686
Ok(kv) => {
1683-
let (k, v) = ptr::read(&kv).into_kv_mut();
1684-
self.back = kv.left_edge();
1685-
return (k, v);
1687+
self.back = ptr::read(&kv).left_edge();
1688+
// Doing the descend invalidates the references returned by `into_kv_mut`,
1689+
// so we have to do this last.
1690+
let (k, v) = kv.into_kv_mut();
1691+
return (k, v); // coerce k from `&mut K` to `&K`
16861692
}
16871693
Err(last_edge) => {
16881694
let next_level = last_edge.into_node().ascend().ok();
@@ -1693,9 +1699,11 @@ impl<'a, K, V> RangeMut<'a, K, V> {
16931699
loop {
16941700
match cur_handle.left_kv() {
16951701
Ok(kv) => {
1696-
let (k, v) = ptr::read(&kv).into_kv_mut();
1697-
self.back = last_leaf_edge(kv.left_edge().descend());
1698-
return (k, v);
1702+
self.back = last_leaf_edge(ptr::read(&kv).left_edge().descend());
1703+
// Doing the descend invalidates the references returned by `into_kv_mut`,
1704+
// so we have to do this last.
1705+
let (k, v) = kv.into_kv_mut();
1706+
return (k, v); // coerce k from `&mut K` to `&K`
16991707
}
17001708
Err(last_edge) => {
17011709
let next_level = last_edge.into_node().ascend().ok();

src/liballoc/collections/btree/node.rs

+22-3
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,8 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
645645
}
646646

647647
fn into_key_slice_mut(mut self) -> &'a mut [K] {
648+
// Same as for `into_key_slice` above, we try to avoid a run-time check
649+
// (the alignment comparison will usually be performed at compile-time).
648650
if mem::align_of::<K>() > mem::align_of::<LeafNode<(), ()>>() && self.is_shared_root() {
649651
&mut []
650652
} else {
@@ -667,9 +669,26 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Mut<'a>, K, V, Type> {
667669
}
668670
}
669671

670-
fn into_slices_mut(self) -> (&'a mut [K], &'a mut [V]) {
671-
let k = unsafe { ptr::read(&self) };
672-
(k.into_key_slice_mut(), self.into_val_slice_mut())
672+
fn into_slices_mut(mut self) -> (&'a mut [K], &'a mut [V]) {
673+
debug_assert!(!self.is_shared_root());
674+
// We cannot use the getters here, because calling the second one
675+
// invalidates the reference returned by the first.
676+
// More precisely, it is the call to `len` that is the culprit,
677+
// because that creates a shared reference to the header, which *can*
678+
// overlap with the keys (and even the values, for ZST keys).
679+
unsafe {
680+
let len = self.len();
681+
let leaf = self.as_leaf_mut();
682+
let keys = slice::from_raw_parts_mut(
683+
MaybeUninit::first_ptr_mut(&mut (*leaf).keys),
684+
len
685+
);
686+
let vals = slice::from_raw_parts_mut(
687+
MaybeUninit::first_ptr_mut(&mut (*leaf).vals),
688+
len
689+
);
690+
(keys, vals)
691+
}
673692
}
674693
}
675694

0 commit comments

Comments
 (0)