1 // Copyright (C) 2024 The Android Open Source Project
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 //! Low-level utilities shared across multiple GBL libraries.
16
17 #![cfg_attr(not(test), no_std)]
18
19 use liberror::{Error, Result};
20 use safemath::SafeNum;
21
22 /// Returns the largest aligned subslice.
23 ///
24 /// This function drops as many bytes as needed from the front of the given slice to ensure the
25 /// result is properly-aligned. It does not truncate bytes from the end, so the resulting size may
26 /// not be a multiple of `alignment`.
27 ///
28 /// If the next `alignment` boundary would be directly following the last byte, this returns the
29 /// 0-length slice at that alignment rather than an error, to match standard slicing behavior.
30 ///
31 /// # Arguments
32 /// * `bytes`: the byte slice to align
33 /// * `alignment`: the desired starting alignment
34 ///
35 /// # Returns
36 /// * The subslice on success
37 /// * [Error::ArithmeticOverflow] if `bytes` overflows when finding the next `alignment`
38 /// * [Error::BufferTooSmall] if `bytes` is not large enough to reach the next `alignment`. The
39 /// error will contain the size that would have been needed to reach `alignment`.
aligned_subslice<T>(bytes: &mut [u8], alignment: T) -> Result<&mut [u8]> where T: Copy + Into<SafeNum>,40 pub fn aligned_subslice<T>(bytes: &mut [u8], alignment: T) -> Result<&mut [u8]>
41 where
42 T: Copy + Into<SafeNum>,
43 {
44 let addr = bytes.as_ptr() as usize;
45 let aligned_offset = (SafeNum::from(addr).round_up(alignment) - addr).try_into()?;
46 Ok(bytes.get_mut(aligned_offset..).ok_or(Error::BufferTooSmall(Some(aligned_offset)))?)
47 }
48
49 /// A helper for getting the offset of the first byte with and aligned address.
50 ///
51 /// # Arguments
52 /// * `bytes`: the byte slice
53 /// * `alignment`: the desired starting alignment.
54 ///
55 /// # Returns
56 ///
57 /// * Returns Ok(offset) on success, Err() on integer overflow.
aligned_offset<T>(buffer: &[u8], alignment: T) -> Result<usize> where T: Copy + Into<SafeNum>,58 pub fn aligned_offset<T>(buffer: &[u8], alignment: T) -> Result<usize>
59 where
60 T: Copy + Into<SafeNum>,
61 {
62 let addr = SafeNum::from(buffer.as_ptr() as usize);
63 (addr.round_up(alignment) - addr).try_into().map_err(From::from)
64 }
65
66 #[cfg(test)]
67 mod test {
68 use super::*;
69
70 // A byte array that's always at least 8-byte aligned for testing.
71 #[repr(align(8))]
72 struct AlignedBytes<const N: usize>([u8; N]);
73
74 #[test]
aligned_subslice_already_aligned()75 fn aligned_subslice_already_aligned() {
76 let mut bytes = AlignedBytes([0u8; 16]);
77 let bytes = &mut bytes.0;
78
79 // AlignedBytes is `align(8)`, so must be 1/2/4/8-aligned.
80 assert_eq!(aligned_subslice(bytes, 1).unwrap().as_ptr_range(), bytes.as_ptr_range());
81 assert_eq!(aligned_subslice(bytes, 2).unwrap().as_ptr_range(), bytes.as_ptr_range());
82 assert_eq!(aligned_subslice(bytes, 4).unwrap().as_ptr_range(), bytes.as_ptr_range());
83 assert_eq!(aligned_subslice(bytes, 8).unwrap().as_ptr_range(), bytes.as_ptr_range());
84 }
85
86 #[test]
aligned_subslice_unaligned()87 fn aligned_subslice_unaligned() {
88 let mut bytes = AlignedBytes([0u8; 16]);
89 let bytes = &mut bytes.0;
90
91 // AlignedBytes is 8-aligned, so offsetting by <8 should snap to the next 8-alignment.
92 assert_eq!(
93 aligned_subslice(&mut bytes[1..], 8).unwrap().as_ptr_range(),
94 bytes[8..].as_ptr_range()
95 );
96 assert_eq!(
97 aligned_subslice(&mut bytes[4..], 8).unwrap().as_ptr_range(),
98 bytes[8..].as_ptr_range()
99 );
100 assert_eq!(
101 aligned_subslice(&mut bytes[7..], 8).unwrap().as_ptr_range(),
102 bytes[8..].as_ptr_range()
103 );
104 }
105
106 #[test]
aligned_subslice_empty_slice()107 fn aligned_subslice_empty_slice() {
108 let mut bytes = AlignedBytes([0u8; 16]);
109 let bytes = &mut bytes.0;
110
111 // If the next alignment is just past the input, return the empty slice.
112 assert_eq!(
113 aligned_subslice(&mut bytes[9..], 8).unwrap().as_ptr_range(),
114 bytes[16..].as_ptr_range()
115 );
116 }
117
118 #[test]
aligned_subslice_buffer_overflow()119 fn aligned_subslice_buffer_overflow() {
120 let mut bytes = AlignedBytes([0u8; 7]); // 7 bytes; can't reach the next 8-alignment.
121 let bytes = &mut bytes.0;
122
123 assert_eq!(aligned_subslice(&mut bytes[1..], 8), Err(Error::BufferTooSmall(Some(7))));
124 assert_eq!(aligned_subslice(&mut bytes[6..], 8), Err(Error::BufferTooSmall(Some(2))));
125 }
126
127 #[test]
aligned_subslice_alignment_overflow()128 fn aligned_subslice_alignment_overflow() {
129 let mut bytes = AlignedBytes([0u8; 16]);
130 let bytes = &mut bytes.0;
131
132 assert!(matches!(aligned_subslice(bytes, SafeNum::MAX), Err(Error::ArithmeticOverflow(_))));
133 }
134 }
135