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 extraction and handling.
16 //!
17 //! Descriptors are information encoded into vbmeta images which can be
18 //! extracted from the resulting data after performing verification.
19
20 extern crate alloc;
21
22 mod chain;
23 mod commandline;
24 mod hash;
25 mod hashtree;
26 mod property;
27 mod util;
28
29 use crate::VbmetaData;
30 use alloc::vec::Vec;
31 use avb_bindgen::{
32 avb_descriptor_foreach, avb_descriptor_validate_and_byteswap, AvbDescriptor, AvbDescriptorTag,
33 };
34 use core::{
35 ffi::{c_void, FromBytesUntilNulError},
36 mem::size_of,
37 slice,
38 str::Utf8Error,
39 };
40
41 pub use chain::{ChainPartitionDescriptor, ChainPartitionDescriptorFlags};
42 pub use commandline::{KernelCommandlineDescriptor, KernelCommandlineDescriptorFlags};
43 pub use hash::{HashDescriptor, HashDescriptorFlags};
44 pub use hashtree::{HashtreeDescriptor, HashtreeDescriptorFlags};
45 pub use property::PropertyDescriptor;
46
47 /// A single descriptor.
48 #[derive(Debug, PartialEq, Eq)]
49 pub enum Descriptor<'a> {
50 /// Wraps `AvbPropertyDescriptor`.
51 Property(PropertyDescriptor<'a>),
52 /// Wraps `AvbHashtreeDescriptor`.
53 Hashtree(HashtreeDescriptor<'a>),
54 /// Wraps `AvbHashDescriptor`.
55 Hash(HashDescriptor<'a>),
56 /// Wraps `AvbKernelCmdlineDescriptor`.
57 KernelCommandline(KernelCommandlineDescriptor<'a>),
58 /// Wraps `AvbChainPartitionDescriptor`.
59 ChainPartition(ChainPartitionDescriptor<'a>),
60 /// Unknown descriptor type.
61 Unknown(&'a [u8]),
62 }
63
64 /// Possible errors when extracting descriptors.
65 #[derive(Clone, Debug, PartialEq, Eq)]
66 pub enum DescriptorError {
67 /// libavb rejected the descriptor header.
68 InvalidHeader,
69 /// A value in the descriptor was invalid.
70 InvalidValue,
71 /// The descriptor claimed to be larger than the available data.
72 InvalidSize,
73 /// A field that was supposed to be valid UTF-8 was not.
74 InvalidUtf8,
75 /// Descriptor contents don't match what we expect.
76 InvalidContents,
77 }
78
79 impl From<Utf8Error> for DescriptorError {
from(_: Utf8Error) -> Self80 fn from(_: Utf8Error) -> Self {
81 Self::InvalidUtf8
82 }
83 }
84
85 impl From<FromBytesUntilNulError> for DescriptorError {
from(_: FromBytesUntilNulError) -> Self86 fn from(_: FromBytesUntilNulError) -> Self {
87 Self::InvalidContents
88 }
89 }
90
91 /// `Result` type for `DescriptorError` errors.
92 pub type DescriptorResult<T> = Result<T, DescriptorError>;
93
94 impl<'a> Descriptor<'a> {
95 /// Extracts the fully-typed descriptor from the generic `AvbDescriptor` header.
96 ///
97 /// # Arguments
98 /// * `raw_descriptor`: the raw `AvbDescriptor` pointing into the vbmeta image.
99 ///
100 /// # Returns
101 /// The fully-typed `Descriptor`, or `DescriptorError` if parsing the descriptor failed.
102 ///
103 /// # Safety
104 /// `raw_descriptor` must point to a valid `AvbDescriptor`, including the `num_bytes_following`
105 /// data contents, that lives at least as long as `'a`.
new(raw_descriptor: *const AvbDescriptor) -> DescriptorResult<Self>106 unsafe fn new(raw_descriptor: *const AvbDescriptor) -> DescriptorResult<Self> {
107 // Transform header to host-endian.
108 let mut descriptor = AvbDescriptor {
109 tag: 0,
110 num_bytes_following: 0,
111 };
112 // SAFETY: both args point to valid `AvbDescriptor` objects.
113 if !unsafe { avb_descriptor_validate_and_byteswap(raw_descriptor, &mut descriptor) } {
114 return Err(DescriptorError::InvalidHeader);
115 }
116
117 // Extract the descriptor header and contents bytes. The descriptor sub-type headers
118 // include the top-level header as the first member, so we need to grab the entire
119 // descriptor including the top-level header.
120 let num_bytes_following = descriptor
121 .num_bytes_following
122 .try_into()
123 .map_err(|_| DescriptorError::InvalidValue)?;
124 let total_size = size_of::<AvbDescriptor>()
125 .checked_add(num_bytes_following)
126 .ok_or(DescriptorError::InvalidValue)?;
127
128 // SAFETY: `raw_descriptor` points to the header plus `num_bytes_following` bytes.
129 let contents = unsafe { slice::from_raw_parts(raw_descriptor as *const u8, total_size) };
130
131 match descriptor.tag.try_into() {
132 Ok(AvbDescriptorTag::AVB_DESCRIPTOR_TAG_PROPERTY) => {
133 Ok(Descriptor::Property(PropertyDescriptor::new(contents)?))
134 }
135 Ok(AvbDescriptorTag::AVB_DESCRIPTOR_TAG_HASHTREE) => {
136 Ok(Descriptor::Hashtree(HashtreeDescriptor::new(contents)?))
137 }
138 Ok(AvbDescriptorTag::AVB_DESCRIPTOR_TAG_HASH) => {
139 Ok(Descriptor::Hash(HashDescriptor::new(contents)?))
140 }
141 Ok(AvbDescriptorTag::AVB_DESCRIPTOR_TAG_KERNEL_CMDLINE) => Ok(
142 Descriptor::KernelCommandline(KernelCommandlineDescriptor::new(contents)?),
143 ),
144 Ok(AvbDescriptorTag::AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) => Ok(
145 Descriptor::ChainPartition(ChainPartitionDescriptor::new(contents)?),
146 ),
147 _ => Ok(Descriptor::Unknown(contents)),
148 }
149 }
150 }
151
152 /// Returns a vector of descriptors extracted from the given vbmeta image.
153 ///
154 /// # Arguments
155 /// * `vbmeta`: the `VbmetaData` object to extract descriptors from.
156 ///
157 /// # Returns
158 /// The descriptors, or `DescriptorError` if any error occurred.
159 ///
160 /// # Safety
161 /// `vbmeta` must have been validated by `slot_verify()`.
get_descriptors(vbmeta: &VbmetaData) -> DescriptorResult<Vec<Descriptor>>162 pub(crate) unsafe fn get_descriptors(vbmeta: &VbmetaData) -> DescriptorResult<Vec<Descriptor>> {
163 let mut result = Ok(Vec::new());
164
165 // Use `avb_descriptor_foreach()` to grab all the descriptor pointers in `vmbeta.data()`.
166 // This implementation processes all the descriptors immediately, so that any error is
167 // detected here and working with descriptors can be error-free.
168 //
169 // SAFETY:
170 // * the caller ensures that `vbmeta` has been validated by `slot_verify()`, which satisfies
171 // the libavb `avb_vbmeta_image_verify()` requirement.
172 // * `avb_descriptor_foreach()` ensures the validity of each descriptor pointer passed to
173 // the `fill_descriptors_vec()` callback.
174 // * our lifetimes guarantee that the raw descriptor data in `vbmeta` will remain unchanged for
175 // the lifetime of the returned `Descriptor` objects.
176 // * the `user_data` param is a valid `DescriptorResult<Vec<Descriptor>>` with no other
177 // concurrent access.
178 unsafe {
179 // We can ignore the return value of this function since we use the passed-in `result`
180 // to convey success/failure as well as more detailed error info.
181 avb_descriptor_foreach(
182 vbmeta.data().as_ptr(),
183 vbmeta.data().len(),
184 Some(fill_descriptors_vec),
185 &mut result as *mut _ as *mut c_void,
186 );
187 }
188
189 result
190 }
191
192 /// Adds the given descriptor to the `Vec` pointed to by `user_data`.
193 ///
194 /// Serves as a C callback for use with `avb_descriptor_foreach()`.
195 ///
196 /// # Returns
197 /// True on success, false on failure (which will stop iteration early).
198 ///
199 /// # Safety
200 /// * `descriptor` must point to a valid `AvbDescriptor`, including the `num_bytes_following`
201 /// data contents, which remains valid and unmodified for the lifetime of the `Descriptor` objects
202 /// in `user_data`.
203 /// * `user_data` must point to a valid `DescriptorResult<Vec<Descriptor>>` with no other concurrent
204 /// access.
fill_descriptors_vec( descriptor: *const AvbDescriptor, user_data: *mut c_void, ) -> bool205 unsafe extern "C" fn fill_descriptors_vec(
206 descriptor: *const AvbDescriptor,
207 user_data: *mut c_void,
208 ) -> bool {
209 // SAFETY: `user_data` gives exclusive access to a valid `DescriptorResult<Vec<Descriptor>>`.
210 let result = unsafe { (user_data as *mut DescriptorResult<Vec<Descriptor>>).as_mut() };
211 // We can always unwrap here because we never pass a NULL pointer as `user_data`.
212 let result = result.unwrap();
213
214 // SAFETY: caller ensures that `descriptor` points to a valid `AvbDescriptor` with header and
215 // body contents, which remains unmodified at least as long as the new `Descriptor`.
216 match unsafe { Descriptor::new(descriptor) } {
217 Ok(d) => {
218 // We can always unwrap here because this function will never be called with an error
219 // in `result`, since we stop iteration as soon as we encounter an error.
220 result.as_mut().unwrap().push(d);
221 true
222 }
223 Err(e) => {
224 // Set the error and stop iteration early.
225 *result = Err(e);
226 false
227 }
228 }
229 }
230
231 #[cfg(test)]
232 mod tests {
233 use super::*;
234
235 #[test]
new_unknown_descriptor()236 fn new_unknown_descriptor() {
237 // A fake descriptor which is valid but with an unknown tag.
238 let data: &[u8] = &[
239 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, // tag = 0x42u64 (BE)
240 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, // num_bytes_following = 8u64 (BE)
241 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // fake contents
242 ];
243
244 // SAFETY: we've crafted a valid descriptor in `data`.
245 let descriptor = unsafe { Descriptor::new(data.as_ptr() as *const _) }.unwrap();
246
247 let contents = match descriptor {
248 Descriptor::Unknown(c) => c,
249 d => panic!("Expected Unknown descriptor, got {d:?}"),
250 };
251 assert_eq!(data, contents);
252 }
253
254 #[test]
new_invalid_descriptor_length_fails()255 fn new_invalid_descriptor_length_fails() {
256 // `avb_descriptor_validate_and_byteswap()` should detect and reject descriptors whose
257 // `num_bytes_following` is not 8-byte aligned.
258 let data: &[u8] = &[
259 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, // tag = 0x42u64 (BE)
260 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, // num_bytes_following = 7u64 (BE)
261 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // fake contents
262 ];
263
264 assert_eq!(
265 // SAFETY: we've created an invalid descriptor in a way that should be detected and
266 // fail safely without triggering any undefined behavior.
267 unsafe { Descriptor::new(data.as_ptr() as *const _) }.unwrap_err(),
268 DescriptorError::InvalidHeader
269 );
270 }
271 }
272