Skip to content

Commit c6bc462

Browse files
committed
Auto merge of rust-lang#81073 - ssomers:btree_owned_root_vs_dying, r=Mark-Simulacrum
BTreeMap: prevent tree from ever being owned by non-root node This introduces a new marker type, `Dying`, which is used to note trees which are in the process of deallocation. On such trees, some fields may be in an inconsistent state as we are deallocating the tree. Unfortunately, there's not a great way to express conditional unsafety, so the methods for traversal can cause UB if not invoked correctly, but not marked as such. This is not a regression from the previous state, but rather isolates the destructive methods to solely being called on the dying state.
2 parents 74500b9 + 417eefe commit c6bc462

File tree

5 files changed

+73
-32
lines changed

5 files changed

+73
-32
lines changed

Diff for: library/alloc/src/collections/btree/map.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -300,8 +300,8 @@ pub struct IterMut<'a, K: 'a, V: 'a> {
300300
/// [`into_iter`]: IntoIterator::into_iter
301301
#[stable(feature = "rust1", since = "1.0.0")]
302302
pub struct IntoIter<K, V> {
303-
front: Option<Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge>>,
304-
back: Option<Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge>>,
303+
front: Option<Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge>>,
304+
back: Option<Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge>>,
305305
length: usize,
306306
}
307307

