From 5f991ecc5cfd3066d92ec144da74844914055a20 Mon Sep 17 00:00:00 2001 From: Francesca Plebani Date: Mon, 5 Aug 2019 15:29:02 -0700 Subject: [PATCH] Fix `BTreeMap` UB caused by using pointer identity on a static In #50352, `EMPTY_ROOT_NODE` was introduced to avoid allocating when creating an empty `BTreeMap`. `EMPTY_ROOT_NODE` was distinguished from regular nodes using pointer identity. However, `EMPTY_ROOT_NODE` can have multiple addresses if multiple copies of `liballoc` are used together, i.e. when interoperating with a dynamic library. In that situation, all sorts of scary things will happen, since it's possible for `EMPTY_ROOT_NODE` to be treated as a regular node. To fix that, this PR adds a flag to every node, and simply uses that flag to distinguish `EMPTY_ROOT_NODE` from regular nodes. This only increases the node size if `keys` previously used every byte of padding after `len`, or if every byte of remaining padding was previously used to also fit `vals`. In those cases, every node will grow by 1 word. This playground link can be used to easily test the space impact of this PR: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=87850055d6049be048f9e73f6a5055d6 --- src/liballoc/collections/btree/node.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/liballoc/collections/btree/node.rs b/src/liballoc/collections/btree/node.rs index e067096f0c780..8188082af02df 100644 --- a/src/liballoc/collections/btree/node.rs +++ b/src/liballoc/collections/btree/node.rs @@ -73,6 +73,9 @@ struct NodeHeader { /// `parent_idx` into the same 32-bit word, reducing space overhead. len: u16, + /// Whether this node is an `EMPTY_ROOT_NODE`. + shared_root: bool, + /// See `into_key_slice`. keys_start: [K2; 0], } @@ -93,6 +96,9 @@ struct LeafNode { /// `parent_idx` into the same 32-bit word, reducing space overhead. len: u16, + /// Whether this node is an `EMPTY_ROOT_NODE`. + shared_root: bool, + /// The arrays storing the actual data of the node. Only the first `len` elements of each /// array are initialized and valid. keys: [MaybeUninit; CAPACITY], @@ -110,14 +116,18 @@ impl LeafNode { vals: uninit_array![_; CAPACITY], parent: ptr::null(), parent_idx: MaybeUninit::uninit(), - len: 0 + len: 0, + shared_root: false, } } } impl NodeHeader { fn is_shared_root(&self) -> bool { - ptr::eq(self, &EMPTY_ROOT_NODE as *const _ as *const _) + // This was previously implemented using pointer identity, but `EMPTY_ROOT_NODE` + // can have multiple addresses if multiple copies of `liballoc` are used together, + // i.e. when interoperating with a dynamic library. + self.shared_root } } @@ -131,6 +141,7 @@ static EMPTY_ROOT_NODE: NodeHeader<(), ()> = NodeHeader { parent: ptr::null(), parent_idx: MaybeUninit::uninit(), len: 0, + shared_root: true, keys_start: [], };