Skip to content

Commit 7d9cd71

Browse files
test: Create minimal PE binary for testing
Buffered UKI readers fail if the binary being read is not a valid PE binary. To fix tests, create a minimal PE binary Assisted-By: Claude Code (Opus) Signed-off-by: Pragyan Poudyal <pragyanpoudyal41999@gmail.com>
1 parent 10a4ae4 commit 7d9cd71

4 files changed

Lines changed: 63 additions & 6 deletions

File tree

crates/lib/src/kernel.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ fn find_uki_path(root: &Dir) -> Result<Option<Utf8PathBuf>> {
168168
#[cfg(test)]
169169
mod tests {
170170
use super::*;
171+
use bootc_utils::create_minimal_pe;
171172
use cap_std_ext::{cap_std, cap_tempfile, dirext::CapStdExtDirExt};
172173

173174
#[test]
@@ -209,7 +210,7 @@ mod tests {
209210
fn test_find_kernel_uki() -> Result<()> {
210211
let tempdir = cap_tempfile::tempdir(cap_std::ambient_authority())?;
211212
tempdir.create_dir_all("boot/EFI/Linux")?;
212-
tempdir.atomic_write("boot/EFI/Linux/fedora-6.12.0.efi", b"fake uki")?;
213+
tempdir.atomic_write("boot/EFI/Linux/fedora-6.12.0.efi", &create_minimal_pe())?;
213214

214215
let kernel_internal = find_kernel(&tempdir)?.expect("should find kernel");
215216
assert_eq!(kernel_internal.kernel.version, "fedora-6.12.0");
@@ -233,7 +234,7 @@ mod tests {
233234
b"fake kernel",
234235
)?;
235236
tempdir.create_dir_all("boot/EFI/Linux")?;
236-
tempdir.atomic_write("boot/EFI/Linux/fedora-6.12.0.efi", b"fake uki")?;
237+
tempdir.atomic_write("boot/EFI/Linux/fedora-6.12.0.efi", &create_minimal_pe())?;
237238

238239
let kernel_internal = find_kernel(&tempdir)?.expect("should find kernel");
239240
// UKI should take precedence
@@ -246,9 +247,9 @@ mod tests {
246247
fn test_find_uki_path_sorted() -> Result<()> {
247248
let tempdir = cap_tempfile::tempdir(cap_std::ambient_authority())?;
248249
tempdir.create_dir_all("boot/EFI/Linux")?;
249-
tempdir.atomic_write("boot/EFI/Linux/zzz.efi", b"fake uki")?;
250-
tempdir.atomic_write("boot/EFI/Linux/aaa.efi", b"fake uki")?;
251-
tempdir.atomic_write("boot/EFI/Linux/mmm.efi", b"fake uki")?;
250+
tempdir.atomic_write("boot/EFI/Linux/zzz.efi", &create_minimal_pe())?;
251+
tempdir.atomic_write("boot/EFI/Linux/aaa.efi", &create_minimal_pe())?;
252+
tempdir.atomic_write("boot/EFI/Linux/mmm.efi", &create_minimal_pe())?;
252253

253254
// Should return first in sorted order
254255
let path = find_uki_path(&tempdir)?.expect("should find uki");

crates/lib/src/ukify.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ pub(crate) async fn build_ukify(
124124

125125
#[cfg(test)]
126126
mod tests {
127+
use bootc_utils::create_minimal_pe;
128+
127129
use super::*;
128130
use std::fs;
129131

@@ -148,7 +150,11 @@ mod tests {
148150

149151
// Create a UKI structure
150152
fs::create_dir_all(tempdir.path().join("boot/EFI/Linux")).unwrap();
151-
fs::write(tempdir.path().join("boot/EFI/Linux/test.efi"), b"fake uki").unwrap();
153+
fs::write(
154+
tempdir.path().join("boot/EFI/Linux/test.efi"),
155+
&create_minimal_pe(),
156+
)
157+
.unwrap();
152158

153159
let result = build_ukify(path, &[], &[], false, None).await;
154160
assert!(result.is_err());

crates/utils/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ mod timestamp;
1818
pub use timestamp::*;
1919
mod tracing_util;
2020
pub use tracing_util::*;
21+
mod uki;
22+
pub use uki::*;
2123

2224
/// The name of our binary
2325
pub const NAME: &str = "bootc";

crates/utils/src/uki.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/// Create a minimal valid PE file for testing UKI parsing
2+
pub fn create_minimal_pe() -> Vec<u8> {
3+
let mut pe = Vec::new();
4+
let section_data_offset = 0x200u32; // Standard section alignment
5+
let section_data = b"quiet splash"; // Sample cmdline content
6+
7+
// DOS header (64 bytes)
8+
pe.extend_from_slice(b"MZ"); // e_magic
9+
pe.extend_from_slice(&[0u8; 58]); // DOS header padding
10+
pe.extend_from_slice(&0x80u32.to_le_bytes()); // e_lfanew (offset to PE header)
11+
12+
// DOS stub padding to reach offset 0x80
13+
pe.resize(0x80, 0);
14+
15+
// PE header (4 bytes)
16+
pe.extend_from_slice(b"PE\0\0");
17+
18+
// COFF header (20 bytes)
19+
pe.extend_from_slice(&0x8664u16.to_le_bytes()); // machine (x64)
20+
pe.extend_from_slice(&1u16.to_le_bytes()); // number of sections
21+
pe.extend_from_slice(&0u32.to_le_bytes()); // timestamp
22+
pe.extend_from_slice(&0u32.to_le_bytes()); // pointer to symbol table
23+
pe.extend_from_slice(&0u32.to_le_bytes()); // number of symbols
24+
pe.extend_from_slice(&0xF0u16.to_le_bytes()); // size of optional header
25+
pe.extend_from_slice(&0x2022u16.to_le_bytes()); // characteristics
26+
27+
// Optional header (240 bytes for PE32+)
28+
pe.extend_from_slice(&0x020Bu16.to_le_bytes()); // magic (PE32+)
29+
pe.extend_from_slice(&[0u8; 0xF0 - 2]); // rest of optional header filled with zeros
30+
31+
// Section header (40 bytes)
32+
let mut section_header = [0u8; 40];
33+
section_header[..8].copy_from_slice(b".cmdline"); // name
34+
section_header[8..12].copy_from_slice(&(section_data.len() as u32).to_le_bytes()); // virtual_size
35+
section_header[12..16].copy_from_slice(&0x1000u32.to_le_bytes()); // virtual_address
36+
section_header[16..20].copy_from_slice(&(section_data.len() as u32).to_le_bytes()); // size_of_raw_data
37+
section_header[20..24].copy_from_slice(&section_data_offset.to_le_bytes()); // pointer_to_raw_data
38+
section_header[36..40].copy_from_slice(&0x40000040u32.to_le_bytes()); // characteristics (readable)
39+
pe.extend_from_slice(&section_header);
40+
41+
// Pad to section data offset
42+
pe.resize(section_data_offset as usize, 0);
43+
44+
// Section data
45+
pe.extend_from_slice(section_data);
46+
47+
pe
48+
}

0 commit comments

Comments
 (0)