Dov Shlachter | 6676eb6 | 2023-08-22 15:11:08 -0700 | [diff] [blame] | 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 | //! The public interface for bootimg structs |
| 16 | use zerocopy::{ByteSlice, LayoutVerified}; |
| 17 | |
| 18 | use bootimg_private::{ |
| 19 | boot_img_hdr_v0, boot_img_hdr_v1, boot_img_hdr_v2, boot_img_hdr_v3, boot_img_hdr_v4, |
| 20 | vendor_boot_img_hdr_v3, vendor_boot_img_hdr_v4, BOOT_MAGIC, BOOT_MAGIC_SIZE, VENDOR_BOOT_MAGIC, |
| 21 | VENDOR_BOOT_MAGIC_SIZE, |
| 22 | }; |
| 23 | |
| 24 | /// Generalized boot image from a backing store of bytes. |
| 25 | #[derive(PartialEq, Debug)] |
| 26 | pub enum BootImage<B: ByteSlice + PartialEq> { |
| 27 | /// Version 0 header |
| 28 | V0(LayoutVerified<B, boot_img_hdr_v0>), |
| 29 | /// Version 1 header |
| 30 | V1(LayoutVerified<B, boot_img_hdr_v1>), |
| 31 | /// Version 2 header |
| 32 | V2(LayoutVerified<B, boot_img_hdr_v2>), |
| 33 | /// Version 3 header |
| 34 | V3(LayoutVerified<B, boot_img_hdr_v3>), |
| 35 | /// Version 4 header |
| 36 | V4(LayoutVerified<B, boot_img_hdr_v4>), |
| 37 | } |
| 38 | |
| 39 | /// Generalized vendor boot header from a backing store of bytes. |
| 40 | #[derive(PartialEq, Debug)] |
| 41 | pub enum VendorImageHeader<B: ByteSlice + PartialEq> { |
| 42 | /// Version 3 header |
| 43 | V3(LayoutVerified<B, vendor_boot_img_hdr_v3>), |
| 44 | /// Version 4 header |
| 45 | V4(LayoutVerified<B, vendor_boot_img_hdr_v4>), |
| 46 | } |
| 47 | |
| 48 | /// Boot related errors. |
| 49 | #[derive(PartialEq, Debug)] |
| 50 | pub enum ImageError { |
| 51 | /// The provided buffer was too small to hold a header. |
| 52 | BufferTooSmall, |
| 53 | /// The magic string was incorrect. |
| 54 | BadMagic, |
| 55 | /// The header version present is not supported. |
| 56 | UnexpectedVersion, |
| 57 | /// Catch-all for remaining errors. |
| 58 | Unknown, |
| 59 | } |
| 60 | |
| 61 | impl core::fmt::Display for ImageError { |
| 62 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 63 | let str = match self { |
| 64 | Self::BufferTooSmall => "The provided buffer is too small", |
| 65 | Self::BadMagic => "The magic string is incorrect", |
| 66 | Self::UnexpectedVersion => "Header version is not supported", |
| 67 | Self::Unknown => "Unknown error", |
| 68 | }; |
| 69 | write!(f, "{str}") |
| 70 | } |
| 71 | } |
| 72 | |
| 73 | /// Common result type for use with boot headers |
| 74 | pub type BootResult<T> = Result<T, ImageError>; |
| 75 | |
| 76 | fn parse_header<B: ByteSlice + PartialEq, T>(buffer: B) -> BootResult<LayoutVerified<B, T>> { |
| 77 | Ok(LayoutVerified::<B, T>::new_from_prefix(buffer).ok_or(ImageError::BufferTooSmall)?.0) |
| 78 | } |
| 79 | |
| 80 | impl<B: ByteSlice + PartialEq> BootImage<B> { |
| 81 | /// Given a byte buffer, attempt to parse the contents and return a zero-copy reference |
| 82 | /// to the associated boot image header. |
| 83 | /// |
| 84 | /// # Arguments |
| 85 | /// * `buffer` - buffer to parse |
| 86 | /// |
| 87 | /// # Returns |
| 88 | /// |
| 89 | /// * `Ok(BootImage)` - if parsing was successful. |
| 90 | /// * `Err(ImageError)` - if `buffer` does not contain a valid boot image header. |
| 91 | /// |
| 92 | /// # Example |
| 93 | /// |
| 94 | /// ``` |
| 95 | /// use bootimg::BootImage; |
| 96 | /// |
| 97 | /// let mut buffer = [0; 4096]; |
| 98 | /// // Not shown: read first 4096 bytes of boot image into buffer |
| 99 | /// let header = BootImage::parse(&buffer[..]).unwrap(); |
| 100 | /// ``` |
| 101 | pub fn parse(buffer: B) -> BootResult<Self> { |
| 102 | let magic_size = BOOT_MAGIC_SIZE as usize; |
| 103 | // Note: even though the v3 header is not a prefix for the v0, v1, or v2 header, |
| 104 | // the version and the magic string exist at the same offset and have the same types. |
| 105 | // Make a v3 temporary because it is the smallest. |
| 106 | let (hdr, _) = |
| 107 | LayoutVerified::<&[u8], boot_img_hdr_v3>::new_from_prefix(buffer.get(..).unwrap()) |
| 108 | .ok_or(ImageError::BufferTooSmall)?; |
| 109 | |
| 110 | if hdr.magic.ne(&BOOT_MAGIC[..magic_size]) { |
| 111 | return Err(ImageError::BadMagic); |
| 112 | } |
| 113 | |
| 114 | match hdr.header_version { |
| 115 | 0 => Ok(Self::V0(parse_header::<B, boot_img_hdr_v0>(buffer)?)), |
| 116 | 1 => Ok(Self::V1(parse_header::<B, boot_img_hdr_v1>(buffer)?)), |
| 117 | 2 => Ok(Self::V2(parse_header::<B, boot_img_hdr_v2>(buffer)?)), |
| 118 | 3 => Ok(Self::V3(parse_header::<B, boot_img_hdr_v3>(buffer)?)), |
| 119 | 4 => Ok(Self::V4(parse_header::<B, boot_img_hdr_v4>(buffer)?)), |
| 120 | _ => Err(ImageError::UnexpectedVersion), |
| 121 | } |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | impl<B: ByteSlice + PartialEq> VendorImageHeader<B> { |
| 126 | /// Given a byte buffer, attempt to parse the contents and return a zero-copy reference |
| 127 | /// to the associated vendor boot image header. |
| 128 | /// |
| 129 | /// # Arguments |
| 130 | /// * `buffer` - buffer to parse |
| 131 | /// |
| 132 | /// # Returns |
| 133 | /// |
| 134 | /// * `Ok(VendorImageHeader)` - if parsing was successful. |
| 135 | /// * `Err(ImageError)` - If `buffer` does not contain a valid boot image header. |
| 136 | /// |
| 137 | /// # Example |
| 138 | /// |
| 139 | /// ``` |
| 140 | /// use bootimg::VendorImageHeader; |
| 141 | /// |
| 142 | /// let mut buffer = [0; 4096]; |
| 143 | /// // Not shown: read first 4096 bytes of vendor image into buffer |
| 144 | /// let header = VendorImageHeader::parse(&buffer[..]).unwrap(); |
| 145 | /// ``` |
| 146 | pub fn parse(buffer: B) -> BootResult<Self> { |
| 147 | let magic_size = VENDOR_BOOT_MAGIC_SIZE as usize; |
| 148 | let (hdr, _) = LayoutVerified::<&[u8], vendor_boot_img_hdr_v3>::new_from_prefix( |
| 149 | buffer.get(..).unwrap(), |
| 150 | ) |
| 151 | .ok_or(ImageError::BufferTooSmall)?; |
| 152 | |
| 153 | if hdr.magic.ne(&VENDOR_BOOT_MAGIC[..magic_size]) { |
| 154 | return Err(ImageError::BadMagic); |
| 155 | } |
| 156 | |
| 157 | match hdr.header_version { |
| 158 | 3 => Ok(Self::V3(parse_header::<B, vendor_boot_img_hdr_v3>(buffer)?)), |
| 159 | 4 => Ok(Self::V4(parse_header::<B, vendor_boot_img_hdr_v4>(buffer)?)), |
| 160 | _ => Err(ImageError::UnexpectedVersion), |
| 161 | } |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | #[cfg(test)] |
| 166 | mod tests { |
| 167 | use super::*; |
| 168 | use zerocopy::AsBytes; |
| 169 | |
| 170 | const MAGIC_SIZE: usize = BOOT_MAGIC_SIZE as usize; |
| 171 | const VENDOR_MAGIC_SIZE: usize = VENDOR_BOOT_MAGIC_SIZE as usize; |
| 172 | |
| 173 | pub fn add<T: AsBytes>(buffer: &mut [u8], t: T) { |
| 174 | t.write_to_prefix(buffer).unwrap(); |
| 175 | } |
| 176 | |
| 177 | #[test] |
| 178 | fn buffer_too_small_for_version() { |
| 179 | let buffer = [0; 40]; |
| 180 | assert_eq!(BootImage::parse(&buffer[..]), Err(ImageError::BufferTooSmall)); |
| 181 | } |
| 182 | |
| 183 | #[test] |
| 184 | fn buffer_too_small_valid_version() { |
| 185 | // Note: because the v1 header fully encapsulates the v0 header, |
| 186 | // we can trigger a buffer-too-small error by providing |
| 187 | // a perfectly valid v0 header and changing the version to 1. |
| 188 | let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v0>()]; |
| 189 | add::<boot_img_hdr_v0>( |
| 190 | &mut buffer, |
| 191 | boot_img_hdr_v0 { |
| 192 | magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(), |
| 193 | header_version: 1, |
| 194 | ..Default::default() |
| 195 | }, |
| 196 | ); |
| 197 | assert_eq!(BootImage::parse(&buffer[..]), Err(ImageError::BufferTooSmall)); |
| 198 | } |
| 199 | |
| 200 | #[test] |
| 201 | fn bad_magic() { |
| 202 | let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v0>()]; |
| 203 | add::<boot_img_hdr_v0>( |
| 204 | &mut buffer, |
| 205 | boot_img_hdr_v0 { magic: *b"ANDROGEN", ..Default::default() }, |
| 206 | ); |
| 207 | assert_eq!(BootImage::parse(&buffer[..]), Err(ImageError::BadMagic)); |
| 208 | } |
| 209 | |
| 210 | #[test] |
| 211 | fn bad_version() { |
| 212 | let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v0>()]; |
| 213 | add::<boot_img_hdr_v0>( |
| 214 | &mut buffer, |
| 215 | boot_img_hdr_v0 { |
| 216 | magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(), |
| 217 | header_version: 2112, |
| 218 | ..Default::default() |
| 219 | }, |
| 220 | ); |
| 221 | assert_eq!(BootImage::parse(&buffer[..]), Err(ImageError::UnexpectedVersion)); |
| 222 | } |
| 223 | |
| 224 | #[test] |
| 225 | fn parse_v0() { |
| 226 | let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v0>()]; |
| 227 | add::<boot_img_hdr_v0>( |
| 228 | &mut buffer, |
| 229 | boot_img_hdr_v0 { |
| 230 | magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(), |
| 231 | header_version: 0, |
| 232 | ..Default::default() |
| 233 | }, |
| 234 | ); |
| 235 | let expected = |
| 236 | Ok(BootImage::V0(LayoutVerified::<&[u8], boot_img_hdr_v0>::new(&buffer).unwrap())); |
| 237 | assert_eq!(BootImage::parse(&buffer[..]), expected); |
| 238 | } |
| 239 | |
| 240 | #[test] |
| 241 | fn parse_v1() { |
| 242 | let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v1>()]; |
| 243 | add::<boot_img_hdr_v1>( |
| 244 | &mut buffer, |
| 245 | boot_img_hdr_v1 { |
| 246 | _base: boot_img_hdr_v0 { |
| 247 | magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(), |
| 248 | header_version: 1, |
| 249 | ..Default::default() |
| 250 | }, |
| 251 | ..Default::default() |
| 252 | }, |
| 253 | ); |
| 254 | let expected = |
| 255 | Ok(BootImage::V1(LayoutVerified::<&[u8], boot_img_hdr_v1>::new(&buffer).unwrap())); |
| 256 | assert_eq!(BootImage::parse(&buffer[..]), expected); |
| 257 | } |
| 258 | |
| 259 | #[test] |
| 260 | fn parse_v2() { |
| 261 | let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v2>()]; |
| 262 | add::<boot_img_hdr_v2>( |
| 263 | &mut buffer, |
| 264 | boot_img_hdr_v2 { |
| 265 | _base: boot_img_hdr_v1 { |
| 266 | _base: boot_img_hdr_v0 { |
| 267 | magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(), |
| 268 | header_version: 2, |
| 269 | ..Default::default() |
| 270 | }, |
| 271 | ..Default::default() |
| 272 | }, |
| 273 | ..Default::default() |
| 274 | }, |
| 275 | ); |
| 276 | let expected = |
| 277 | Ok(BootImage::V2(LayoutVerified::<&[u8], boot_img_hdr_v2>::new(&buffer).unwrap())); |
| 278 | assert_eq!(BootImage::parse(&buffer[..]), expected); |
| 279 | } |
| 280 | |
| 281 | #[test] |
| 282 | fn parse_v3() { |
| 283 | let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v3>()]; |
| 284 | add::<boot_img_hdr_v3>( |
| 285 | &mut buffer, |
| 286 | boot_img_hdr_v3 { |
| 287 | magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(), |
| 288 | header_version: 3, |
| 289 | ..Default::default() |
| 290 | }, |
| 291 | ); |
| 292 | let expected = |
| 293 | Ok(BootImage::V3(LayoutVerified::<&[u8], boot_img_hdr_v3>::new(&buffer).unwrap())); |
| 294 | assert_eq!(BootImage::parse(&buffer[..]), expected); |
| 295 | } |
| 296 | |
| 297 | #[test] |
| 298 | fn parse_v4() { |
| 299 | let mut buffer = [0; core::mem::size_of::<boot_img_hdr_v4>()]; |
| 300 | add::<boot_img_hdr_v4>( |
| 301 | &mut buffer, |
| 302 | boot_img_hdr_v4 { |
| 303 | _base: boot_img_hdr_v3 { |
| 304 | magic: BOOT_MAGIC[0..MAGIC_SIZE].try_into().unwrap(), |
| 305 | header_version: 4, |
| 306 | ..Default::default() |
| 307 | }, |
| 308 | ..Default::default() |
| 309 | }, |
| 310 | ); |
| 311 | let expected = |
| 312 | Ok(BootImage::V4(LayoutVerified::<&[u8], boot_img_hdr_v4>::new(&buffer).unwrap())); |
| 313 | assert_eq!(BootImage::parse(&buffer[..]), expected); |
| 314 | } |
| 315 | |
| 316 | #[test] |
| 317 | fn vendor_buffer_too_small_for_version() { |
| 318 | let buffer = [0; VENDOR_MAGIC_SIZE + 3]; |
| 319 | assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::BufferTooSmall)); |
| 320 | } |
| 321 | |
| 322 | #[test] |
| 323 | fn vendor_bad_magic() { |
| 324 | let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()]; |
| 325 | add::<vendor_boot_img_hdr_v3>( |
| 326 | &mut buffer, |
| 327 | vendor_boot_img_hdr_v3 { magic: *b"VNDRBOOK", header_version: 3, ..Default::default() }, |
| 328 | ); |
| 329 | assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::BadMagic)); |
| 330 | } |
| 331 | |
| 332 | #[test] |
| 333 | fn vendor_bad_version() { |
| 334 | let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()]; |
| 335 | add::<vendor_boot_img_hdr_v3>( |
| 336 | &mut buffer, |
| 337 | vendor_boot_img_hdr_v3 { |
| 338 | magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(), |
| 339 | header_version: 2112, |
| 340 | ..Default::default() |
| 341 | }, |
| 342 | ); |
| 343 | assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::UnexpectedVersion)); |
| 344 | } |
| 345 | |
| 346 | #[test] |
| 347 | fn vendor_buffer_too_small_valid_version() { |
| 348 | let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()]; |
| 349 | add::<vendor_boot_img_hdr_v3>( |
| 350 | &mut buffer, |
| 351 | vendor_boot_img_hdr_v3 { |
| 352 | magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(), |
| 353 | // Note: because the v4 header fully encapsulates the v3 header, |
| 354 | // we can trigger a buffer-too-small error by providing |
| 355 | // a perfectly valid v3 header and changing the version to 4. |
| 356 | header_version: 4, |
| 357 | ..Default::default() |
| 358 | }, |
| 359 | ); |
| 360 | assert_eq!(VendorImageHeader::parse(&buffer[..]), Err(ImageError::BufferTooSmall)); |
| 361 | } |
| 362 | |
| 363 | #[test] |
| 364 | fn vendor_parse_v3() { |
| 365 | let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v3>()]; |
| 366 | add::<vendor_boot_img_hdr_v3>( |
| 367 | &mut buffer, |
| 368 | vendor_boot_img_hdr_v3 { |
| 369 | magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(), |
| 370 | header_version: 3, |
| 371 | ..Default::default() |
| 372 | }, |
| 373 | ); |
| 374 | let expected = Ok(VendorImageHeader::V3( |
| 375 | LayoutVerified::<&[u8], vendor_boot_img_hdr_v3>::new(&buffer).unwrap(), |
| 376 | )); |
| 377 | assert_eq!(VendorImageHeader::parse(&buffer[..]), expected); |
| 378 | } |
| 379 | |
| 380 | #[test] |
| 381 | fn vendor_parse_v4() { |
| 382 | let mut buffer = [0; core::mem::size_of::<vendor_boot_img_hdr_v4>()]; |
| 383 | add::<vendor_boot_img_hdr_v4>( |
| 384 | &mut buffer, |
| 385 | vendor_boot_img_hdr_v4 { |
| 386 | _base: vendor_boot_img_hdr_v3 { |
| 387 | magic: VENDOR_BOOT_MAGIC[0..VENDOR_MAGIC_SIZE].try_into().unwrap(), |
| 388 | header_version: 4, |
| 389 | ..Default::default() |
| 390 | }, |
| 391 | ..Default::default() |
| 392 | }, |
| 393 | ); |
| 394 | let expected = Ok(VendorImageHeader::V4( |
| 395 | LayoutVerified::<&[u8], vendor_boot_img_hdr_v4>::new(&buffer).unwrap(), |
| 396 | )); |
| 397 | assert_eq!(VendorImageHeader::parse(&buffer[..]), expected); |
| 398 | } |
| 399 | } |