Skip to content

Commit bed8a53

Browse files
committed
loader: Verify alignment of the LOAD segment
For the x86-64 kernel, the loaded address must be aligned to 2MB; otherwise, the check in __startup_64() in the Linux kernel would fail and cause the kernel to hang. Therefore, verify the alignment of the LOAD segment during the loading process. Signed-off-by: Hou Wenlong <houwenlong.hwl@antgroup.com>
1 parent 4fb56c1 commit bed8a53

File tree

5 files changed

+52
-21
lines changed

5 files changed

+52
-21
lines changed

docs/elfio_files/bad_align_writer.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ int main( void )
5656
// Create a loadable segment
5757
segment* text_seg = writer.segments.add();
5858
text_seg->set_type( PT_LOAD );
59-
text_seg->set_virtual_address( 0x400 );
60-
text_seg->set_physical_address( 0x400 );
59+
text_seg->set_virtual_address( 0x200000 );
60+
text_seg->set_physical_address( 0x200000 );
6161
text_seg->set_flags( PF_X | PF_R );
62-
text_seg->set_align( 0x100 );
62+
text_seg->set_align( 0x200000 );
6363

6464
// Add code section into program segment
6565
text_seg->add_section_index( text_sec->get_index(),
@@ -80,10 +80,10 @@ int main( void )
8080
// Create a read/write segment
8181
segment* data_seg = writer.segments.add();
8282
data_seg->set_type( PT_NOTE );
83-
data_seg->set_virtual_address( 0x8888048020 );
84-
data_seg->set_physical_address( 0x8888048020 );
83+
data_seg->set_virtual_address( 0x8888400000 );
84+
data_seg->set_physical_address( 0x888400000 );
8585
data_seg->set_flags( PF_W | PF_R );
86-
data_seg->set_align( 13 );
86+
data_seg->set_align( 0x200000 );
8787

8888
// Add code section into program segment
8989
data_seg->add_section_index( data_sec->get_index(),
@@ -103,7 +103,7 @@ int main( void )
103103
// In this example, the code starts at the first address of the
104104
// 'text_seg' segment. Therefore, the start address is set
105105
// to be equal to the segment location
106-
writer.set_entry( 0x400 );
106+
writer.set_entry( 0x200000 );
107107

108108
// Create ELF file
109109
writer.save( "test_bad_align.bin" );

docs/elfio_files/basic_elf.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ int main( void )
5656
// Create a loadable segment
5757
segment* text_seg = writer.segments.add();
5858
text_seg->set_type( PT_LOAD );
59-
text_seg->set_virtual_address( 0x400 );
60-
text_seg->set_physical_address( 0x0 );
59+
text_seg->set_virtual_address( 0x200000 );
60+
text_seg->set_physical_address( 0x200000 );
6161
text_seg->set_flags( PF_X | PF_R );
62-
text_seg->set_align( 0x1 );
62+
text_seg->set_align( 0x200000 );
6363

6464
// Add code section into program segment
6565
text_seg->add_section_index( text_sec->get_index(),
@@ -80,10 +80,10 @@ int main( void )
8080
// Create a read/write segment
8181
segment* data_seg = writer.segments.add();
8282
data_seg->set_type( PT_LOAD );
83-
data_seg->set_virtual_address( 0x0420 );
84-
data_seg->set_physical_address( 0x0420 );
83+
data_seg->set_virtual_address( 0x400000 );
84+
data_seg->set_physical_address( 0x400000 );
8585
data_seg->set_flags( PF_W | PF_R );
86-
data_seg->set_align( 0x10 );
86+
data_seg->set_align( 0x200000 );
8787

8888
// Add code section into program segment
8989
data_seg->add_section_index( data_sec->get_index(),
@@ -103,7 +103,7 @@ int main( void )
103103
// In this example, the code starts at the first address of the
104104
// 'text_seg' segment. Therefore, the start address is set
105105
// to be equal to the segment location
106-
writer.set_entry( 0x400 );
106+
writer.set_entry( 0x200000 );
107107

108108
// Create ELF file
109109
writer.save( "test_elf.bin" );

src/loader/elf/mod.rs

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ impl fmt::Display for PvhBootCapability {
135135
/// Raw ELF (a.k.a. vmlinux) kernel image support.
136136
pub struct Elf;
137137

138+
// x86-64 kernel must be aligned to 2MB.
139+
const KERNEL_SEGMENT_ALIGN: u64 = 0x200000; // 2 MiB
140+
138141
impl Elf {
139142
/// Verifies that magic numbers are present in the Elf header.
140143
fn validate_header(ehdr: &elf::Elf64_Ehdr) -> std::result::Result<(), Error> {
@@ -224,12 +227,18 @@ impl KernelLoader for Elf {
224227

225228
let mut loader_result = KernelLoaderResult {
226229
kernel_load: match kernel_offset {
227-
Some(k_offset) => GuestAddress(
228-
k_offset
230+
Some(k_offset) => {
231+
let load_addr = k_offset
229232
.raw_value()
230233
.checked_add(ehdr.e_entry)
231-
.ok_or(Error::Overflow)?,
232-
),
234+
.ok_or(Error::Overflow)?;
235+
// Ensure that kernel_offset is aligned to 2M, otherwise, the kernel may hang during
236+
// booting. Refer to the check in __startup_64() in the Linux kernel source code.
237+
if k_offset.raw_value() & (KERNEL_SEGMENT_ALIGN - 1) != 0 {
238+
return Err(Error::Align.into());
239+
}
240+
GuestAddress(load_addr)
241+
},
233242
None => GuestAddress(ehdr.e_entry),
234243
},
235244
..Default::default()
@@ -271,6 +280,12 @@ impl KernelLoader for Elf {
271280
.seek(SeekFrom::Start(phdr.p_offset))
272281
.map_err(|_| Error::SeekKernelStart)?;
273282

283+
// Verify alignment of the LOAD segment.
284+
// See commit c55b8550fa57ba4f5e507be406ff9fc2845713e8 in Linux kernel tree.
285+
if phdr.p_align & (KERNEL_SEGMENT_ALIGN - 1) != 0 {
286+
return Err(Error::Align.into());
287+
}
288+
274289
// if the vmm does not specify where the kernel should be loaded, just
275290
// load it to the physical address p_paddr for each segment.
276291
let mem_offset = match kernel_offset {
@@ -479,10 +494,10 @@ mod tests {
479494
Some(highmem_start_address),
480495
)
481496
.unwrap();
482-
assert_eq!(loader_result.kernel_load.raw_value(), 0x200400);
497+
assert_eq!(loader_result.kernel_load.raw_value(), 0x400000);
483498

484499
loader_result = Elf::load(&gm, Some(kernel_addr), &mut Cursor::new(&image), None).unwrap();
485-
assert_eq!(loader_result.kernel_load.raw_value(), 0x200400);
500+
assert_eq!(loader_result.kernel_load.raw_value(), 0x400000);
486501

487502
loader_result = Elf::load(
488503
&gm,
@@ -491,7 +506,7 @@ mod tests {
491506
Some(highmem_start_address),
492507
)
493508
.unwrap();
494-
assert_eq!(loader_result.kernel_load.raw_value(), 0x400);
509+
assert_eq!(loader_result.kernel_load.raw_value(), 0x200000);
495510

496511
highmem_start_address = GuestAddress(0xa00000);
497512
assert_eq!(
@@ -632,4 +647,20 @@ mod tests {
632647
.err()
633648
);
634649
}
650+
651+
#[test]
652+
fn test_unaligned_loadaddr() {
653+
let gm = create_guest_mem();
654+
let image = make_elf_bin();
655+
assert_eq!(
656+
Some(KernelLoaderError::Elf(Error::Align)),
657+
Elf::load(
658+
&gm,
659+
Some(GuestAddress(0x1000)),
660+
&mut Cursor::new(&image),
661+
None
662+
)
663+
.err()
664+
);
665+
}
635666
}

src/loader/elf/test_bad_align.bin

4 MB
Binary file not shown.

src/loader/elf/test_elf.bin

4 MB
Binary file not shown.

0 commit comments

Comments
 (0)