Skip to main content

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