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