sandbox/
dict.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::{Capability, CapabilityBound};
6use derivative::Derivative;
7use fidl_fuchsia_component_sandbox as fsandbox;
8use fuchsia_sync::{Mutex, MutexGuard};
9use std::borrow::Borrow;
10use std::collections::BTreeMap;
11use std::collections::btree_map::Entry;
12use std::sync::Arc;
13
14#[cfg(target_os = "fuchsia")]
15use fuchsia_async as fasync;
16
17pub type Key = cm_types::Name;
18pub type BorrowedKey = cm_types::BorrowedName;
19
20/// A capability that represents a dictionary of capabilities.
21#[derive(Debug, Clone)]
22pub struct Dict {
23    inner: Arc<Mutex<DictInner>>,
24}
25
26#[derive(Derivative)]
27#[derivative(Debug)]
28pub(crate) struct DictInner {
29    /// The contents of the [Dict].
30    pub(crate) entries: HybridMap,
31
32    /// When an external request tries to access a non-existent entry, this closure will be invoked
33    /// with the name of the entry.
34    #[derivative(Debug = "ignore")]
35    // Currently this is only used on target, but it's compatible with host.
36    #[allow(dead_code)]
37    pub(crate) not_found: Option<Box<dyn Fn(&str) -> () + 'static + Send + Sync>>,
38
39    /// Tasks that serve the Dictionary protocol. This is an `Option` because dictionaries are not
40    /// necessarily used in async contexts but a TaskGroup will fail construction if there is no
41    /// async executor.
42    #[cfg(target_os = "fuchsia")]
43    #[derivative(Debug = "ignore")]
44    task_group: Option<fasync::TaskGroup>,
45
46    /// Functions that will be invoked whenever the contents of this dictionary changes.
47    #[derivative(Debug = "ignore")]
48    update_notifiers: UpdateNotifiers,
49}
50
51impl CapabilityBound for Dict {
52    fn debug_typename() -> &'static str {
53        "Dictionary"
54    }
55}
56
57impl Drop for DictInner {
58    fn drop(&mut self) {
59        // When this dictionary doesn't exist anymore, then neither do its entries (or at least
60        // these references of them). Notify anyone listening that all of the entries have been
61        // removed.
62        let entries = std::mem::replace(&mut self.entries, HybridMap::default());
63        for (key, _) in entries.into_iter() {
64            self.update_notifiers.update(EntryUpdate::Remove(&key))
65        }
66    }
67}
68
69/// Represents a change to a dictionary, where an entry is either added or removed.
70#[derive(Debug, Copy, Clone)]
71pub enum EntryUpdate<'a> {
72    Add(&'a BorrowedKey, &'a Capability),
73    Remove(&'a BorrowedKey),
74    Idle,
75}
76
77/// Represents whether an update notifier should be retained and thus continue to be called for
78/// future updates, or if it should be dropped and no longer be called.
79pub enum UpdateNotifierRetention {
80    Retain,
81    Drop_,
82}
83
84/// A function that will be called when the contents of a dictionary changes. Note that this
85/// function will be called while the internal dictionary structure is locked, so it must not
86/// interact with the dictionary on which it is registered. It shouldn't even hold a strong
87/// reference to the dictionary it's registered on, as that'll create a cycle and make LSAN sad.
88pub type UpdateNotifierFn =
89    Box<dyn for<'a> FnMut(EntryUpdate<'a>) -> UpdateNotifierRetention + Sync + Send>;
90
91impl Default for Dict {
92    fn default() -> Self {
93        Self {
94            inner: Arc::new(Mutex::new(DictInner {
95                entries: HybridMap::default(),
96                not_found: None,
97                #[cfg(target_os = "fuchsia")]
98                task_group: None,
99                update_notifiers: UpdateNotifiers::default(),
100            })),
101        }
102    }
103}
104
105impl Dict {
106    /// Creates an empty dictionary.
107    pub fn new() -> Self {
108        Self::default()
109    }
110
111    /// Creates an empty dictionary. When an external request tries to access a non-existent entry,
112    /// the name of the entry will be sent using `not_found`.
113    pub fn new_with_not_found(not_found: impl Fn(&str) -> () + 'static + Send + Sync) -> Self {
114        Self {
115            inner: Arc::new(Mutex::new(DictInner {
116                entries: HybridMap::default(),
117                not_found: Some(Box::new(not_found)),
118                #[cfg(target_os = "fuchsia")]
119                task_group: None,
120                update_notifiers: UpdateNotifiers::default(),
121            })),
122        }
123    }
124
125    pub(crate) fn lock(&self) -> MutexGuard<'_, DictInner> {
126        self.inner.lock()
127    }
128
129    /// Registers a new update notifier function with this dictionary. The function will be called
130    /// whenever an entry is added to or removed from this dictionary. The function will be
131    /// immediately called with an `EntryUpdate::Add` value for any entries already in this
132    /// dictionary.
133    ///
134    /// Note that this function will be called while the internal dictionary structure is locked,
135    /// so it must not interact with the dictionary on which it is registered. It shouldn't even
136    /// hold a strong reference to the dictionary it's registered on, as that'll create a cycle and
137    /// make LSAN sad.
138    pub fn register_update_notifier(&self, mut notifier_fn: UpdateNotifierFn) {
139        let mut guard = self.lock();
140        for (key, value) in guard.entries.iter() {
141            if let UpdateNotifierRetention::Drop_ = (notifier_fn)(EntryUpdate::Add(key, value)) {
142                // The notifier has signaled that it doesn't want to process any more updates.
143                return;
144            }
145        }
146        if let UpdateNotifierRetention::Retain = (notifier_fn)(EntryUpdate::Idle) {
147            guard.update_notifiers.0.push(notifier_fn);
148        }
149    }
150
151    /// Inserts an entry, mapping `key` to `capability`. If an entry already exists at `key`, a
152    /// `fsandbox::DictionaryError::AlreadyExists` will be returned.
153    pub fn insert(
154        &self,
155        key: Key,
156        capability: Capability,
157    ) -> Result<(), fsandbox::CapabilityStoreError> {
158        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
159        entries.insert(key, capability, update_notifiers)
160    }
161
162    pub fn append(&self, other: &Dict) -> Result<(), ()> {
163        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
164        let other = other.lock();
165        entries.append(&other.entries, update_notifiers)
166    }
167
168    /// Returns a clone of the capability associated with `key`. If there is no entry for `key`,
169    /// `None` is returned.
170    ///
171    /// If the value could not be cloned, returns an error.
172    pub fn get<Q: ?Sized>(&self, key: &Q) -> Result<Option<Capability>, ()>
173    where
174        Key: Borrow<Q> + Ord,
175        Q: Ord,
176    {
177        self.lock().entries.get(key)
178    }
179
180    /// Returns a clone of the capability associated with `key`, or populates it with `default` if
181    /// it is not present.
182    ///
183    /// If the value could not be cloned, returns an error.
184    pub fn get_or_insert(
185        &self,
186        key: &Key,
187        default: impl FnOnce() -> Capability,
188    ) -> Result<Capability, ()> {
189        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
190        match entries.get(key)? {
191            Some(v) => Ok(v),
192            None => {
193                let v = (default)();
194                entries.insert(key.clone(), v.try_clone()?, update_notifiers).unwrap();
195                Ok(v)
196            }
197        }
198    }
199
200    /// Removes `key` from the entries, returning the capability at `key` if the key was already in
201    /// the entries.
202    pub fn remove(&self, key: &BorrowedKey) -> Option<Capability> {
203        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
204        entries.remove(key, update_notifiers)
205    }
206
207    /// Returns an iterator over a clone of the entries, sorted by key.
208    ///
209    /// If a capability is not cloneable, an error returned for the value.
210    pub fn enumerate(&self) -> impl Iterator<Item = (Key, Result<Capability, ()>)> + use<> {
211        self.lock()
212            .entries
213            .iter()
214            .map(|(k, v)| (k.clone(), v.try_clone()))
215            .collect::<Vec<_>>()
216            .into_iter()
217    }
218
219    /// Returns an iterator over the keys, in sorted order.
220    pub fn keys(&self) -> impl Iterator<Item = Key> + use<> {
221        self.lock().entries.iter().map(|(k, _)| k.clone()).collect::<Vec<_>>().into_iter()
222    }
223
224    /// Removes all entries from the Dict and returns them as an iterator.
225    pub fn drain(&self) -> impl Iterator<Item = (Key, Capability)> + use<> {
226        let DictInner { entries, update_notifiers, .. } = &mut *self.lock();
227        let entries = std::mem::replace(entries, HybridMap::default());
228        for (key, _) in entries.iter() {
229            update_notifiers.update(EntryUpdate::Remove(&key))
230        }
231        entries.into_iter()
232    }
233
234    /// Creates a new Dict with entries cloned from this Dict.
235    ///
236    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
237    /// underlying data.
238    ///
239    /// If any value in the dictionary could not be cloned, returns an error.
240    pub fn shallow_copy(&self) -> Result<Self, ()> {
241        let copy = Self::new();
242        {
243            let DictInner { entries, .. } = &*self.lock();
244            let mut copy = copy.lock();
245            copy.entries = entries.shallow_copy()?;
246        }
247        Ok(copy)
248    }
249
250    /// Sends the name of an entry to the not found handler.
251    // Currently this is only used on target, but it's compatible with host.
252    #[allow(dead_code)]
253    pub(crate) fn not_found(&self, entry: &str) {
254        if let Some(not_found_handler) = &self.lock().not_found {
255            not_found_handler(entry);
256        }
257    }
258}
259
260impl DictInner {
261    #[cfg(target_os = "fuchsia")]
262    pub(crate) fn tasks(&mut self) -> &mut fasync::TaskGroup {
263        self.task_group.get_or_insert_with(|| fasync::TaskGroup::new())
264    }
265}
266
267pub(crate) const HYBRID_SWITCH_INSERTION_LEN: usize = 11;
268pub(crate) const HYBRID_SWITCH_REMOVAL_LEN: usize = 5;
269
270/// A map collection whose representation is a `Vec` for small `N` and a `BTreeMap` for larger `N`,
271/// where the threshold is defined by the constant `HYBRID_SWITCH_INSERTION_LEN` for insertion and
272/// `HYBRID_SWITCH_REMOVAL_LEN` for removal. This is a more space efficient representation for
273/// small `N` than a `BTreeMap`, which has a big impact because component_manager creates lots of
274/// `Dict` objects for component sandboxes.
275///
276/// Details: Rust's `BTreeMap` implementation uses a `B` value of 6, which means each node in the
277/// `BTreeMap` reserves space for 11 entries. Inserting 1 entry will allocate a node with space for
278/// 11 entries. Each entry is 48 bytes, 48 * 11 + some metadata = 544 bytes. Rounding up to the
279/// nearest scudo bucket size means each node consumes 656 bytes of memory.
280///
281/// A BTreeMap with only 2 entries uses 656 bytes when 112 would be sufficient (48 * 2 = 96 fits in
282/// the 112 byte scudo bucket).
283#[derive(Debug)]
284pub(crate) enum HybridMap {
285    Vec(Vec<(Key, Capability)>),
286    Map(BTreeMap<Key, Capability>),
287}
288
289impl Default for HybridMap {
290    fn default() -> Self {
291        Self::Vec(Vec::default())
292    }
293}
294
295impl HybridMap {
296    pub fn get<Q: ?Sized>(&self, key: &Q) -> Result<Option<Capability>, ()>
297    where
298        Key: Borrow<Q> + Ord,
299        Q: Ord,
300    {
301        match self {
302            Self::Vec(vec) => match Self::sorted_vec_index_of(vec, key) {
303                Ok(index) => Ok(Some(vec[index].1.try_clone()?)),
304                Err(_) => Ok(None),
305            },
306            Self::Map(map) => match map.get(key) {
307                Some(capability) => Ok(Some(capability.try_clone()?)),
308                None => Ok(None),
309            },
310        }
311    }
312
313    pub fn insert(
314        &mut self,
315        key: Key,
316        capability: Capability,
317        update_notifiers: &mut UpdateNotifiers,
318    ) -> Result<(), fsandbox::CapabilityStoreError> {
319        match self {
320            Self::Vec(vec) => match Self::sorted_vec_index_of(vec, &key) {
321                Ok(index) => {
322                    update_notifiers.update(EntryUpdate::Remove(&key));
323                    update_notifiers.update(EntryUpdate::Add(&key, &capability));
324                    vec[index].1 = capability;
325                    Err(fsandbox::CapabilityStoreError::ItemAlreadyExists)
326                }
327                Err(index) => {
328                    update_notifiers.update(EntryUpdate::Add(&key, &capability));
329                    if vec.len() + 1 >= HYBRID_SWITCH_INSERTION_LEN {
330                        self.switch_to_map();
331                        let Self::Map(map) = self else { unreachable!() };
332                        map.insert(key, capability);
333                        Ok(())
334                    } else {
335                        vec.reserve_exact(1);
336                        vec.insert(index, (key, capability));
337                        Ok(())
338                    }
339                }
340            },
341            Self::Map(map) => match map.entry(key.clone()) {
342                Entry::Occupied(mut occupied) => {
343                    update_notifiers.update(EntryUpdate::Remove(&key));
344                    update_notifiers.update(EntryUpdate::Add(&key, &capability));
345                    occupied.insert(capability);
346                    Err(fsandbox::CapabilityStoreError::ItemAlreadyExists)
347                }
348                Entry::Vacant(vacant) => {
349                    update_notifiers.update(EntryUpdate::Add(&key, &capability));
350                    vacant.insert(capability);
351                    Ok(())
352                }
353            },
354        }
355    }
356
357    pub fn remove(
358        &mut self,
359        key: &BorrowedKey,
360        update_notifiers: &mut UpdateNotifiers,
361    ) -> Option<Capability> {
362        let result = match self {
363            Self::Vec(vec) => match Self::sorted_vec_index_of(vec, key) {
364                Ok(index) => {
365                    update_notifiers.update(EntryUpdate::Remove(key));
366                    Some(vec.remove(index).1)
367                }
368                Err(_) => None,
369            },
370            Self::Map(map) => {
371                let result = map.remove(key);
372                if result.is_some() {
373                    update_notifiers.update(EntryUpdate::Remove(key));
374                    if self.len() <= HYBRID_SWITCH_REMOVAL_LEN {
375                        self.switch_to_vec();
376                    }
377                }
378                result
379            }
380        };
381        result
382    }
383
384    pub fn append(
385        &mut self,
386        other: &Self,
387        update_notifiers: &mut UpdateNotifiers,
388    ) -> Result<(), ()> {
389        if other.is_empty() {
390            // If other is empty then return early.
391            return Ok(());
392        }
393
394        // If any clone would fail, throw an error early and don't modify the dict
395        let to_insert: Result<Vec<_>, _> =
396            other.iter().map(|(k, v)| v.try_clone().map(|v| (k.clone(), v))).collect();
397        let to_insert = to_insert?;
398        for (k, _) in &to_insert {
399            let contains_key = match self {
400                Self::Vec(vec) => matches!(Self::sorted_vec_index_of(vec, k), Ok(_)),
401                Self::Map(map) => map.contains_key(k),
402            };
403            if contains_key {
404                return Err(());
405            }
406        }
407
408        if self.len() + other.len() >= HYBRID_SWITCH_INSERTION_LEN {
409            // If at some point we will need to switch to a map then do it now.
410            self.switch_to_map();
411        } else if let Self::Vec(vec) = self {
412            // We're currently a Vec and won't need to convert to a Map so grow the Vec to the final
413            // size now.
414            vec.reserve(other.len());
415        }
416        for (k, v) in to_insert {
417            self.insert(k, v, update_notifiers).expect("append: insert should have succeeded");
418        }
419        Ok(())
420    }
421
422    pub fn shallow_copy(&self) -> Result<Self, ()> {
423        match self {
424            Self::Vec(vec) => {
425                let mut new_vec = Vec::with_capacity(vec.len());
426                for (key, value) in vec.iter() {
427                    new_vec.push((key.clone(), value.try_clone()?));
428                }
429                Ok(HybridMap::Vec(new_vec))
430            }
431            Self::Map(map) => {
432                let mut new_map = BTreeMap::new();
433                for (key, value) in map.iter() {
434                    new_map.insert(key.clone(), value.try_clone()?);
435                }
436                Ok(HybridMap::Map(new_map))
437            }
438        }
439    }
440
441    pub fn len(&self) -> usize {
442        match self {
443            Self::Map(map) => map.len(),
444            Self::Vec(vec) => vec.len(),
445        }
446    }
447
448    pub fn is_empty(&self) -> bool {
449        match self {
450            Self::Map(map) => map.is_empty(),
451            Self::Vec(vec) => vec.is_empty(),
452        }
453    }
454
455    pub fn iter(&self) -> impl Iterator<Item = (&Key, &Capability)> {
456        match self {
457            Self::Vec(vec) => itertools::Either::Left(vec.iter().map(|kv| (&kv.0, &kv.1))),
458            Self::Map(map) => itertools::Either::Right(map.iter()),
459        }
460    }
461
462    pub fn into_iter(self) -> impl Iterator<Item = (Key, Capability)> {
463        match self {
464            Self::Vec(vec) => itertools::Either::Left(vec.into_iter()),
465            Self::Map(map) => itertools::Either::Right(map.into_iter()),
466        }
467    }
468
469    fn switch_to_map(&mut self) {
470        match self {
471            Self::Map(_) => {}
472            Self::Vec(vec) => {
473                let vec = std::mem::replace(vec, Vec::new());
474                let map = BTreeMap::from_iter(vec.into_iter());
475                *self = Self::Map(map);
476            }
477        }
478    }
479
480    fn switch_to_vec(&mut self) {
481        match self {
482            Self::Vec(_) => {}
483            Self::Map(map) => {
484                let map = std::mem::replace(map, Default::default());
485                let vec = Vec::from_iter(map.into_iter());
486                *self = Self::Vec(vec);
487            }
488        }
489    }
490
491    #[inline]
492    fn sorted_vec_index_of<Q: ?Sized>(vec: &Vec<(Key, Capability)>, key: &Q) -> Result<usize, usize>
493    where
494        Key: Borrow<Q> + Ord,
495        Q: Ord,
496    {
497        vec.binary_search_by(|probe| probe.0.borrow().cmp(&key))
498    }
499}
500
501#[derive(Default)]
502pub(crate) struct UpdateNotifiers(Vec<UpdateNotifierFn>);
503
504impl UpdateNotifiers {
505    fn update<'a>(&'a mut self, update: EntryUpdate<'a>) {
506        self.0.retain_mut(|notifier_fn| match (notifier_fn)(update) {
507            UpdateNotifierRetention::Retain => true,
508            UpdateNotifierRetention::Drop_ => false,
509        });
510    }
511}
512
513// Tests are located in fidl/dict.rs.