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