@@ -1364,7 +1364,7 @@ impl<K, V> IntoIterator for BTreeMap<K, V> {
13641364
fn into_iter(self) -> IntoIter<K, V> {
13651365
let mut me = ManuallyDrop::new(self);
13661366
if let Some(root) = me.root.take() {
1367-
let (f, b) = root.full_range();
1367+
let (f, b) = root.into_dying().full_range();
13681368

13691369
IntoIter { front: Some(f), back: Some(b), length: me.length }
13701370
} else {

Diff for: library/alloc/src/collections/btree/navigate.rs

+18-12
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use super::unwrap_unchecked;
1212
///
1313
/// The result is meaningful only if the tree is ordered by key, like the tree
1414
/// in a `BTreeMap` is.
15-
fn range_search<BorrowType, K, V, Q, R>(
15+
fn range_search<BorrowType: marker::BorrowType, K, V, Q, R>(
1616
root1: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
1717
root2: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
1818
range: R,
@@ -105,7 +105,7 @@ where
105105
}
106106

107107
/// Equivalent to `range_search(k, v, ..)` but without the `Ord` bound.
108-
fn full_range<BorrowType, K, V>(
108+
fn full_range<BorrowType: marker::BorrowType, K, V>(
109109
root1: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
110110
root2: NodeRef<BorrowType, K, V, marker::LeafOrInternal>,
111111
) -> (
@@ -202,15 +202,15 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::ValMut<'a>, K, V, marker::LeafOrInternal>
202202
}
203203
}
204204

205-
impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
205+
impl<K, V> NodeRef<marker::Dying, K, V, marker::LeafOrInternal> {
206206
/// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree.
207207
/// The results are non-unique references allowing massively destructive mutation, so must be
208208
/// used with the utmost care.
209209
pub fn full_range(
210210
self,
211211
) -> (
212-
Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge>,
213-
Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge>,
212+
Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge>,
213+
Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge>,
214214
) {
215215
// We duplicate the root NodeRef here -- we will never access it in a way
216216
// that overlaps references obtained from the root.
@@ -219,7 +219,9 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
219219
}
220220
}
221221

222-
impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> {
222+
impl<BorrowType: marker::BorrowType, K, V>
223+
Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge>
224+
{
223225
/// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV
224226
/// on the right side, which is either in the same leaf node or in an ancestor node.
225227
/// If the leaf edge is the last one in the tree, returns [`Result::Err`] with the root node.
@@ -263,7 +265,9 @@ impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::E
263265
}
264266
}
265267

266-
impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Internal>, marker::Edge> {
268+
impl<BorrowType: marker::BorrowType, K, V>
269+
Handle<NodeRef<BorrowType, K, V, marker::Internal>, marker::Edge>
270+
{
267271
/// Given an internal edge handle, returns [`Result::Ok`] with a handle to the neighboring KV
268272
/// on the right side, which is either in the same internal node or in an ancestor node.
269273
/// If the internal edge is the last one in the tree, returns [`Result::Err`] with the root node.
@@ -297,8 +301,8 @@ macro_rules! def_next_kv_uncheched_dealloc {
297301
/// - The node carrying the next KV returned must not have been deallocated by a
298302
/// previous call on any handle obtained for this tree.
299303
unsafe fn $name <K, V>(
300-
leaf_edge: Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge>,
301-
) -> Handle<NodeRef<marker::Owned, K, V, marker::LeafOrInternal>, marker::KV> {
304+
leaf_edge: Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge>,
305+
) -> Handle<NodeRef<marker::Dying, K, V, marker::LeafOrInternal>, marker::KV> {
302306
let mut edge = leaf_edge.forget_node_type();
303307
loop {
304308
edge = match edge.$adjacent_kv() {
@@ -378,7 +382,7 @@ impl<'a, K, V> Handle<NodeRef<marker::ValMut<'a>, K, V, marker::Leaf>, marker::E
378382
}
379383
}
380384

381-
impl<K, V> Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge> {
385+
impl<K, V> Handle<NodeRef<marker::Dying, K, V, marker::Leaf>, marker::Edge> {
382386
/// Moves the leaf edge handle to the next leaf edge and returns the key and value
383387
/// in between, deallocating any node left behind while leaving the corresponding
384388
/// edge in its parent node dangling.
@@ -422,7 +426,7 @@ impl<K, V> Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge> {
422426
}
423427
}
424428

425-
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
429+
impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
426430
/// Returns the leftmost leaf edge in or underneath a node - in other words, the edge
427431
/// you need first when navigating forward (or last when navigating backward).
428432
#[inline]
@@ -503,7 +507,9 @@ impl<'a, K: 'a, V: 'a> NodeRef<marker::Immut<'a>, K, V, marker::LeafOrInternal>
503507
}
504508
}
505509

506-
impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, marker::KV> {
510+
impl<BorrowType: marker::BorrowType, K, V>
511+
Handle<NodeRef<BorrowType, K, V, marker::LeafOrInternal>, marker::KV>
512+
{
507513
/// Returns the leaf edge closest to a KV for forward navigation.
508514
pub fn next_leaf_edge(self) -> Handle<NodeRef<BorrowType, K, V, marker::Leaf>, marker::Edge> {
509515
match self.force() {

Diff for: library/alloc/src/collections/btree/node.rs

+49-14
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ struct InternalNode<K, V> {
9393
data: LeafNode<K, V>,
9494

9595
/// The pointers to the children of this node. `len + 1` of these are considered
96-
/// initialized and valid. Although during the process of `into_iter` or `drop`,
97-
/// some pointers are dangling while others still need to be traversed.
96+
/// initialized and valid, except that near the end, while the tree is held
97+
/// through borrow type `Dying`, some of these pointers are dangling.
9898
edges: [MaybeUninit<BoxedNode<K, V>>; 2 * B],
9999
}
100100

@@ -119,7 +119,7 @@ impl<K, V> InternalNode<K, V> {
119119
/// is not a separate type and has no destructor.
120120
type BoxedNode<K, V> = NonNull<LeafNode<K, V>>;
121121

122-
/// An owned tree.
122+
/// The root node of an owned tree.
123123
///
124124
/// Note that this does not have a destructor, and must be cleaned up manually.
125125
pub type Root<K, V> = NodeRef<marker::Owned, K, V, marker::LeafOrInternal>;
@@ -157,18 +157,23 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::Internal> {
157157
}
158158

159159
impl<K, V, Type> NodeRef<marker::Owned, K, V, Type> {
160-
/// Mutably borrows the owned node. Unlike `reborrow_mut`, this is safe,
161-
/// because the return value cannot be used to destroy the node itself,
162-
/// and there cannot be other references to the tree (except during the
163-
/// process of `into_iter` or `drop`, but that is horrific already).
160+
/// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe
161+
/// because the return value cannot be used to destroy the root, and there
162+
/// cannot be other references to the tree.
164163
pub fn borrow_mut(&mut self) -> NodeRef<marker::Mut<'_>, K, V, Type> {
165164
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
166165
}
167166

168-
/// Slightly mutably borrows the owned node.
167+
/// Slightly mutably borrows the owned root node.
169168
pub fn borrow_valmut(&mut self) -> NodeRef<marker::ValMut<'_>, K, V, Type> {
170169
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
171170
}
171+
172+
/// Irreversibly transistions to a reference that offers traversal,
173+
/// destructive methods and little else.
174+
pub fn into_dying(self) -> NodeRef<marker::Dying, K, V, Type> {
175+
NodeRef { height: self.height, node: self.node, _marker: PhantomData }
176+
}
172177
}
173178

174179
impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
@@ -196,8 +201,13 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
196201

197202
let top = self.node;
198203

199-
let internal_node = NodeRef { height: self.height, node: top, _marker: PhantomData };
200-
*self = internal_node.first_edge().descend();
204+
// SAFETY: we asserted to be internal.
205+
let internal_self = unsafe { self.borrow_mut().cast_to_internal_unchecked() };
206+
// SAFETY: we borrowed `self` exclusively and its borrow type is exclusive.
207+
let internal_node = unsafe { &mut *NodeRef::as_internal_ptr(&internal_self) };
208+
// SAFETY: the first edge is always initialized.
209+
self.node = unsafe { internal_node.edges[0].assume_init_read() };
210+
self.height -= 1;
201211
self.clear_parent_link();
202212

203213
unsafe {
@@ -224,6 +234,9 @@ impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
224234
/// although insert methods allow a mutable pointer to a value to coexist.
225235
/// - When this is `Owned`, the `NodeRef` acts roughly like `Box<Node>`,
226236
/// but does not have a destructor, and must be cleaned up manually.
237+
/// - When this is `Dying`, the `NodeRef` still acts roughly like `Box<Node>`,
238+
/// but has methods to destroy the tree bit by bit, and ordinary methods,
239+
/// while not marked as unsafe to call, can invoke UB if called incorrectly.
227240
/// Since any `NodeRef` allows navigating through the tree, `BorrowType`
228241
/// effectively applies to the entire tree, not just to the node itself.
229242
/// - `K` and `V`: These are the types of keys and values stored in the nodes.
@@ -280,6 +293,7 @@ unsafe impl<'a, K: Sync + 'a, V: Sync + 'a, Type> Send for NodeRef<marker::Immut
280293
unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef<marker::Mut<'a>, K, V, Type> {}
281294
unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send for NodeRef<marker::ValMut<'a>, K, V, Type> {}
282295
unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Owned, K, V, Type> {}
296+
unsafe impl<K: Send, V: Send, Type> Send for NodeRef<marker::Dying, K, V, Type> {}
283297

284298
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::Internal> {
285299
/// Unpack a node reference that was packed as `NodeRef::parent`.
@@ -343,7 +357,7 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
343357
}
344358
}
345359

346-
impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
360+
impl<BorrowType: marker::BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
347361
/// Finds the parent of the current node. Returns `Ok(handle)` if the current
348362
/// node actually has a parent, where `handle` points to the edge of the parent
349363
/// that points to the current node. Returns `Err(self)` if the current node has
@@ -356,6 +370,7 @@ impl<BorrowType, K, V, Type> NodeRef<BorrowType, K, V, Type> {
356370
pub fn ascend(
357371
self,
358372
) -> Result<Handle<NodeRef<BorrowType, K, V, marker::Internal>, marker::Edge>, Self> {
373+
assert!(BorrowType::PERMITS_TRAVERSAL);
359374
// We need to use raw pointers to nodes because, if BorrowType is marker::ValMut,
360375
// there might be outstanding mutable references to values that we must not invalidate.
361376
let leaf_ptr: *const _ = Self::as_leaf_ptr(&self);
@@ -410,13 +425,13 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef<marker::Immut<'a>, K, V, Type> {
410425
}
411426
}
412427

413-
impl<K, V> NodeRef<marker::Owned, K, V, marker::LeafOrInternal> {
428+
impl<K, V> NodeRef<marker::Dying, K, V, marker::LeafOrInternal> {
414429
/// Similar to `ascend`, gets a reference to a node's parent node, but also
415430
/// deallocates the current node in the process. This is unsafe because the
416431
/// current node will still be accessible despite being deallocated.
417432
pub unsafe fn deallocate_and_ascend(
418433
self,
419-
) -> Option<Handle<NodeRef<marker::Owned, K, V, marker::Internal>, marker::Edge>> {
434+
) -> Option<Handle<NodeRef<marker::Dying, K, V, marker::Internal>, marker::Edge>> {
420435
let height = self.height;
421436
let node = self.node;
422437
let ret = self.ascend().ok();
@@ -951,14 +966,17 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, mark
951966
}
952967
}
953968

954-
impl<BorrowType, K, V> Handle<NodeRef<BorrowType, K, V, marker::Internal>, marker::Edge> {
969+
impl<BorrowType: marker::BorrowType, K, V>
970+
Handle<NodeRef<BorrowType, K, V, marker::Internal>, marker::Edge>
971+
{
955972
/// Finds the node pointed to by this edge.
956973
///
957974
/// The method name assumes you picture trees with the root node on top.
958975
///
959976
/// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should
960977
/// both, upon success, do nothing.
961978
pub fn descend(self) -> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
979+
assert!(BorrowType::PERMITS_TRAVERSAL);
962980
// We need to use raw pointers to nodes because, if BorrowType is
963981
// marker::ValMut, there might be outstanding mutable references to
964982
// values that we must not invalidate. There's no worry accessing the
@@ -1596,10 +1614,27 @@ pub mod marker {
15961614
pub enum LeafOrInternal {}
15971615

15981616
pub enum Owned {}
1617+
pub enum Dying {}
15991618
pub struct Immut<'a>(PhantomData<&'a ()>);
16001619
pub struct Mut<'a>(PhantomData<&'a mut ()>);
16011620
pub struct ValMut<'a>(PhantomData<&'a mut ()>);
16021621

1622+
pub trait BorrowType {
1623+
// Whether node references of this borrow type allow traversing
1624+
// to other nodes in the tree.
1625+
const PERMITS_TRAVERSAL: bool = true;
1626+
}
1627+
impl BorrowType for Owned {
1628+
// Traversal isn't needede, it happens using the result of `borrow_mut`.
1629+
// By disabling traversal, and only creating new references to roots,
1630+
// we know that every reference of the `Owned` type is to a root node.
1631+
const PERMITS_TRAVERSAL: bool = false;
1632+
}
1633+
impl BorrowType for Dying {}
1634+
impl<'a> BorrowType for Immut<'a> {}
1635+
impl<'a> BorrowType for Mut<'a> {}
1636+
impl<'a> BorrowType for ValMut<'a> {}
1637+
16031638
pub enum KV {}
16041639
pub enum Edge {}
16051640
}

Diff for: library/alloc/src/collections/btree/node/tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ fn test_partial_cmp_eq() {
9595
assert_eq!(top_edge_1.partial_cmp(&top_edge_2), None);
9696

9797
root1.pop_internal_level();
98-
unsafe { root1.deallocate_and_ascend() };
99-
unsafe { root2.deallocate_and_ascend() };
98+
unsafe { root1.into_dying().deallocate_and_ascend() };
99+
unsafe { root2.into_dying().deallocate_and_ascend() };
100100
}
101101

102102
#[test]

Diff for: library/alloc/src/collections/btree/search.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub enum IndexResult {
1515
Edge(usize),
1616
}
1717

18-
impl<BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
18+
impl<BorrowType: marker::BorrowType, K, V> NodeRef<BorrowType, K, V, marker::LeafOrInternal> {
1919
/// Looks up a given key in a (sub)tree headed by the node, recursively.
2020
/// Returns a `Found` with the handle of the matching KV, if any. Otherwise,
2121
/// returns a `GoDown` with the handle of the leaf edge where the key belongs.

0 commit comments

Comments
 (0)