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_bindgen::{
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 {
fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result62     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 
parse_header<B: ByteSlice + PartialEq, T>(buffer: B) -> BootResult<LayoutVerified<B, T>>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     /// ```
parse(buffer: B) -> BootResult<Self>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     /// ```
parse(buffer: B) -> BootResult<Self>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 
add<T: AsBytes>(buffer: &mut [u8], t: T)173     pub fn add<T: AsBytes>(buffer: &mut [u8], t: T) {
174         t.write_to_prefix(buffer).unwrap();
175     }
176 
177     #[test]
buffer_too_small_for_version()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]
buffer_too_small_valid_version()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]
bad_magic()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]
bad_version()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]
parse_v0()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]
parse_v1()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]
parse_v2()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]
parse_v3()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]
parse_v4()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]
vendor_buffer_too_small_for_version()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]
vendor_bad_magic()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]
vendor_bad_version()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]
vendor_buffer_too_small_valid_version()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]
vendor_parse_v3()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]
vendor_parse_v4()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 }
400