xref: /aosp_15_r20/external/avb/rust/src/descriptor/util.rs (revision d289c2ba6de359471b23d594623b906876bc48a0)
1 // Copyright 2023, 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 //! Descriptor utilities.
16 
17 use super::{DescriptorError, DescriptorResult};
18 use zerocopy::{FromBytes, FromZeroes, Ref};
19 
20 /// Splits `size` bytes off the front of `data`.
21 ///
22 /// This is a thin wrapper around `slice::split_at()` but it:
23 /// 1. Returns a `DescriptorError` rather than panicking if `data` is too small.
24 /// 2. Accepts a variety of `size` types since descriptors commonly use `u32` or `u64`.
25 ///
26 /// # Arguments
27 /// * `data`: descriptor data.
28 /// * `size`: the number of bytes to pull off the front.
29 ///
30 /// # Returns
31 /// A tuple containing (extracted_bytes, data_remainder) on success, or
32 /// `DescriptorError` if we couldn't get `size` bytes out of `data`.
split_slice<T>(data: &[u8], size: T) -> DescriptorResult<(&[u8], &[u8])> where T: TryInto<usize>,33 pub(super) fn split_slice<T>(data: &[u8], size: T) -> DescriptorResult<(&[u8], &[u8])>
34 where
35     T: TryInto<usize>,
36 {
37     let size = size.try_into().map_err(|_| DescriptorError::InvalidValue)?;
38     if size > data.len() {
39         Err(DescriptorError::InvalidSize)
40     } else {
41         Ok(data.split_at(size))
42     }
43 }
44 
45 /// Function type for the `avb_*descriptor_validate_and_byteswap()` C functions.
46 pub(super) type ValidationFunc<T> = unsafe extern "C" fn(*const T, *mut T) -> bool;
47 
48 /// Trait to represent an `Avb*Descriptor` type which has an associated `ValidationFunc`.
49 ///
50 /// This allows the generic `parse_descriptor()` function to extract a descriptor header for any
51 /// descriptor type.
52 ///
53 /// To use, implement `ValidateAndByteSwap` on the `Avb*Descriptor` struct.
54 ///
55 /// # Safety
56 /// The function assigned to `VALIDATE_AND_BYTESWAP_FUNC` must be the libavb
57 /// `avb_*descriptor_validate_and_byteswap()` function corresponding to the descriptor implementing
58 /// this trait (e.g. `AvbHashDescriptor` -> `avb_hash_descriptor_validate_and_byteswap`).
59 pub(super) unsafe trait ValidateAndByteswap {
60     /// The specific libavb validation function for this descriptor type.
61     const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self>;
62 }
63 
64 /// A descriptor that has been extracted, validated, and byteswapped.
65 #[derive(Debug)]
66 pub(super) struct ParsedDescriptor<'a, T> {
67     /// The original raw (big-endian) header.
68     pub raw_header: &'a T,
69     /// A copy of the header in host-endian format.
70     pub header: T,
71     /// The descriptor body contents.
72     pub body: &'a [u8],
73 }
74 
75 /// Extracts the descriptor header from the given buffer.
76 ///
77 /// # Arguments
78 /// `data`: the descriptor contents in raw (big-endian) format.
79 ///
80 /// # Returns
81 /// A `ParsedDescriptor` on success, `DescriptorError` if `data` was too small or the header looks
82 /// invalid.
parse_descriptor<T>(data: &[u8]) -> DescriptorResult<ParsedDescriptor<T>> where T: Default + FromZeroes + FromBytes + ValidateAndByteswap,83 pub(super) fn parse_descriptor<T>(data: &[u8]) -> DescriptorResult<ParsedDescriptor<T>>
84 where
85     T: Default + FromZeroes + FromBytes + ValidateAndByteswap,
86 {
87     let (raw_header, body) =
88         Ref::<_, T>::new_from_prefix(data).ok_or(DescriptorError::InvalidHeader)?;
89     let raw_header = raw_header.into_ref();
90 
91     let mut header = T::default();
92     // SAFETY:
93     // * all `VALIDATE_AND_BYTESWAP_FUNC` functions check the validity of the fields.
94     // * even if the data is corrupted somehow and is not detected by the validation logic, these
95     //   functions never try to access memory outside of the header.
96     if !unsafe { T::VALIDATE_AND_BYTESWAP_FUNC(raw_header, &mut header) } {
97         return Err(DescriptorError::InvalidHeader);
98     }
99 
100     Ok(ParsedDescriptor {
101         raw_header,
102         header,
103         body,
104     })
105 }
106 
107 #[cfg(test)]
108 mod tests {
109     use super::*;
110     use avb_bindgen::{avb_descriptor_validate_and_byteswap, AvbDescriptor, AvbHashDescriptor};
111     use std::mem::size_of;
112 
113     #[test]
split_slice_with_various_size_types_succeeds()114     fn split_slice_with_various_size_types_succeeds() {
115         let data = &[1, 2, 3, 4];
116         let expected = Ok((&data[..2], &data[2..]));
117         assert_eq!(split_slice(data, 2u32), expected);
118         assert_eq!(split_slice(data, 2u64), expected);
119         assert_eq!(split_slice(data, 2usize), expected);
120     }
121 
122     #[test]
split_slice_with_negative_size_fails()123     fn split_slice_with_negative_size_fails() {
124         let data = &[1, 2, 3, 4];
125         assert_eq!(split_slice(data, -1i32), Err(DescriptorError::InvalidValue));
126     }
127 
128     #[test]
split_slice_with_size_overflow_fails()129     fn split_slice_with_size_overflow_fails() {
130         let data = &[1, 2, 3, 4];
131         assert_eq!(split_slice(data, 5u32), Err(DescriptorError::InvalidSize));
132     }
133 
134     // Enable `parse_descriptor()` on a generic `AvbDescriptor` of any sub-type.
135     // SAFETY: `VALIDATE_AND_BYTESWAP_FUNC` is the correct libavb validator for this descriptor.
136     unsafe impl ValidateAndByteswap for AvbDescriptor {
137         const VALIDATE_AND_BYTESWAP_FUNC: ValidationFunc<Self> =
138             avb_descriptor_validate_and_byteswap;
139     }
140 
141     // Hardcoded test descriptor of custom sub-type (tag = 42).
142     const TEST_DESCRIPTOR: &[u8] = &[
143         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, // tag = 0x42u64 (BE)
144         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, // num_bytes_following = 8u64 (BE)
145         0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // fake contents
146     ];
147 
148     #[test]
parse_descriptor_success()149     fn parse_descriptor_success() {
150         let descriptor = parse_descriptor::<AvbDescriptor>(TEST_DESCRIPTOR).unwrap();
151 
152         // `assert_eq!()` cannot be used on `repr(packed)` struct fields.
153         assert!(descriptor.raw_header.tag == 0x42u64.to_be());
154         assert!(descriptor.raw_header.num_bytes_following == 8u64.to_be());
155         assert!(descriptor.header.tag == 0x42);
156         assert!(descriptor.header.num_bytes_following == 8);
157         assert_eq!(descriptor.body, &[1, 2, 3, 4, 5, 6, 7, 8]);
158     }
159 
160     #[test]
parse_descriptor_buffer_too_short_failure()161     fn parse_descriptor_buffer_too_short_failure() {
162         let bad_length = size_of::<AvbDescriptor>() - 1;
163         assert_eq!(
164             parse_descriptor::<AvbDescriptor>(&TEST_DESCRIPTOR[..bad_length]).unwrap_err(),
165             DescriptorError::InvalidHeader
166         );
167     }
168 
169     #[test]
parse_descriptor_wrong_type_failure()170     fn parse_descriptor_wrong_type_failure() {
171         assert_eq!(
172             // `TEST_DESCRIPTOR` is not a valid `AvbHashDescriptor`, this should fail without
173             // triggering any undefined behavior.
174             parse_descriptor::<AvbHashDescriptor>(TEST_DESCRIPTOR).unwrap_err(),
175             DescriptorError::InvalidHeader
176         );
177     }
178 }
179