1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Types for creating arenas used in deserialization of np_adv. This implementation is purpose-made
16 //! for deserializing in `np_adv` and is not intended for general use as an arena.
17 
18 use crate::extended::BLE_5_ADV_SVC_MAX_CONTENT_LEN;
19 
20 /// Create a [`DeserializationArena`] suitable for use with deserializing an advertisement.
21 #[macro_export]
22 macro_rules! deserialization_arena {
23     // Trick borrowed from `pin!`: In an argument to a braced constructor, if we take a reference to
24     // a temporary value, that value will be upgraded to live for the scope of the enclosing block,
25     // avoiding another `let` binding for the buffer which is normally required to keep the buffer
26     // on the stack.
27     // Reference: https://doc.rust-lang.org/reference/destructors.html#temporary-lifetime-extension
28     () => {
29         $crate::deserialization_arena::DeserializationArena {
30             buffer: &mut $crate::deserialization_arena::DeserializationArena::new_buffer(),
31         }
32     };
33 }
34 
35 /// A simple allocator that simply keeps splitting the given slice on allocation. Use the
36 /// [`deserialization_arena!`][crate::deserialization_arena!] macro to create an instance.
37 pub(crate) struct DeserializationArenaAllocator<'a> {
38     #[doc(hidden)]
39     slice: &'a mut [u8],
40 }
41 
42 impl<'a> DeserializationArenaAllocator<'a> {
43     /// Allocates `len` bytes from the slice given upon construction. In the expected use case, the
44     /// allocated slice should be written to with actual data, overwriting what's contained in
45     /// there. While reading from the allocated slice without first writing to it is safe in the
46     /// Rust memory-safety sense, the returned slice contains "garbage" data from the slice given
47     /// during construction.
48     ///
49     /// Returns an error with [`ArenaOutOfSpace`] if the remaining slice is not long enough to
50     /// allocate `len` bytes.
allocate(&mut self, len: u8) -> Result<&'a mut [u8], ArenaOutOfSpace>51     pub fn allocate(&mut self, len: u8) -> Result<&'a mut [u8], ArenaOutOfSpace> {
52         if usize::from(len) > self.slice.len() {
53             return Err(ArenaOutOfSpace);
54         }
55         let (allocated, remaining) = core::mem::take(&mut self.slice).split_at_mut(len.into());
56         self.slice = remaining;
57         // Note: the returned data is logically garbage. While it's deterministic (not UB),
58         // semantically this should be treated as a write only slice.
59         Ok(allocated)
60     }
61 }
62 
63 /// A simple allocator that simply keeps splitting the given slice on allocation. Use the
64 /// [`deserialization_arena!`][crate::deserialization_arena!] macro to create an instance.
65 pub struct DeserializationArena<'a> {
66     #[doc(hidden)] // Exposed for use by `deserialization_arena!` only.
67     pub buffer: &'a mut [u8; BLE_5_ADV_SVC_MAX_CONTENT_LEN],
68 }
69 
70 impl<'a> DeserializationArena<'a> {
71     #[doc(hidden)] // Exposed for use by `deserialization_arena!` only.
new_buffer() -> [u8; BLE_5_ADV_SVC_MAX_CONTENT_LEN]72     pub fn new_buffer() -> [u8; BLE_5_ADV_SVC_MAX_CONTENT_LEN] {
73         [0; BLE_5_ADV_SVC_MAX_CONTENT_LEN]
74     }
75 
76     /// Convert this arena into an allocator that can start allocating.
into_allocator(self) -> DeserializationArenaAllocator<'a>77     pub(crate) fn into_allocator(self) -> DeserializationArenaAllocator<'a> {
78         DeserializationArenaAllocator { slice: self.buffer }
79     }
80 }
81 
82 /// Error indicating that the arena has ran out of space, and deserialization cannot proceed. This
83 /// should never happen if the arena is created with [`crate::deserialization_arena!`], since the
84 /// total size of decrypted sections should be less than the size of the incoming BLE advertisement.
85 #[derive(Debug, PartialEq, Eq)]
86 pub(crate) struct ArenaOutOfSpace;
87 
88 #[cfg(test)]
89 mod test {
90     use crate::{deserialization_arena::ArenaOutOfSpace, extended::BLE_5_ADV_SVC_MAX_CONTENT_LEN};
91 
92     #[test]
test_creation()93     fn test_creation() {
94         assert_eq!(BLE_5_ADV_SVC_MAX_CONTENT_LEN, deserialization_arena!().buffer.len());
95     }
96 
97     #[test]
test_allocation()98     fn test_allocation() {
99         let arena = deserialization_arena!();
100         let mut allocator = arena.into_allocator();
101         assert_eq!(Ok(&mut [0_u8; 100][..]), allocator.allocate(100));
102         assert_eq!(BLE_5_ADV_SVC_MAX_CONTENT_LEN - 100, allocator.slice.len());
103     }
104 
105     #[test]
test_allocation_overflow()106     fn test_allocation_overflow() {
107         let arena = deserialization_arena!();
108         let mut allocator = arena.into_allocator();
109         assert_eq!(Err(ArenaOutOfSpace), allocator.allocate(u8::MAX));
110     }
111 
112     #[test]
test_allocation_twice_overflow()113     fn test_allocation_twice_overflow() {
114         let arena = deserialization_arena!();
115         let mut allocator = arena.into_allocator();
116         assert_eq!(Ok(&mut [0_u8; 128][..]), allocator.allocate(128));
117         assert_eq!(Err(ArenaOutOfSpace), allocator.allocate(128));
118     }
119 }
120