linux_loader/loader/bzimage/
mod.rs1#![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)]
26pub enum Error {
28 InvalidBzImage,
30 Overflow,
32 ReadBzImageHeader,
34 ReadBzImageCompressedKernel,
36 SeekBzImageEnd,
38 SeekBzImageHeader,
40 SeekBzImageCompressedKernel,
42 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
65pub struct BzImage;
67
68impl KernelLoader for BzImage {
69 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 boot_header.header != 0x5372_6448 {
129 return Err(Error::InvalidBzImage.into());
130 }
131
132 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 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 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 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 unsafe { std::ptr::addr_of!(setup_header.header).read_unaligned() },
245 0x53726448
246 );
247 assert_eq!(
248 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 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 loader_result = BzImage::load(&gm, None, &mut Cursor::new(&image), None).unwrap();
271 assert_eq!(
273 0x53726448,
274 unsafe { std::ptr::addr_of!(setup_header.header).read_unaligned() }
278 );
279 assert_eq!(loader_result.kernel_load.raw_value(), 0x100000);
280
281 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 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}