linux_loader/loader/bzimage/
mod.rs

1// Copyright (c) 2019 Intel Corporation. All rights reserved.
2// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3//
4// Copyright 2017 The Chromium OS Authors. All rights reserved.
5// Use of this source code is governed by a BSD-style license that can be
6// found in the LICENSE-BSD-3-Clause file.
7//
8// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
9
10//! Traits and structs for loading bzimage kernels into guest memory.
11
12#![cfg(all(feature = "bzimage", any(target_arch = "x86", target_arch = "x86_64")))]
13
14use std::fmt;
15use std::io::{Seek, SeekFrom};
16
17use vm_memory::{Address, ByteValued, GuestAddress, GuestMemory, GuestUsize, ReadVolatile};
18
19use crate::loader::{
20    bootparam, Error as KernelLoaderError, KernelLoader, KernelLoaderResult, Result,
21};
22
23#[derive(Debug, PartialEq, Eq)]
24/// Bzimage kernel loader errors.
25pub enum Error {
26    /// Invalid bzImage binary.
27    InvalidBzImage,
28    /// Overflow occurred during an arithmetic operation.
29    Overflow,
30    /// Unable to read bzImage header.
31    ReadBzImageHeader,
32    /// Unable to read bzImage compressed image.
33    ReadBzImageCompressedKernel,
34    /// Unable to seek to bzImage end.
35    SeekBzImageEnd,
36    /// Unable to seek to bzImage header.
37    SeekBzImageHeader,
38    /// Unable to seek to bzImage compressed kernel.
39    SeekBzImageCompressedKernel,
40    /// Underflow occurred during an arithmetic operation.
41    Underflow,
42}
43
44impl fmt::Display for Error {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        let desc = match self {
47            Error::InvalidBzImage => "Invalid bzImage",
48            Error::Overflow => "Overflow occurred during an arithmetic operation",
49            Error::ReadBzImageHeader => "Unable to read bzImage header",
50            Error::ReadBzImageCompressedKernel => "Unable to read bzImage compressed kernel",
51            Error::SeekBzImageEnd => "Unable to seek bzImage end",
52            Error::SeekBzImageHeader => "Unable to seek bzImage header",
53            Error::SeekBzImageCompressedKernel => "Unable to seek bzImage compressed kernel",
54            Error::Underflow => "Underflow occurred during an arithmetic operation",
55        };
56
57        write!(f, "Kernel Loader: {}", desc)
58    }
59}
60
61impl std::error::Error for Error {}
62
63/// Big zImage (bzImage) kernel image support.
64pub struct BzImage;
65
66impl KernelLoader for BzImage {
67    /// Loads a kernel from a bzImage to guest memory.
68    ///
69    /// The kernel is loaded at `code32_start`, the default load address stored in the bzImage
70    /// setup header.
71    ///
72    /// # Arguments
73    ///
74    /// * `guest_mem`: [`GuestMemory`] to load the kernel in.
75    /// * `kernel_offset`: Address in guest memory where the kernel is loaded.
76    /// * `kernel_image` - Input bzImage image.
77    /// * `highmem_start_address`: Address where high memory starts.
78    ///
79    /// # Examples
80    ///
81    /// ```rust
82    /// # extern crate vm_memory;
83    /// # use std::io::Cursor;
84    /// # use linux_loader::loader::*;
85    /// # use vm_memory::{Address, GuestAddress};
86    /// # type GuestMemoryMmap = vm_memory::GuestMemoryMmap<()>;
87    /// let mem_size: usize = 0x1000000;
88    /// let himem_start = GuestAddress(0x0);
89    /// let kernel_addr = GuestAddress(0x200000);
90    /// let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), mem_size)]).unwrap();
91    /// let mut kernel_image = vec![];
92    /// kernel_image.extend_from_slice(include_bytes!("bzimage"));
93    /// bzimage::BzImage::load(
94    ///     &gm,
95    ///     Some(kernel_addr),
96    ///     &mut Cursor::new(&kernel_image),
97    ///     Some(himem_start),
98    /// )
99    /// .unwrap();
100    /// ```
101    ///
102    /// [`GuestMemory`]: https://docs.rs/vm-memory/latest/vm_memory/guest_memory/trait.GuestMemory.html
103    fn load<F, M: GuestMemory>(
104        guest_mem: &M,
105        kernel_offset: Option<GuestAddress>,
106        kernel_image: &mut F,
107        highmem_start_address: Option<GuestAddress>,
108    ) -> Result<KernelLoaderResult>
109    where
110        F: ReadVolatile + Seek,
111    {
112        let mut kernel_size = kernel_image
113            .seek(SeekFrom::End(0))
114            .map_err(|_| Error::SeekBzImageEnd)? as usize;
115        kernel_image
116            .seek(SeekFrom::Start(0x1F1))
117            .map_err(|_| Error::SeekBzImageHeader)?;
118
119        let mut boot_header = bootparam::setup_header::default();
120        kernel_image
121            .read_volatile(&mut boot_header.as_bytes())
122            .map_err(|_| Error::ReadBzImageHeader)?;
123
124        // If the `HdrS` magic number is not found at offset 0x202, the boot protocol version is
125        // "old", the image type is assumed as zImage, not bzImage.
126        if boot_header.header != 0x5372_6448 {
127            return Err(Error::InvalidBzImage.into());
128        }
129
130        // Follow the section related to loading the rest of the kernel in the linux boot protocol.
131        if (boot_header.version < 0x0200) || ((boot_header.loadflags & 0x1) == 0x0) {
132            return Err(Error::InvalidBzImage.into());
133        }
134
135        let mut setup_size = boot_header.setup_sects as usize;
136        if setup_size == 0 {
137            setup_size = 4;
138        }
139        setup_size = setup_size
140            .checked_add(1)
141            .and_then(|setup_size| setup_size.checked_mul(512))
142            .ok_or(Error::Overflow)?;
143        kernel_size = kernel_size
144            .checked_sub(setup_size)
145            .ok_or(Error::Underflow)?;
146
147        // Check that `code32_start`, the default address of the kernel, is not lower than high
148        // memory.
149        if (highmem_start_address.is_some())
150            && (u64::from(boot_header.code32_start) < highmem_start_address.unwrap().raw_value())
151        {
152            return Err(KernelLoaderError::InvalidKernelStartAddress);
153        }
154
155        let mem_offset = match kernel_offset {
156            Some(start) => start,
157            None => GuestAddress(u64::from(boot_header.code32_start)),
158        };
159
160        boot_header.code32_start = mem_offset.raw_value() as u32;
161
162        let mut loader_result = KernelLoaderResult {
163            setup_header: Some(boot_header),
164            kernel_load: mem_offset,
165            ..Default::default()
166        };
167
168        // Seek the compressed `vmlinux.bin` and read it to memory.
169        kernel_image
170            .seek(SeekFrom::Start(setup_size as u64))
171            .map_err(|_| Error::SeekBzImageCompressedKernel)?;
172        guest_mem
173            .read_exact_volatile_from(mem_offset, kernel_image, kernel_size)
174            .map_err(|_| Error::ReadBzImageCompressedKernel)?;
175
176        loader_result.kernel_end = mem_offset
177            .raw_value()
178            .checked_add(kernel_size as GuestUsize)
179            .ok_or(KernelLoaderError::MemoryOverflow)?;
180
181        Ok(loader_result)
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188
189    use std::fs::File;
190    use std::io::{Cursor, Read};
191    use std::process::Command;
192    use vm_memory::{Address, GuestAddress};
193    type GuestMemoryMmap = vm_memory::GuestMemoryMmap<()>;
194
195    const MEM_SIZE: u64 = 0x100_0000;
196
197    fn create_guest_mem() -> GuestMemoryMmap {
198        GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
199    }
200
201    fn download_resources() {
202        let command = "./.buildkite/download_resources.sh";
203        let status = Command::new(command).status().unwrap();
204        if !status.success() {
205            panic!("Cannot run build script");
206        }
207    }
208
209    fn make_bzimage() -> Vec<u8> {
210        download_resources();
211        let mut v = Vec::new();
212        let path = concat!(env!("CARGO_MANIFEST_DIR"), "/src/loader/bzimage/bzimage");
213        let mut f = File::open(path).unwrap();
214        f.read_to_end(&mut v).unwrap();
215
216        v
217    }
218
219    #[allow(non_snake_case)]
220    #[test]
221    fn test_load_bzImage() {
222        let gm = create_guest_mem();
223        let image = make_bzimage();
224        let mut kernel_offset = GuestAddress(0x200000);
225        let mut highmem_start_address = GuestAddress(0x0);
226
227        // load bzImage with good kernel_offset and himem_start setting
228        let mut loader_result = BzImage::load(
229            &gm,
230            Some(kernel_offset),
231            &mut Cursor::new(&image),
232            Some(highmem_start_address),
233        )
234        .unwrap();
235        let setup_header = loader_result.setup_header.unwrap();
236
237        assert_eq!(loader_result.kernel_load.raw_value(), 0x200000);
238        assert_eq!(
239            // SAFETY:
240            // Reading the value from an unaligned address is not considered safe.
241            // but this is not an issue since this is a test.
242            unsafe { std::ptr::addr_of!(setup_header.header).read_unaligned() },
243            0x53726448
244        );
245        assert_eq!(
246            // SAFETY:
247            // Reading the value from an unaligned address is not considered safe.
248            // but this is not an issue since this is a test.
249            unsafe { std::ptr::addr_of!(setup_header.version).read_unaligned() },
250            0x20f
251        );
252        assert_eq!(loader_result.setup_header.unwrap().loadflags, 1);
253        assert_eq!(loader_result.kernel_end, 0x8b5e40);
254
255        // load bzImage without kernel_offset
256        loader_result = BzImage::load(
257            &gm,
258            None,
259            &mut Cursor::new(&image),
260            Some(highmem_start_address),
261        )
262        .unwrap();
263        let setup_header = loader_result.setup_header.unwrap();
264
265        assert_eq!(loader_result.kernel_load.raw_value(), 0x100000);
266
267        // load bzImage without himem_start
268        loader_result = BzImage::load(&gm, None, &mut Cursor::new(&image), None).unwrap();
269        // Reading the value from an unaligned address is not considered safe.
270        assert_eq!(
271            0x53726448,
272            // SAFETY:
273            // Reading the value from an unaligned address is not considered safe.
274            // but this is not an issue since this is a test.
275            unsafe { std::ptr::addr_of!(setup_header.header).read_unaligned() }
276        );
277        assert_eq!(loader_result.kernel_load.raw_value(), 0x100000);
278
279        // load bzImage with a bad himem setting
280        kernel_offset = GuestAddress(0x1000);
281        highmem_start_address = GuestAddress(0x200000);
282
283        assert_eq!(
284            Some(KernelLoaderError::InvalidKernelStartAddress),
285            BzImage::load(
286                &gm,
287                Some(kernel_offset),
288                &mut Cursor::new(&image),
289                Some(highmem_start_address),
290            )
291            .err()
292        );
293    }
294
295    #[test]
296    fn test_invalid_bzimage_underflow() {
297        use crate::loader::Error as LoaderError;
298
299        let path = concat!(
300            env!("CARGO_MANIFEST_DIR"),
301            "/src/loader/bzimage/fuzz_invalid_bzimage.bin"
302        );
303
304        let gm = create_guest_mem();
305        let mut image = File::open(path).unwrap();
306        let kernel_offset = GuestAddress(0x200000);
307        let highmem_start_address = GuestAddress(0x0);
308
309        // load bzImage with good kernel_offset and himem_start setting
310        let loader_result = BzImage::load(
311            &gm,
312            Some(kernel_offset),
313            &mut image,
314            Some(highmem_start_address),
315        );
316
317        assert_eq!(
318            loader_result.unwrap_err(),
319            LoaderError::Bzimage(Error::Underflow)
320        );
321    }
322}